Localized Clock with ID-based Translation
The example shows best practices for using Qt's ID-based translation features in CMake and Qt Quick including the handling of plurals in different languages, and localized time formats.
User Interface
The example shows the current time and date in your system's locale and language. Translation for these languages is supported: English, German, French, Spanish, Italian, Japanese, Korean, Portuguese, Arabic, and Chinese. If your desktop is in another language, it falls back to English.
The example also accepts a locale as a command-line argument to test different languages and locales without altering your system.: localizedClockIdBased --locale en_GB or localizedClockIdBased --locale de
By default, the application shows the time and date in the current locale, but it also provides a button that opens a dialog with a list of time zones. The user can use the dialog to change the time zone of the clock.
ID-based Translation
In this example, we use ID-based translation, where translatable texts are identified by a unique ID rather than the traditional context + text combination (see Text ID-based translations). This approach allows translations to be reused across different contexts. We demonstrate this by sharing a translation between Main.qml and the Time Zone Dialog, a QWidget-based form written in C++. Another aspect of ID-based translation is that it separates the text shown in the UI from the developers, making the source code independent of the actual words presented to the user.
In the following two screenshots from the application in English, the two instances of the text "Select time zone: " in the main window (QML) and the dialog (C++) share the same translation by using the same ID.
Screenshot from the application window in English version:

The dialog with a list of time zones (English version):

The main window of the application in German:

The dialog with a list of time zones (German version):

