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.
1146 lines
36 KiB
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 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.
|