summaryrefslogtreecommitdiffstats
path: root/examples/linguist/doc/src/localizedclock-idbased.qdoc
blob: dbf1aee5981d4647d385c18e93bf4b988701fc14 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
// Copyright (C) 2025 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only

/*!
    \example localizedclock-idbased
    \ingroup examples-linguist
    \examplecategory {User Interface Components}
    \meta tags {qtquick,i18n,l10n,linguist,plurals,id-based}

    \title Localized Clock with ID-based Translation

    \brief 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.

    \section1 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.:
    \c {localizedClockIdBased --locale en_GB} or
    \c {localizedClockIdBased --locale de}

    By default, the application shows the time and date in the \e{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.

    \section2 ID-based Translation

    In this example, we use ID-based translation, where translatable
    texts are identified by a \e{unique ID} rather than the
    traditional \e{context + text} combination
    (see \l{Text ID-based translations}). This approach allows
    translations to be reused across different contexts.
    We demonstrate this by sharing a translation between
    \l Main.qml and the \l{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:
    \image linguist-localizedclock-idbased_en.webp
    The dialog with a list of time zones (English version):
    \image linguist-localizedclock-idbased-dialog_en.webp

    The main window of the application in German:
    \image linguist-localizedclock-idbased_de.webp
    The dialog with a list of time zones (German version):
    \image linguist-localizedclock-idbased-dialog_de.webp

    \section1 Implementation

    The application has five parts:
    \list
        \li \l CMakeLists.txt
        \li \l main.cpp
        \li \l {Time Zone Dialog}
        \li \l {Time Zone Manager}
        \li \l Main.qml
    \endlist

    \section2 CMakeLists.txt
    The CMake file of the application enables Qt's ID-based translation
    and localization support. Here are the relevant pieces:

    \b{\c find_package(Qt6 REQUIRED COMPONENTS Core Linguist Qml Quick):}
            Finds and links the required Qt 6 modules, including \c Linguist,
            essential for internationalization.
    \b{\c 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.
            \quotefromfile localizedclock-idbased/CMakeLists.txt
            \skipto qt_standard_project_setup
            \printuntil I18N_TRANSLATED_LANGUAGES

    \b{\c qt_add_translations(...):}
            Bundles the functionality of \c lupdate and \c lrelease by generating the
            translation source files (TS files) in the "i18n" directory using
            \c clock as the base name, and compiling them into binary \c .qm files if
            they contain translations.
            \list
            \li Generates one TS file per language
                listed in \c I18N_TRANSLATED_LANGUAGES of \c qt_standard_project_setup.
            \li \c MERGE_QT_TRANSLATIONS and \c {QT_TRANSLATION_CATALOGS qtbase} include
                the Qt translations in the project. This is necessary to translate
                the buttons of the \c QDialog widget in the \l{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 \l {ID-based Translation}).
            \endlist
            \quotefromfile localizedclock-idbased/CMakeLists.txt
            \skipto qt_add_translations
            \printuntil )

    \b{\c qt_add_qml_module(...):}
            Adds a QML module under the URI \c qtexamples.localizedclock,
            includes the \l Main.qml file, and imports the source and header
            files of \l{Time Zone Manager} into the QML module.
            \quotefromfile localizedclock-idbased/CMakeLists.txt
            \skipto qt_add_qml_module
            \printuntil )

    \section2 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., \c {--locale en_US} or \c{--locale de_DE}:

    \quotefromfile localizedclock-idbased/main.cpp
    \skipto QCommandLineParser parser
    \printuntil parser.process

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

    \quotefromfile localizedclock-idbased/main.cpp
    \skipto QLocale locale
    \printuntil QLocale::setDefault

    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:

    \quotefromfile localizedclock-idbased/main.cpp
    \skipto QTranslator enPlurals
    \printuntil app.installTranslator

    Install a translation according to the given locale:

    \quotefromfile localizedclock-idbased/main.cpp
    \skipto QTranslator translation
    \printuntil }
    \printuntil }
    \printuntil }

    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.

    \section2 Time Zone Dialog
    This class is a C++ \l {QWidget}-based dialog (\l QDialog) that shows a
    \l QComboBox containing a list of time zones.
    Below is an explanation of the code:

    Enable ID-based translation in the UI form (dialog.ui):
    \code
    <ui version="4.0" idbasedtr="true">
    \endcode

    Set the title with ID-based translation (dialog.ui). Here,
    "title" is the unique ID of the translation:
    \code
    <property name="windowTitle">
        <string id="title">Time Zone</string>
    </property>
    \endcode

    Add a label with ID-based translation (dialog.ui), with the
    ID "timezonelabel":
    \code
    <widget class="QLabel" name="label">
    ...
    <property name="text">
    <string id="timezonelabel">Select time zone</string>
    </property>
    ...
    </widget>
    \endcode

    \section2 Time Zone Manager
    A \c QML_SINGLETON class in \c C++ responsible for handling
    time zone changes.

    Upon selecting a time zone by the user, the instance of \c TimeZoneManager
    remembers the chosen time zone:
    \quotefromfile localizedclock-idbased/timezonemanager.cpp
    \skipto connect
    \printuntil connect

    Updating the time zone emits a TimeZoneManager::timeZoneChanged signal:
    \quotefromfile localizedclock-idbased/timezonemanager.cpp
    \skipto TimeZoneManager::setTimeZone
    \printuntil }
    \printuntil }

    \c TimeZoneManager::currentTimeZoneOffsetMs() is marked with
    \l Q_INVOKABLE and returns the time offset of the selected time zone.
    Since the \c TimeZoneManager class is declared with \l QML_ELEMENT
    and \l QML_SINGLETON, the method can be directly accessed from QML
    to update the presented time; also, see \l{Main.qml}.

    \quotefromfile localizedclock-idbased/timezonemanager.cpp
    \skipto currentTimeZoneOffsetMs
    \printuntil }

    \section2 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 \l {Time Zone Dialog} to change the
    time zone.
    Below is an explanation of the relevant pieces of code:

    \b{Define the window and set its title using \l{Qt::}{qsTrId()}
    for ID-based translation.} The text
    for the source language is specified using the meta string
    notation \c {//%} (see \l {Text ID-based translations}).
    \l 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 \c I18N_TRANSLATED_LANGUAGES
    by the setup in \l{CMakeLists.txt}.

    \qml
    //% "Digital Clock"
    title: qsTrId("Main-Digital-Clock")
    \endqml

    \b{Show the number of seconds using \l{Qt::}{qsTrId()} with plural
    support.} Again here the text in the source language is specified using
    the \c{//%} meta string.
    The plural form is enabled by using the special notation "%n"
    in the source text in the meta string (see \l{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 \c root.seconds is larger
    than one, the plural form is used, otherwise the singular form is used.
    In \l{Translation Rules for Plural Forms} you find the plural rules
    for different languages.

    \qml
    //% "%n second(s)"
    text: qsTrId("Main-n-second-s", root.seconds)
    \endqml

    \b{Display the currently selected time zone using ID-based
    translation,} with \c { //% "Time zone: "} specifying the
    text in the source language:
    \qml
    //% "Time zone: "
    text: qsTrId("timezone") + TimeZoneManager.timeZone;
    \endqml

    \b{A Button to open the \l{Time Zone Dialog}.} The text on the
    button is specified using \l{Qt::}{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 \l{Time Zone Dialog}). In ID-based translation, it
    suffices to specify the source text per ID only once in the project:
    \quotefromfile localizedclock-idbased/Main.qml
    \skipto Button
    \printuntil }

    Upon changing the time zone and receiving the
    \c TimeZoneManager::timeZoneChanged() signal
    (see \l{Time Zone Manager}), update the \c diff variable with
    the time offset of the selected time zone:
    \quotefromfile localizedclock-idbased/Main.qml
    \skipto Connections
    \printuntil }
    \printuntil }

    \b{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:
    \quotefromfile localizedclock-idbased/Main.qml
    \skipto Timer
    \printuntil }
    \printuntil }

    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
    \e DD.MM.YYYY date format, while a US locale uses a 12-hour clock and
    \e MM/DD/YYYY date format.
*/