Implementation
The application has five parts:
CMakeLists.txt
The CMake file of the application enables Qt's ID-based 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, essential for internationalization. qt_standard_project_setup(): Sets up the internationalization system with support for the listed locales. Although the source language is English, an English translation is still required when using ID-based translation. The source code only contains the IDs and does not see the source texts. Hence, we need to set up the project with the English translation, so that qt_add_translations creates a TS file for English; otherwise, they will be missing at runtime.
qt_standard_project_setup(REQUIRES 6.8
I18N_TRANSLATED_LANGUAGES de ar ko zh ja fr it es pt en)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.
- Generates one TS file per language listed in
I18N_TRANSLATED_LANGUAGESofqt_standard_project_setup. MERGE_QT_TRANSLATIONSandQT_TRANSLATION_CATALOGS qtbaseinclude the Qt translations in the project. This is necessary to translate the buttons of theQDialogwidget in the Time Zone Dialog. As the text on those buttons is controlled by QDialog, without including Qt translations those buttons do not get translated (see the translated texts of the dialog in the German screenshots in ID-based Translation).
qt_add_translations(localizedClockIdBased
TS_FILE_BASE i18n/clock
MERGE_QT_TRANSLATIONS
QT_TRANSLATION_CATALOGS qtbase
RESOURCE_PREFIX i18n
)qt_add_qml_module(...): Adds a QML module under the URI qtexamples.localizedclock, includes the Main.qml file, and imports the source and header files of Time Zone Manager into the QML module.
qt_add_qml_module(localizedClockIdBased
URI qtexamples.localizedclock
VERSION 1.0
QML_FILES
Main.qml
RESOURCES dialog.ui
SOURCES
timezonemanager.h timezonemanager.cpp
dialog.h dialog.cpp
)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 in which 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:
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())); } }
As we installed English translation in the previous step, we might end up with two installed translations. 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.
Time Zone Dialog
This class is a C++ QWidget-based dialog (QDialog) that shows a QComboBox containing a list of time zones. Below is an explanation of the code:
Enable ID-based translation in the UI form (dialog.ui):
<ui version="4.0" idbasedtr="true">
Set the title with ID-based translation (dialog.ui). Here, "title" is the unique ID of the translation:
<property name="windowTitle"> <string id="title">Time Zone</string> </property>
Add a label with ID-based translation (dialog.ui), with the ID "timezonelabel":
<widget class="QLabel" name="label"> ... <property name="text"> <string id="timezonelabel">Select time zone</string> </property> ... </widget>
Time Zone Manager
A QML_SINGLETON class in C++ responsible for handling time zone changes.
Upon selecting a time zone by the user, the instance of TimeZoneManager remembers the chosen time zone:
connect(m_dialog.get(), &Dialog::timeZoneSelected, this, &TimeZoneManager::setTimeZone);
Updating the time zone emits a TimeZoneManager::timeZoneChanged signal:
connect(m_dialog.get(), &Dialog::timeZoneSelected, this, &TimeZoneManager::setTimeZone); } m_dialog->show(); }
TimeZoneManager::currentTimeZoneOffsetMs() is marked with Q_INVOKABLE and returns the time offset of the selected time zone. Since the TimeZoneManager class is declared with QML_ELEMENT and QML_SINGLETON, the method can be directly accessed from QML to update the presented time; also, see Main.qml.
qint64 TimeZoneManager::currentTimeZoneOffsetMs() { const QTimeZone tz(m_timeZone.toLatin1()); if (!tz.isValid()) return 0; const QDateTime nowUtc = QDateTime::currentDateTimeUtc(); const int targetOffset = tz.offsetFromUtc(nowUtc); const int systemOffset = QTimeZone::systemTimeZone().offsetFromUtc(nowUtc); return (targetOffset - systemOffset) * 1000; }
Main.qml
The main QML file defines the application's user interface. The UI presents time, date, current time zone, and a counter for seconds. It also provides a button that opens a Time Zone Dialog to change the time zone. Below is an explanation of the relevant pieces of code:
Define the window and set its title using qsTrId() for ID-based translation. The text for the source language is specified using the meta string notation //% (see Text ID-based translations). lupdate parses the meta strings and writes the defined source texts to the TS file. As the source text is specified using meta strings and in the form of a comment, they are not visible to the application at runtime. Hence, when the application is loaded in the English locale, even though the source language is in English, it still needs to install the English translation to present the English texts. Otherwise, the raw ID "Main-Digital-Clock" is shown. This is also why we specified "en" in I18N_TRANSLATED_LANGUAGES by the setup in CMakeLists.txt.
//% "Digital Clock"
title: qsTrId("Main-Digital-Clock")Show the number of seconds using qsTrId() with plural support. Again here the text in the source language is specified using the //% meta string. The plural form is enabled by using the special notation "%n" in the source text in the meta string (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.
//% "%n second(s)"
text: qsTrId("Main-n-second-s", root.seconds)Display the currently selected time zone using ID-based translation, with //% "Time zone: " specifying the text in the source language:
//% "Time zone: "
text: qsTrId("timezone") + TimeZoneManager.timeZone;A Button to open the Time Zone Dialog. The text on the button is specified using qsTrId() for ID-based translation. In this case, the source text is not defined by meta strings anymore since this is a reuse of the ID "timezonelabel", which was previously used in the dialog.ui document (see Time Zone Dialog). In ID-based translation, it suffices to specify the source text per ID only once in the project:
Button { text: qsTrId("timezonelabel") onClicked: TimeZoneManager.openDialog() Layout.alignment: Qt.AlignHCenter }
Upon changing the time zone and receiving the TimeZoneManager::timeZoneChanged() signal (see Time Zone Manager), update the diff variable with the time offset of the selected time zone:
Connections { target: TimeZoneManager function onTimeZoneChanged() { root.diff = TimeZoneManager.currentTimeZoneOffsetMs(); } }
Declare a Timer that triggers every second and updates the time, date, and seconds properties. The timer calculates the time by adding the current time to the time offset of the selected time zone:
Timer { interval: 1000 running: true repeat: true triggeredOnStart: true onTriggered: { const now = new Date(new Date().getTime() + root.diff); const locale = Qt.locale(); root.time = now.toLocaleTimeString(locale, Locale.ShortFormat); root.date = now.toLocaleDateString(locale); root.seconds = now.getSeconds(); } }
The locale affects how dates and times are displayed. These are formatted according to the current locale's country conventions. For example, a German locale results in 24-hour time and DD.MM.YYYY date format, while a US locale uses a 12-hour clock and MM/DD/YYYY date format.
© 2025 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.