Localized Clock Example | Qt Linguist Manual
Description: The example shows best practices for using Qt's translation and localization features in CMake and Qt Quick including the handling of plurals in different languages, and localized time and date formats.

- Qt 6.11
- Qt Linguist Manual
- Qt Linguist Examples
- Localized Clock Example
- Qt 6.11.1 Reference Documentation

# Localized Clock Example

The example shows best practices for using Qt's translation and localization features in CMake and Qt Quick including the handling of plurals in different languages, and localized time and date formats.

See the Qt Linguist Manual for more information about translating Qt applications.

#### User Interface

The example shows the current time and date in the locale and language of your system. The texts in the UI are furthermore localized for the following languages: English, German, French, Spanish, Italian, Japanese, Korean, Portuguese, Arabic, and Chinese. If your desktop is in another language, it falls back to English.

To test different languages and locales without altering your system, the example also accepts a locale as command line argument. For example, starting localizedClock from the command line with the option `--locale de` shows the clock in German, with a date and time format as it is common in Germany.

The screenshot shows the en_US version:

##### Internationalization

In the application translation is used to set the main window title and a few UI texts, including ones with placeholders and plural forms. The application counts the seconds with plural forms enabled (see Handle Plural Forms). As a result, depending on the count of seconds, the translation function returns a different translation, with the correct grammatical number for the target language. For instance, in English, if the count is larger than one, the plural form is used, otherwise the singular form is used. In Translation Rules for Plural Forms you find the plural rules for different languages.

The locale also affects how dates and times are displayed. These are formatted according to the country conventions of the current locale. For example, a German locale results in 24-hour time and the day written before the month, while a US locale uses a 12-hour clock, and the month is written before the day.

This screenshot shows the en_GB version. Notice that the data format is different than the en_US version above, while the same English plural translation is loaded in both cases.

Here is the screenshot of the de_DE version, which similar to GB has a different date format than US. Notice that the German translation for regular and plural forms is loaded accordingly.

#### Implementation

The implementation has three parts:

- CMakeLists.txt
- main.cpp
- Main.qml

##### CMakeLists.txt

The CMake file of the application enables Qt's translation and localization support. Here are the relevant pieces:

`find_package(Qt6 REQUIRED COMPONENTS Core Linguist Qml Quick)`: Finds and links the required Qt 6 modules, including `Linguist` which are essential for internationalization.

`qt_standard_project_setup(...)`: Sets up the internationalization system with support for the listed locales. `I18N_SOURCE_LANGUAGE` is left at its default value (English), since the source code contains English texts.

     qt_standard_project_setup(REQUIRES 6.8
         I18N_TRANSLATED_LANGUAGES de ar ko zh ja fr it es pt)

`qt_add_translations(...)`: Bundles the functionality of `lupdate` and `lrelease` by generating the translation source files (TS files) in the "i18n" directory using `clock` as the base name, and compiling them into binary `.qm` files if they contain translations. The following TS files are generated:

- "clock_{de, ar, ko, zh, ja, fr, it, es, pt}.ts": One TS file per language listed in `I18N_TRANSLATED_LANGUAGES` of `qt_standard_project_setup` containing the translations to that language.
- "clock_en.ts": Contains English plural forms, since the source code has plural form translation ("%n second(s)"). The function `qt_add_translations` writes only the plural forms here, as we specified the language of the texts in the source code as English, by leaving `I18N_SOURCE_LANGUAGE` with the default value. So the reglular texts do not need translations.

     qt_add_translations(localizedClock
         TS_FILE_BASE i18n/clock
         RESOURCE_PREFIX i18n
     )

`qt_add_qml_module(...)`: Adds a QML module under the URI `qtexamples.localizedclock`, including the Main.qml file.

     qt_add_qml_module(localizedClock
         URI qtexamples.localizedclock
         VERSION 1.0
         QML_FILES
             Main.qml
     )

##### main.cpp

The starting point of the application. This part is responsible for setting the locale, installing required translations, and loading UI. Below is an explanation of the relevant pieces of code:

Define the locale argument, e.g., `--locale en_US` or `--locale de_DE`:

         QCommandLineParser parser;
         QCommandLineOption localeOption("locale"_L1, "Locale to be used in the user interface"_L1,
                                         "locale"_L1);
         parser.addOption(localeOption);
         parser.addHelpOption();
         parser.process(app);

Parse the arguments, fetch the provided locale, and set the input locale as the default locale of the application:

             QLocale locale(parser.value(localeOption));
             qInfo() << "Setting locale to" << locale.name();
             QLocale::setDefault(locale);

Install the English translation regardless of the locale, to allow incomplete translations for other languages. QTranslator queries translations for texts in the reversed order the translations are installed:

         QTranslator enPlurals;
         const auto enPluralsPath = ":/i18n/clock_en.qm"_L1;
         if (!enPlurals.load(enPluralsPath))
             qFatal("Could not load %s!", qUtf8Printable(enPluralsPath));
         app.installTranslator(&enPlurals);

Install a translation according to the given locale. As English translation is already installed in the previous step, we might end up with two installed translations here. Qt uses the most recently installed translation for any overlapping keys. Therefore, the locale-specific translation will take precedence over English, and in case of any missing translations, QTranslator falls back to English.

         QTranslator translation;
         if (QLocale().language() != QLocale::English) {
             if (translation.load(QLocale(), "clock"_L1, "_"_L1, ":/i18n/"_L1)) {
                 qInfo("Loading translation %s",
                       qUtf8Printable(QDir::toNativeSeparators(translation.filePath())));
                 if (!app.installTranslator(&translation))
                     qWarning("Could not install %s!",
                              qUtf8Printable(QDir::toNativeSeparators(translation.filePath())));
             } else {
                 qInfo("Could not load translation to %s. Using English.",
                       qUtf8Printable(QLocale().name()));
             }
         }

##### Main.qml

This QML file defines the main UI window of the application, which presents time, date, the used locale, and a counter for seconds. Below is an explanation of the relevant pieces of code:

Set the title of the Window using qsTr() for translation. To find the translation for this text QTranslator queries in the TS file of the current language the text "Digital Clock" within the context "Main" (the file name):

     title: qsTr("Digital Clock")

Show the number of seconds using qsTr() with plural (numeral) support. The plural form is enabled by using the special notation "%n" (see Handle Plural Forms). Depending on the value of n, the translation function returns a different translation, with the correct grammatical number for the target language. For instance in English, if the value of `root.seconds` is larger than one, the plural form is used, otherwise the singular form is used. In Translation Rules for Plural Forms you find the plural rules for different languages.

     text: qsTr("%n second(s)", "seconds", root.seconds)

Display the current locale and use qsTr() to translate the source text "Locale: %1". Also the translation needs to contain the argument notation "%1". As a result, the argument of the text (i.e. `Qt.locale().name`) can correctly be used to format the text:

     text: qsTr("Locale: %1").arg(Qt.locale().name)

Format the time and date according to the locale convensions. Different countires might have specific preferences on how time and date should be presented. For instance, the German locale uses a 24-hour clock and writes the day before the month, whereas the US locale follows a 12-hour clock and places the month before the day. The method Date.toLocaleTimeString takes these considerations into account and formats the time and date correctly based on the given locale:

                 const now = new Date();
                 const locale = Qt.locale();
                 root.time = now.toLocaleTimeString(locale, Locale.ShortFormat);
                 root.date = now.toLocaleDateString(locale);

Example project @ code.qt.io