You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tqt3/tools/linguist/book/linguist-programmer.leaf

1146 lines
36 KiB

\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 TQt 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 TQt application begins.
\index TQTranslator!load()
\index load()!TQTranslator
\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 TQObject!tr()
User-visible strings are marked as translation targets by wrapping them
in a \c tr() call, for example:
\code
button = new TQPushButton( "&Quit", this );
\endcode
would become
\code
button = new TQPushButton( tr("&Quit"), this);
\endcode
\index TQ_OBJECT
All \l TQObject subclasses that use the \c TQ_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 TQObject subclass, in
other cases an explicit class name can be supplied, for example:
\code
TQPushButton::tr("&Quit")
\endcode
or
\code
TQObject::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 TQRadioButton( tr("Enabled", "Color frame"), this );
\endcode
and
\code
rbh = new TQRadioButton( 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 TQObject subclass
\section3 Using QApplication::translate()
If the quoted text is not in a member function of a TQObject subclass,
use either the tr() function of an appropriate class, or the
QApplication::translate() function directly:
\code
void some_global_function( LoginWidget *logwid )
{
TQLabel *label = new TQLabel(
LoginWidget::tr("Password:"), logwid );
}
void same_global_function( LoginWidget *logwid )
{
TQLabel *label = new TQLabel(
tqApp->translate("LoginWidget", "Password:"),
logwid );
}
\endcode
\section3 Using TQT_TR_NOOP() and TQT_TRANSLATE_NOOP()
If you need to have translatable text completely outside a function,
there are two macros to help: TQT_TR_NOOP() and TQT_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 TQT_TR_NOOP():
\code
TQString FriendlyConversation::greeting( int greet_type )
{
static const char* greeting_strings[] = {
TQT_TR_NOOP( "Hello" ),
TQT_TR_NOOP( "Goodbye" )
};
return tr( greeting_strings[greet_type] );
}
\endcode
Example of TQT_TRANSLATE_NOOP():
\code
static const char* greeting_strings[] = {
TQT_TRANSLATE_NOOP( "FriendlyConversation", "Hello" ),
TQT_TRANSLATE_NOOP( "FriendlyConversation", "Goodbye" )
};
TQString FriendlyConversation::greeting( int greet_type )
{
return tr( greeting_strings[greet_type] );
}
TQString global_greeting( int greet_type )
{
return tqApp->translate( "FriendlyConversation",
greeting_strings[greet_type] );
}
\endcode
\section1 Tutorials
Three tutorials are presented. The first demonstrates the creation of
a \l TQTranslator 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 tqtranslator
\printline tqtranslator
\index TQTranslator
This line includes the definition of the \l TQTranslator class.
Objects of this class provide translations for user-visible text.
\skipto TQTranslator
\printuntil tor
Creates a \l TQTranslator 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 TQObject!tr()
All classes that inherit \l TQObject have a \c tr() function. Inside
a member function of a \l TQObject class, we simply write \c tr("Hello
world!") instead of \c TQPushButton::tr("Hello world!") or \c
TQObject::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 TQt 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 TQt 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>TQPushButton</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 TQt Linguist
\index TQt 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 "TQPushButton" 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 TQMainWindow
\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 TQWidget. 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 TQ_OBJECT
\index tr()
\index TQObject!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 TQ_OBJECT macro defines \c tr(x) in \c ArrowPad like this
\index QApplication!translate()
\index translate()!QApplication
\code
tqApp->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 TQPushButton
\printline TQPushButton
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 TQ_OBJECT
\index MainWindow!in Translation Tutorial
\quotefile tt2/mainwindow.h
\skipto TQMainWindow
\printline TQMainWindow
\printuntil TQ_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 TQ_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 TQTranslator
\printline TQTranslator
\printuntil install
\index TQTextCodec!locale()
\index locale()!TQTextCodec
\index LANG!Environment Variable
\index Environment Variables!LANG
We choose which translation to use according to the current locale.
\l TQTextCodec::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 TQVBox
\printline TQVBox
\printuntil TQ_OBJECT
\index TQ_OBJECT
\index PrintPanel!in Translation Tutorial
PrintPanel is a \l TQWidget. It needs the \c TQ_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 TQTranslator
\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 TQRadioButton( tr("Enabled", "two-sided"), twoSided );
but = new TQRadioButton( 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 TQRadioButton( tr("Enabled", "colors"), colors );
but = new TQRadioButton( 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 TQLabel 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! TQt 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.