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
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
|
\chapter Programmers
Support for multiple languages is extremely simple in Qt
applications, and adds little overhead to the programmer's workload.
Qt minimizes the performance cost of using translations by
translating the phrases for each window as they are created. In most
applications the main window is created just once. Dialogs are often
created once and then shown and hidden as required. Once the initial
translation has taken place there is no further runtime overhead for
the translated windows. Only those windows that are created,
destroyed and subsequently created will have a translation
performance cost.
Creating applications that can switch language at runtime is possible
with Qt, but requires a certain amount of programmer intervention and
will of course incur some runtime performance cost.
\section1 Making the Application Translation Aware
Programmers should make their application look for and load the
appropriate translation file and mark user-visible text and Ctrl
keyboard accelerators as targets for translation.
Each piece of text that requires translating requires context to help
the translator identify where in the program the text occurs. In the
case of multiple identical texts that require different translations,
the translator also requires some information to disambiguate the
source texts. Marking text for translation will automatically cause
the class name to be used as basic context information. In some cases
the programmer may be required to add additional information to help
the translator.
\section2 Creating Translation Files
\index .ts Files
\index Translation Source Files
Translation files consist of all the user-visible text and Ctrl key
accelerators in an application and translations of that text.
Translation files are created as follows:
\index lupdate
\index lrelease
\list 1
\i Run \l lupdate initially to generate the first set of \c .ts
translation source files with all the user-visible text but no
translations.
\i The \c .ts files are given to the translator who adds translations
using \e {Qt Linguist}. \e {Qt Linguist} takes care of any changed
or deleted source text.
\i Run \l lupdate to incorporate any new text added to the
application. \l lupdate synchronizes the user-visible text from the
application with the translations; it does not destroy any data.
\i Steps 2 and 3 are repeated as often as necessary.
\i When a release of the application is needed \l lrelease is run to
read the \c .ts files and produce the \c .qm files used by the
application at runtime.
\endlist
\index .pro Files
\index Project Files
\index qmake!Project Files
For \l lupdate to work successfully, it must know which translation
files to produce. The files are simply listed in the application's \c
.pro Qt project file, for example:
\quotefile tt2/tt2.pro
\skipto TRANSLATIONS
\printline TRANSLATIONS
\printline
See the \link lupdate "lupdate" \endlink and \link lrelease
"lrelease" \endlink sections.
\section2 Loading Translations
\quotefile tt1/main.cpp
\skipto main(
\printline main(
\printuntil QApplication
\index main()
This is how a simple \c main() function of a Qt application begins.
\index QTranslator!load()
\index load()!QTranslator
\index QApplication!installTranslator()
\index installTranslator()!QApplication
\quotefile tt1/main.cpp
\skipto main(
\printline main(
\printuntil app.installTrans
For a translation-aware application a translator object is created, a
translation is loaded and the translator object installed into the
application.
\quotefile tt2/main.cpp
\skipto main(
\printline main(
\printuntil app.installTrans
In production applications a more flexible approach, for example,
loading translations according to locale, might be more appropriate. If
the \c .ts files are all named according to a convention such as
\e appname_locale, e.g. \c tt2_fr, \c tt2_de etc, then the
code above will load the current locale's translation at runtime.
If there is no translation file for the current locale the application
will fall back to using the original source text.
\section2 Making the Application Translate User-Visible Strings
\index tr()
\index QObject!tr()
User-visible strings are marked as translation targets by wrapping them
in a \c tr() call, for example:
\code
button = new QPushButton( "&Quit", this );
\endcode
would become
\code
button = new QPushButton( tr("&Quit"), this);
\endcode
\index Q_OBJECT
All \l QObject subclasses that use the \c Q_OBJECT macro implement
the \c tr() function.
Although the \c tr() call is normally made directly since it is
usually called as a member function of a \l QObject subclass, in
other cases an explicit class name can be supplied, for example:
\code
QPushButton::tr("&Quit")
\endcode
or
\code
QObject::tr("&Quit")
\endcode
\section2 Distinguishing Identical Strings That Require Different
Translations
\index Translation Contexts
\index Contexts!for Translation
\index lupdate
The \l lupdate program automatically provides a \e context for every
source text. This context is the class name of the class that contains
the \c tr() call. This is sufficient in the vast majority of cases.
Sometimes however, the translator will need further information to
uniquely identify a source text; for example, a dialog that contained
two separate frames, each of which contained an "Enabled" option would
need each identified because in some languages the translation would
differ between the two. This is easily achieved using the
two argument form of the \c tr() call, e.g.
\code
rbc = new QRadioButton( tr("Enabled", "Color frame"), this );
\endcode
and
\code
rbh = new QRadioButton( tr("Enabled", "Hue frame"), this );
\endcode
\index Ctrl Key
Ctrl key accelerators are also translatable:
\quotefile tt3/mainwindow.cpp
\skipto quit()
\printline quit()
\printuntil Quit
It is strongly recommended that the two argument form of \c tr() is used
for Ctrl key accelerators. The second argument is the only clue the
translator has as to the function performed by the accelerator.
\section2 Helping The Translator With Navigation Information
\index TRANSLATOR!in Comments
\index Translator Comments
\index Comments!for Translators
In large complex applications it may be difficult for the translator to
see where a particular source text comes from. This problem can be
solved by adding a comment using the keyword \e TRANSLATOR which
describes the navigation steps to reach the text in question; e.g.
\code
/* TRANSLATOR FindDialog
Choose Edit|Find from the menu bar or press Ctrl+F to pop up the
Find dialog.
*/
\endcode
These comments are particularly useful for widget classes.
\section2 Coping With C++ Namespaces
\index Namespaces
\index C++!Namespaces
\index lupdate
C++ namespaces and the \c {using namespace} statement can confuse
\l lupdate. It will interpret \c MyClass::tr() as meaning just
that, not as \c MyNamespace::MyClass::tr(), even if \c MyClass is
defined in the \c MyNamespace namespace. Runtime translation of
these strings will fail because of that.
\index TRANSLATOR!in Comments
\index Translator Comments
\index Comments!for Translators
You can work around this limitation by putting a \e TRANSLATOR
comment at the beginning of the source files that use \c
MyClass::tr():
\code
/* TRANSLATOR MyNamespace::MyClass */
\endcode
After the comment, all references to \c MyClass::tr() will be
understood as meaning \c MyNamespace::MyClass::tr().
\section2 Translating Text that is Outside of a QObject subclass
\section3 Using QApplication::translate()
If the quoted text is not in a member function of a QObject subclass,
use either the tr() function of an appropriate class, or the
QApplication::translate() function directly:
\code
void some_global_function( LoginWidget *logwid )
{
QLabel *label = new QLabel(
LoginWidget::tr("Password:"), logwid );
}
void same_global_function( LoginWidget *logwid )
{
QLabel *label = new QLabel(
qApp->translate("LoginWidget", "Password:"),
logwid );
}
\endcode
\section3 Using QT_TR_NOOP() and QT_TRANSLATE_NOOP()
If you need to have translatable text completely outside a function,
there are two macros to help: QT_TR_NOOP() and QT_TRANSLATE_NOOP().
These macros merely mark the text for extraction by \l{lupdate}.
The macros expand to just the text (without the context).
Example of QT_TR_NOOP():
\code
QString FriendlyConversation::greeting( int greet_type )
{
static const char* greeting_strings[] = {
QT_TR_NOOP( "Hello" ),
QT_TR_NOOP( "Goodbye" )
};
return tr( greeting_strings[greet_type] );
}
\endcode
Example of QT_TRANSLATE_NOOP():
\code
static const char* greeting_strings[] = {
QT_TRANSLATE_NOOP( "FriendlyConversation", "Hello" ),
QT_TRANSLATE_NOOP( "FriendlyConversation", "Goodbye" )
};
QString FriendlyConversation::greeting( int greet_type )
{
return tr( greeting_strings[greet_type] );
}
QString global_greeting( int greet_type )
{
return qApp->translate( "FriendlyConversation",
greeting_strings[greet_type] );
}
\endcode
\section1 Tutorials
Three tutorials are presented. The first demonstrates the creation of
a \l QTranslator object. It also shows the simplest use of the \c
tr() function to mark user-visible source text for translation. The
second tutorial explains how to make the application load the
translation file applicable to the current locale. It also shows the
use of the two-argument form of \c tr() which provides additional
information to the translator. The third tutorial explains how
identical source texts can be distinguished even when they occur in
the same context. This tutorial also discusses how the translation
tools help minimize the translator's work when an application is
upgraded.
\section2 Tutorial 1: Loading and Using Translations
\img tt1_en.png
\caption Tutorial 1 Screenshot, English version
\include tt1/tt1.pro
\caption \c tt1.pro
\include tt1/main.cpp
\caption \c main.cpp
This example is a reworking of the \link tutorial1-01.html
"hello-world" \endlink example from \link tutorial.html Tutorial
#1\endlink, with a Latin translation. The \e {Tutorial 1 Screenshot,
English version}, above, shows the English version.
\quotefile tt1/main.cpp
\section3 Line by Line Walk-through
\quotefile tt1/main.cpp
\skipto qtranslator
\printline qtranslator
\index QTranslator
This line includes the definition of the \l QTranslator class.
Objects of this class provide translations for user-visible text.
\skipto QTranslator
\printuntil tor
Creates a \l QTranslator object without a parent.
\printline load
\index tt1_la.qm
Tries to load a file called \c tt1_la.qm (the \c .qm file extension is
implicit) that contains Latin translations for the source texts used in
the program. No error will occur if the file is not found.
\index QApplication!installTranslator()
\index installTranslator()!QApplication
\printline installTranslator
Adds the translations from \c tt1_la.qm to the pool of translations used
by the program.
\index Hello World
\printline hello
Creates a push button that displays "Hello world!". If \c tt1_la.qm
was found and contains a translation for "Hello world!", the
translation appears; if not, the source text appears.
\index tr()
\index QObject!tr()
All classes that inherit \l QObject have a \c tr() function. Inside
a member function of a \l QObject class, we simply write \c tr("Hello
world!") instead of \c QPushButton::tr("Hello world!") or \c
QObject::tr("Hello world!").
\section3 Running the Application in English
\index English Language
Since we haven't made the translation file \c tt1_la.qm, the source text
is shown when we run the application:
\img tt1_en.png
\caption Tutorial 1 Screenshot, English version
\section3 Creating a Latin Message File
\index tt1.pro
\index Latin
The first step is to create a project file, \c tt1.pro, that lists
all the source files for the project. The project file can be a qmake
project file, or even an ordinary makefile. Any file that contains
\index SOURCES!in Project Files
\index TRANSLATIONS!in Project Files
\quotefile tt1/tt1.pro
\skipto SOURCES
\printline SOURCES
\skipto TRANSLATIONS
\printline TRANSLATIONS
will work. \e TRANSLATIONS specifies the message files we want to
maintain. In this example, we just maintain one set of translations,
namely Latin.
\index .ts Files
\index Translation Source Files
\index .qm Files
\index Qt Message Files
Note that the file extension is \c .ts, not \c .qm. The \c .ts
translation source format is designed for use during the
application's development. Programmers or release managers run the \l
lupdate program to generate and update \c .ts files with the source
text that is extracted from the source code. Translators read and
update the \c .ts files using \e {Qt Linguist} adding and editing
their translations.
\index XML
The \c .ts format is human-readable XML that can be emailed directly
and is easy to put under version control. If you edit this file
manually, be aware that the default encoding for XML is UTF-8, not
Latin-1 (ISO 8859-1). One way to type in a Latin-1 character such as
'\OSLASH' (Norwegian o with slash) is to use an XML entity:
"\ø". This will work for any Unicode character.
Once the translations are complete the \l lrelease program is used to
convert the \c .ts files into the \c .qm Qt message file format. The
\c .qm format is a compact binary format designed to deliver very
fast lookup performance. Both \l lupdate and \l lrelease read all the
project's source and header files (as specified in the HEADERS and
SOURCES lines of the project file) and extract the strings that
appear in \c tr() function calls.
\index lupdate
\l lupdate is used to create and update the message files (\c tt1_la.ts
in this case) to keep them in sync with the source code. It is safe to
run \l lupdate at any time, as \l lupdate does not remove any
information. For example, you can put it in the makefile, so the \c .ts
files are updated whenever the source changes.
\index .ts Files
\index Translation Source Files
\index XML
Try running \l lupdate right now, like this:
\code
lupdate -verbose tt1.pro
\endcode
(The \c -verbose option instructs \c lupdate to display messages that
explain what it is doing.) You should now have a file \c tt1_la.ts in
the current directory, containing this:
\code
<!DOCTYPE TS><TS>
<context>
<name>QPushButton</name>
<message>
<source>Hello world!</source>
<translation type="unfinished"></translation>
</message>
</context>
</TS>
\endcode
You don't need to understand the file format since it is read and
updated using tools (\l lupdate, \e {Qt Linguist}, \l lrelease).
\section3 Translating to Latin with Qt Linguist
\index Qt Linguist
\index Linguist
We will use \e {Qt Linguist} to provide the translation, although
you can use any XML or plain text editor to enter a translation into a
\c .ts file.
To start \e {Qt Linguist}, type
\code
linguist tt1_la.ts
\endcode
You should now see the text "QPushButton" in the top left pane.
Double-click it, then click on "Hello world!" and enter "Orbis, te
saluto!" in the \e Translation pane (the middle right of the
window). Don't forget the exclamation mark!
Click the \e Done checkbox and choose \e File|Save from the
menu bar. The \c .ts file will no longer contain
\code
<translation type='unfinished'></translation>
\endcode
but instead will have
\code
<translation>Orbis, te saluto!</translation>
\endcode
\section3 Running the Application in Latin
\index Latin
\index lrelease
To see the application running in Latin, we have to generate a \c .qm
file from the \c .ts file. Generating a \c .qm file can be achieved
either from within \e {Qt Linguist} (for a single \c .ts file), or
by using the command line program \l lrelease which will produce one \c
.qm file for each of the \c .ts files listed in the project file.
Generate \c tt1_la.qm from \c tt1_la.ts by choosing
\e File|Release from \e {Qt Linguist}'s menu bar and pressing
\e Save in the file save dialog that pops up. Now run the \e tt1 example
program again. This time the button will be labelled "Orbis, te
saluto!".
\img tt1_la.png
\caption Tutorial 1 Screenshot, Latin version
\section2 Tutorial 2: Using Two or More Languages
\img tt2_en.png
\caption Tutorial 2 Screenshot, English version
\index .pro Files
\index Project Files
\index qmake!Project Files
\include tt2/tt2.pro
\caption tt2.pro
\index Translation Contexts
\index Contexts!for Translation
This example is a slightly more involved and introduces a key
\e {Qt Linguist} concept: "contexts".
\list
\i \c arrowpad.h contains the definition of \c ArrowPad, a custom widget;
\i \c arrowpad.cpp contains the implementation of \c ArrowPad;
\i \c mainwindow.h contains the definition of \c MainWindow, a subclass of
\l QMainWindow
\i \c mainwindow.cpp contains the implementation of \c MainWindow;
\i \c main.cpp contains main().
\endlist
\index tt2.pro
\index French Language
\index Dutch Language
We will use two translations, French and Dutch, although there is no
effective limit on the number of possible translations that can be used
with an application. The relevant lines of \c tt2.pro are
\quotefile tt2/tt2.pro
\skipto HEADERS
\printuntil tt2_nl.ts
\index lupdate
\index tt2_fr.ts
\index tt2_nl.ts
Run \l lupdate; it should produce two identical message files
\c tt2_fr.ts and \c tt2_nl.ts. These files will contain all the source
texts marked for translation with \c tr() calls and their contexts.
\section3 Line by Line Walk-through
\index ArrowPad!in Translation Tutorial
\index English Language
In \c arrowpad.h we define the \c ArrowPad subclass which is a
subclass of \l QWidget. In the \e {Tutorial 2 Screenshot, English
version}, above, the central widget with the four buttons is an
\c ArrowPad.
\quotefile tt2/arrowpad.h
\skipto class ArrowPad
\printline class ArrowPad
\index Q_OBJECT
\index tr()
\index QObject!tr()
\index Translation Contexts
\index Contexts!for Translation
When \l lupdate is run it not only extracts the source texts but it
also groups them into contexts. A context is the name of the class in
which the source text appears. Thus, in this example, "ArrowPad" is a
context: it is the context of the texts in the \c ArrowPad class.
The \c Q_OBJECT macro defines \c tr(x) in \c ArrowPad like this
\index QApplication!translate()
\index translate()!QApplication
\code
qApp->translate( "ArrowPad", x )
\endcode
Knowing which class each source text appears in enables \e {Qt
Linguist} to group texts that are logically related together, e.g.
all the text in a dialog will have the context of the dialog's class
name and will be shown together. This provides useful information for
the translator since the context in which text appears may influence how
it should be translated. For some translations keyboard
accelerators may need to be changed and having all the source texts in a
particular context (class) grouped together makes it easier for the
translator to perform any accelerator changes without introducing
conflicts.
In \c arrowpad.cpp we implement the \c ArrowPad class.
\quotefile tt2/arrowpad.cpp
\skipto QPushButton
\printline QPushButton
We call \c ArrowPad::tr() for each button's label since the labels are
user-visible text.
\img tt2_en.png
\caption Tutorial 2 Screenshot, English version
\index Q_OBJECT
\index MainWindow!in Translation Tutorial
\quotefile tt2/mainwindow.h
\skipto QMainWindow
\printline QMainWindow
\printuntil Q_OBJECT
In the \e {Tutorial 2 Screenshot, English version}, above, the whole
window is a \c MainWindow. This is defined in the \c mainwindow.h
header file. Here too, we use \c Q_OBJECT, so that \c MainWindow will
become a context in \e {Qt Linguist}.
In the implementation of \c MainWindow, \c mainwindow.cpp, we create
an instance of our \c ArrowPad class
\quotefile tt2/mainwindow.cpp
\skipto arrow pad
\printline arrow pad
We also call \c MainWindow::tr() twice, once for the menu item and
once for the accelerator.
\index Ctrl Key
\index Alt Key
\skipto quit()
\printline quit()
\printuntil Ctrl+Q
Note the use of \c tr() to support different keys in other languages.
"Ctrl+Q" is a good choice for Quit in English, but a Dutch translator
might want to use "Ctrl+A" (for Afsluiten) and a German translator
"Strg+E" (for Beenden). When using \c tr() for Ctrl key accelerators,
the two argument form should be used with the second argument
describing the function that the accelerator performs.
\index main()
Our \c main() function is defined in \c main.cpp as usual.
\quotefile tt2/main.cpp
\skipto QTranslator
\printline QTranslator
\printuntil install
\index QTextCodec!locale()
\index locale()!QTextCodec
\index LANG!Environment Variable
\index Environment Variables!LANG
We choose which translation to use according to the current locale.
\l QTextCodec::locale() can be influenced by setting the \c LANG
environment variable, for example. Notice that the use of a naming
convention that incorporates the locale for \c .qm message files,
(and \c .ts files), makes it easy to implement choosing the
translation file according to locale.
If there is no \c .qm message file for the locale chosen the original
source text will be used and no error raised.
\section3 Translating to French and Dutch
We'll begin by translating the example application into French. Start
\e {Qt Linguist} with \c tt2_fr.ts. You should get the seven source
texts ("\&Up", "\&Left", etc.) grouped in two contexts ("ArrowPad"
and "MainWindow").
Now, enter the following translations:
\list
\i \c ArrowPad
\list
\i \&Up - \&Haut
\i \&Left - \&Gauche
\i \&Right - \&Droite
\i \&Down - \&Bas
\endlist
\i \c MainWindow
\list
\i E\&xit - \&Quitter
\i Ctrl+Q - Ctrl+Q
\i \&File - \&Fichier
\endlist
\endlist
It's quickest to press \Key Alt+D (which clicks the \e {Done \& Next}
button) after typing each translation, since this marks the
translation as done and moves on to the next source text.
Save the file and do the same for Dutch working with \c tt2_nl.ts:
\list
\i \c ArrowPad
\list
\i \&Up - \&Boven
\i \&Left - \&Links
\i \&Right - \&Rechts
\i \&Down - \&Onder
\endlist
\i \c MainWindow
\list
\i E\&xit - \&Afsluiten
\i Ctrl+Q - Ctrl+A
\i File - \&Bestand
\endlist
\endlist
We have to convert the \c tt1_fr.ts and \c tt1_nl.ts translation source
files into \c .qm files. We could use \e {Qt Linguist} as we've done
before; however using the command line tool \l lrelease ensures that
\e all the \c .qm files for the application are created without us
having to remember to load and \e File|Release each one
individually from \e {Qt Linguist}.
In practice we would include calls to \l lupdate and \l lrelease in the
application's makefile to ensure that the latest translations are
used.
\omit
an example of a makefile or .pro file that did this would be nice
\endomit
Type
\code
lrelease tt2.pro
\endcode
\index LANG!Environment Variable
\index export!Unix Command
\index setenv!Unix Command
This should create both \c tt2_fr.qm and \c tt2_nl.qm. Set the \c
LANG environment variable to \c fr. In Unix, one of the two following
commands should work
\code
export LANG=fr
setenv LANG fr
\endcode
\index
\index autoexec.bat
\index set!Windows Command
In Windows, either modify \c autoexec.bat or run
\code
set LANG=fr
\endcode
When you run the program, you should now see the French version:
\img tt2_fr.png
\caption Tutorial 2 Screenshot, French version
Try the same with Dutch, by setting \c LANG=nl. Now the Dutch
version should appear:
\img tt2_nl.png
\caption Tutorial 2 Screenshot, Dutch version
\section3 Exercises
Mark one of the translations in \e {Qt Linguist} as not done, i.e.
by unchecking the "done" checkbox; run \l lupdate, then \l lrelease,
then the example. What effect did this change have?
\index Canada
\index French Canada
Set \c LANG=fr_CA (French Canada) and run the example program again.
Explain why the result is the same as with \c LANG=fr.
Change one of the accelerators in the Dutch translation to eliminate the
conflict between \e \&Bestand and \e \&Boven.
\section2 Tutorial 3: Disambiguating Identical Strings
\img tt3_10_en.png
\caption Tutorial 3 Screenshot, "Troll Print 1.0", English version
\include tt3/tt3.pro
\caption \c tt3.pro
\index Portuguese Language
\index Brazilian Language
We've included a translation file, \c tt3_pt.ts, which contains some
Portuguese translations for this example.
\index Troll Print
We will consider two releases of the same application: Troll Print
1.0 and 1.1. We will learn to reuse the translations created for one
release in a subsequent release. (In this tutorial, you need to edit
some source files. It's probably best to copy all the files to a new
temporary directory and work from there.)
Troll Print is a toy example application that lets the user choose
printer settings. It comes in two versions: English and Portuguese.
Version 1.0 consists of these files:
\index tt3.pro
\index tt3_pt.ts
\list
\i \c printpanel.h contains the definition of PrintPanel;
\i \c printpanel.cpp contains the implementation of PrintPanel;
\i \c mainwindow.h contains the definition of \c MainWindow;
\i \c mainwindow.cpp contains the implementation of \c MainWindow;
\i \c main.cpp contains main();
\i \c tt3.pro is the \e qmake project file.
\i \c tt3_pt.ts is the Portuguese message file.
\endlist
\section3 Line by Line Walk-through
The PrintPanel is defined in \c printpanel.h.
\quotefile tt3/printpanel.h
\skipto QVBox
\printline QVBox
\printuntil Q_OBJECT
\index Q_OBJECT
\index PrintPanel!in Translation Tutorial
PrintPanel is a \l QWidget. It needs the \c Q_OBJECT macro for \c
tr() to work properly.
The implementation file is \c printpanel.cpp.
\quotefile tt3/printpanel.cpp
\skipto setSpacing
\skipto /
\printline /
\printline
\printline
\printline
\index Troll Print
Some of the code is commented out in Troll Print 1.0; you will uncomment
it later, for Troll Print 1.1.
\quotefile tt3/printpanel.cpp
\skipto twoSided
\printline twoSided
\printuntil toggle
\printline
\printuntil toggle
Notice the two occurrences of \c tr("Enabled") and of \c
tr("Disabled") in PrintPanel. Since both "Enabled"s and "Disabled"s
appear in the same context \e {Qt Linguist} will only display one
occurrence of each and will use the same translations for the
duplicates that it doesn't display. Whilst this is a useful
timesaver, in some languages, such as Portuguese, the second
occurrence requires a separate translation. We will see how \e {Qt
Linguist} can be made to display all the occurrences for separate
translation shortly.
\index MainWindow!in Translation Tutorial
The header file for \c MainWindow, \c mainwindow.h, contains no
surprises. In the implementation, \c mainwindow.cpp, we have some
user-visible source texts that must be marked for translation.
\quotefile tt3/mainwindow.cpp
\skipto setCaption
\printline setCaption
We must translate the window's caption.
\skipto quit
\printline quit
\printuntil Help
We also need to translate the menu items. Note that the two argument
form of \c tr() is used for the keyboard accelerator, "Ctrl+Q", since
the second argument is the only clue the translator has to indicate
what function that accelerator will perform.
\quotefile tt3/main.cpp
\skipto QTranslator
\printuntil installTranslator
\index main()
The \c main() function in \c main.cpp is the same as the one in \link
{Tutorial 2...} Tutorial 2 \endlink. In particular it chooses a
translation file based on the current locale.
\section3 Running Troll Print 1.0 in English and in Portuguese
We will use the translations in the \c tt3_pt.ts file that is provided.
Set the \c LANG environment variable to \c pt, and then run \c tt3.
You should still see the English version, as shown in the \e
{Tutorial 3 Screenshot, "Troll Print 1.0", English version}, above.
Now run \l lrelease, e.g. \c {lrelease tt3.pro}, and then run the
example again. Now you should see the Portuguese edition (Troll
Imprimir 1.0):
\img tt3_10_pt_bad.png
\caption Tutorial 3 Screenshot, "Troll Imprimir 1.0", (Bad) Portuguese version
Whilst the translation has appeared correctly, it is in fact wrong. In
good Portuguese, the second occurrence of "Enabled" should be
"Ativadas", not "Ativado" and the ending for the second translation of
"Disabled" must change similarly too.
If you open \c tt3_pt.ts using \e {Qt Linguist}, you will see that
there is just one occurrence of "Enabled" and of "Disabled" in the
translation source file, even though there are two of each in the
source code. This is because \e {Qt Linguist} tries to minimize the
translator's work by using the same translation for duplicate source
texts. In cases such as this where an identical translation is wrong,
the programmer must disambiguate the duplicate occurrences. This is
easily achieved by using the two argument form of \c tr().
We can easily determine which file must be changed because the
translator's "context" is in fact the class name for the class where
the texts that must be changed appears. In this case the file is \c
printpanel.cpp, where the there are four lines to change. Add the
second argument "two-sided" in the appropriate \c tr() calls to the
first pair of radio buttons:
\code
but = new QRadioButton( tr("Enabled", "two-sided"), twoSided );
but = new QRadioButton( tr("Disabled", "two-sided"), twoSided );
\endcode
and add the second argument "colors" in the appropriate \c tr() calls
for the second pair of radio buttons:
\code
but = new QRadioButton( tr("Enabled", "colors"), colors );
but = new QRadioButton( tr("Disabled", "colors"), colors );
\endcode
\index lupdate
\index tt3_pt.ts
Now run \l lupdate and open \c tt3_pt.ts with \e {Qt Linguist}. You
should now see two changes.
First, the translation source file now contains \e three "Enabled",
"Disabled" pairs. The first pair is marked "(obs.)" signifying that they
are obsolete. This is because these texts appeared in \c tr() calls that
have been replaced by new calls with two arguments. The second pair has
"two-sided" as their comment, and the third pair has "colors" as their
comment. The comments are shown in the \e {Source text and comments}
area in \e {Qt Linguist}.
Second, the translation text "Ativado" and "Desativado" have been
automatically used as translations for the new "Enabled" and "Disabled"
texts, again to minimize the translator's work. Of course in this case
these are not correct for the second occurrence of each word, but they
provide a good starting point.
Change the second "Ativado" into "Ativadas" and the second
"Desativado" into "Desativadas", then save and quit. Run \l lrelease
to obtain an up-to-date binary \c tt3_pt.qm file, and run Troll Print
(or rather Troll Imprimir).
\img tt3_10_pt_good.png
\caption Tutorial 3 Screenshot, "Troll Imprimir 1.0", (Good) Portuguese version
\index Translator Comments
\index Comments!for Translators
The second argument to \c tr() calls, called "comments" in \e {Qt
Linguist}, distinguish between identical source texts that occur in
the same context (class). They are also useful in other cases to give
clues to the translator, and in the case of Ctrl key accelerators are
the only means of conveying the function performed by the accelerator to
the translator.
\index TRANSLATOR!in Comments
\index Translator Comments
\index Comments!for Translators
An additional way of helping the translator is to provide information on
how to navigate to the particular part of the application that contains
the source texts they must translate. This helps them see the context
in which the translation appears and also helps them to find and test
the translations. This can be achieved by using a \e TRANSLATOR comment
in the source code:
\code
/* TRANSLATOR MainWindow
In this application the whole application is a MainWindow.
Choose Help|About from the menu bar to see some text
belonging to MainWindow.
*/
\endcode
Try adding these comments to some source files, particularly to
dialog classes, describing the navigation necessary to reach the
dialogs. You could also add them to the example files, e.g. \c
mainwindow.cpp and \c printpanel.cpp are appropriate files. Run \l
lupdate and then start \e {Qt Linguist} and load in \c tt3_pt.ts.
You should see the comments in the \e {Source text and comments} area
as you browse through the list of source texts.
Sometimes, particularly with large programs, it can be difficult for
the translator to find their translations and check that they're
correct. Comments that provide good navigation information can save
them time:
\code
/* TRANSLATOR ZClientErrorDialog
Choose Client|Edit to reach the Client Edit dialog, then choose
Client Specification from the drop down list at the top and pick
client Bartel Leendert van der Waerden. Now check the Profile
checkbox and then click the Start Processing button. You should
now see a pop up window with the text "Error: Name too long!".
This window is a ZClientErrorDialog.
*/
\endcode
\section3 Troll Print 1.1
We'll now prepare release 1.1 of Troll Print. Start your favorite text
editor and follow these steps:
\list
\i Uncomment the two lines that create a \l QLabel with the text
"\<b\>TROLL PRINT\</b\>" in \c printpanel.cpp.
\i Word-tidying: Replace "2-sided" by "Two-sided" in \c printpanel.cpp.
\i Replace "1.0" with "1.1" everywhere it occurs in \c mainwindow.cpp.
\i Update the copyright year to 1999-2000 in \c mainwindow.cpp.
\endlist
(Of course the version number and copyright year would be consts or
#defines in a real application.)
Once finished, run \l lupdate, then open \c tt3_pt.ts in \e {Qt
Linguist}. The following items are of special interest:
\list
\i \c MainWindow
\list
\i Troll Print 1.0 - marked "(obs.)", obsolete
\i About Troll Print 1.0 - marked "(obs.)", obsolete
\i Troll Print 1.0. Copyright 1999 Macroshaft, Inc. -
marked "(obs.)", obsolete
\i Troll Print 1.1 - automatically translated as
"Troll Imprimir 1.1"
\i About Troll Print 1.1 - automatically translated as
"Troll Imprimir 1.1"
\i Troll Print 1.1. Copyright 1999-2000 Macroshaft,
Inc. - automatically translated as "Troll Imprimir 1.1.
Copyright 1999-2000 Macroshaft, Inc."
\endlist
\i \c PrintPanel
\list
\i 2-sided - marked "(obs.)", obsolete
\i \<b\>TROLL PRINT\</b\> - unmarked, i.e. untranslated
\i Two-sided - unmarked, i.e. untranslated.
\endlist
\endlist
Notice that \l lupdate works hard behind the scenes to make revisions
easier, and it's pretty smart with numbers.
Go over the translations in \c MainWindow and mark these as "done".
Translate "\<b\>TROLL PRINT\</b\>" as "\<b\>TROLL IMPRIMIR\</b\>".
When you're translating "Two-sided", press the \e {Guess Again}
button to translate "Two-sided", but change the "2" into "Dois".
Save and quit, then run \l lrelease. The Portuguese version
should look like this:
\img tt3_11_pt.png
\caption Tutorial 3 Screenshot, "Troll Imprimir 1.1", Portuguese version
Choose \e{Ajuda|Sobre}, (\e{Help|About}), to see the about box
\img tt3_11_about_pt.png
\caption Tutorial 3 Screenshot, About box, Portuguese version
\index English Language
\index Translating Qt
\index Qt!Translating Qt
If you choose \e {Ajuda|Sobre Qt}, (\e {Help|About Qt}), you'll get
an English dialog. Oops! Qt itself needs to be translated. See the
document \link i18n.html#qt-itself Internationalization with Qt
\endlink for details.
Now set \c LANG=en to get the original English version:
\img tt3_11_en.png
\caption Tutorial 3 Screenshot, "Troll Print 1.1", English version
\section2 Summary
These tutorials cover all that you need to know to prepare your Qt
applications for translation.
At the beginning of a project add the translation source files to be
used to the project file and add calls to \l lupdate and \l lrelease to
the make file.
During the project all the programmer must do is wrap any user-visible
text in \c tr() calls. They should also use the two argument form for
Ctrl key accelerators, or when asked by the translator for the cases
where the same text translates into two different forms in the same
context. The programmer should also include \e TRANSLATION comments to
help the translator navigate the application.
|