Qt - translation difficulties
You have written a program in Qt and want to translate it into other languages to make it useful for people in other countries. To do this is not easy, but very simple. To do this, we need to take only three simple steps.
Let us have a simple program:
All she does is create a window that says "Hello, World!" We will make a translation into Russian for her.
All lines that the user sees must be processed by the QObject :: tr () or QcoreApplication :: translate () functions.
All Qt classes inherited from QObject have a member function tr (). Since we work with a string in a global function that does not belong to any class, we use the translate () function, which allows you to specify the context of the translation, that is, the class to which it belongs (in this case, QLabel).
In func tions tr () and translate (), after the translated line, you can specify a comment that will be shown to the translator during the translation of the application into another language. Comments are used to eliminate ambiguity.
If you need to translate text that is outside the function, there are two macros to help: QT_TR_NOOP () and QT_TRANSLATE_NOOP (), similar to tr () and translate (). They quietly mark the text for extraction with the lupdate utility, which we will talk about below:
When you turn off automatic conversion from const char * to QString by compiling a program with a specific macro QT_NO_CAST_FROM_ASCII, you can find all the lines that were skipped.
When we need to insert the values of any variables in the middle of the line, it is best to use the arg () function. For example, when copying files, we could display the progress of the process as follows:
If you need to change the order of the arguments during the translation, then during the translation you will need to swap the variables with the% symbol in places, for example, like this:
Rewrite the program because of this is not required.
After we used tr () throughout the application, we need to create a translation of the text. Translation of the text also contains 3 steps:
Usually, you need to repeat these steps for each release of the application. The lupdate utility does everything possible to reuse translations from previous releases.
Before running lupdate, you need to prepare a project file. Here's what our project file (helloworld.pro file) will look like:
TEMPLATE = app
TARGET = release
DEPENDPATH + =.
INCLUDEPATH + =.
SOURCES + = main.cpp
TRANSLATIONS + = helloworld_ru.ts
When you run lupdate or lrelease, you must provide the project file name as a command line argument.
In our application, we must download the QTranslator :: load () files using QcoreApplication :: installTranslator (). The final version of the program will take the form:
We create a QTranslator object, load the translation file into it using the load () function. In it we indicate the beginning of the file name of our translation. By default, translation files are searched in the program folder, but you can specify any directory by passing its name as the second parameter of the function. The extension ".qm" will be added automatically. The Qlocale :: system (). Name () function returns the name of the current locale, in my case it was ru_RU.UTF-8. The search order for the translation file with load () will be as follows:
However, when I tried to load the translation file in Windows XP in this way, nothing came of it. It turned out that (at least for me) the Qlocale :: system (). Name () function constantly returned the value “C”. Therefore, it is worthwhile to provide an additional way to specify the language of the application interface, for example, through the program settings dialog or command line parameters.
That's all, our application has been translated into Russian, but there are a couple of points that are worth mentioning in this article.
Qt contains about 400 lines inside, which should also be translated into the languages we need. In the $ QTDIR / translations directory, you can find translation files for French, German, and Simplified Chinese, as well as templates for translating into other languages. (This directory also contains some additional unsupported translations that may be useful, for example, translation into Russian).
Usually these translations are loaded in the main () function as well:
Note the use of QLibraryInfo :: location () to detect Qt translations. The developer should request a translation path by processing QLibraryInfo :: TranslationsPath for this function instead of using the QTDIR environment variable in their applications. Although in cases where we are not sure that the user has the full version of Qt, it makes sense to supply this translation file with the program and download it from the program directory (or any other one to choose from).
Some applications must provide changes to the user's language settings during operation. To warn widgets about changes to QTranslators installed, you can redo the function of the changeEvent () widget to check if the event is a LanguageChange event, and update the text displayed by the widgets using the tr () function in the usual way. For instance:
All other change events must be handled by calling the default implementation of this function.
The list of installed translations can be changed in response to the LocaleChange event, or the application can provide an interface to the user that allows him to change the current language of the application.
The default event handler for subclasses of QWidget responds to the QEvent :: LanguageChange event and will call this function if necessary; in other components of the application, you can also force widgets to update themselves by sending them a LanguageChange event.
UPD: As intellinside suggests , the graphical user interface classes generated by Qt Designer have a retranslateUi () function that can be called to dynamically change the application language.
Source program
Let us have a simple program:
- #include
- #include
-
- int main(int argc, char *argv[]) {
- QApplication app(argc, argv);
- QLabel label("Hello, World!");
- label.show();
- return app.exec();
- }
* This source code was highlighted with Source Code Highlighter.
All she does is create a window that says "Hello, World!" We will make a translation into Russian for her.
Step 1. Indication of all lines for which you want to translate
All lines that the user sees must be processed by the QObject :: tr () or QcoreApplication :: translate () functions.
All Qt classes inherited from QObject have a member function tr (). Since we work with a string in a global function that does not belong to any class, we use the translate () function, which allows you to specify the context of the translation, that is, the class to which it belongs (in this case, QLabel).
- #include
- #include
-
- int main(int argc, char *argv[]) {
- QApplication app(argc, argv);
- QLabel label(app.translate("QLabel", "Hello, World!"));
- label.show();
- return app.exec();
- }
* This source code was highlighted with Source Code Highlighter.
In func tions tr () and translate (), after the translated line, you can specify a comment that will be shown to the translator during the translation of the application into another language. Comments are used to eliminate ambiguity.
If you need to translate text that is outside the function, there are two macros to help: QT_TR_NOOP () and QT_TRANSLATE_NOOP (), similar to tr () and translate (). They quietly mark the text for extraction with the lupdate utility, which we will talk about below:
- static const char *greeting_strings[] = {
- QT_TR_NOOP("Hello"),
- QT_TR_NOOP("Goodbye")
- };
- static const char *greeting_strings[] = {
- QT_TRANSLATE_NOOP("HelloWidget", "Hello"),
- QT_TRANSLATE_NOOP("HelloWidget", "Goodbye")
- };
* This source code was highlighted with Source Code Highlighter.
When you turn off automatic conversion from const char * to QString by compiling a program with a specific macro QT_NO_CAST_FROM_ASCII, you can find all the lines that were skipped.
When we need to insert the values of any variables in the middle of the line, it is best to use the arg () function. For example, when copying files, we could display the progress of the process as follows:
- void FileCopier::showProgress(int done, int total,
- const QString ¤tFile)
- {
- label.setText(tr("%1 of %2 files copied.\nCopying: %3")
- .arg(done)
- .arg(total)
- .arg(currentFile));
- }
* This source code was highlighted with Source Code Highlighter.
If you need to change the order of the arguments during the translation, then during the translation you will need to swap the variables with the% symbol in places, for example, like this:
“Copy the file% 3. % 1 of% 2 files copied »
Rewrite the program because of this is not required.
Step 2. Create a translation
After we used tr () throughout the application, we need to create a translation of the text. Translation of the text also contains 3 steps:
- Run lupdate to extract translated text from the source code of a Qt C ++ application, creating a message file for translators (a .ts file). The utility recognizes the tr () and translate () constructors and the QT_TR * _NOOP () macros described above and produces .ts files (usually one for each language).
- Providing translations for sources in a .ts file using Qt Linguist. Since .ts files are in XML format, they can also be edited manually.
- Running lrelease to get a lightweight message file (.qm file) from a .ts file, convenient only for end use. Think of .ts files as “source files,” and .qm files as “object files.” The translator edits .ts files, but users of our application only need .qm files. Both file types are platform and locale independent.
Usually, you need to repeat these steps for each release of the application. The lupdate utility does everything possible to reuse translations from previous releases.
Before running lupdate, you need to prepare a project file. Here's what our project file (helloworld.pro file) will look like:
TEMPLATE = app
TARGET = release
DEPENDPATH + =.
INCLUDEPATH + =.
SOURCES + = main.cpp
TRANSLATIONS + = helloworld_ru.ts
When you run lupdate or lrelease, you must provide the project file name as a command line argument.
Step 3. Download the translation files in the application.
In our application, we must download the QTranslator :: load () files using QcoreApplication :: installTranslator (). The final version of the program will take the form:
- #include
- #include
-
- int main(int argc, char *argv[]) {
- QApplication app(argc, argv);
- QTranslator myTranslator;
- myTranslator.load("helloworld_" + QLocale::system().name());
- app.installTranslator(&myTranslator);
- QLabel label(app.translate("QLabel", "Hello, World!"));
- label.show();
- return app.exec();
- }
* This source code was highlighted with Source Code Highlighter.
We create a QTranslator object, load the translation file into it using the load () function. In it we indicate the beginning of the file name of our translation. By default, translation files are searched in the program folder, but you can specify any directory by passing its name as the second parameter of the function. The extension ".qm" will be added automatically. The Qlocale :: system (). Name () function returns the name of the current locale, in my case it was ru_RU.UTF-8. The search order for the translation file with load () will be as follows:
- helloworld_ru_RU.UTF-8.qm
- helloworld_ru_RU.UTF-8
- helloworld_ru_RU.qm
- helloworld_ru_RU
- helloworld_ru.qm
- helloworld_ru
- helloworld.qm
- helloworld
However, when I tried to load the translation file in Windows XP in this way, nothing came of it. It turned out that (at least for me) the Qlocale :: system (). Name () function constantly returned the value “C”. Therefore, it is worthwhile to provide an additional way to specify the language of the application interface, for example, through the program settings dialog or command line parameters.
That's all, our application has been translated into Russian, but there are a couple of points that are worth mentioning in this article.
Additional text strings
Qt contains about 400 lines inside, which should also be translated into the languages we need. In the $ QTDIR / translations directory, you can find translation files for French, German, and Simplified Chinese, as well as templates for translating into other languages. (This directory also contains some additional unsupported translations that may be useful, for example, translation into Russian).
Usually these translations are loaded in the main () function as well:
- int main(int argc, char *argv[])
- {
- ...
- QTranslator qtTranslator;
- qtTranslator.load("qt_" + QLocale::system().name(),
- QLibraryInfo::location(QLibraryInfo::TranslationsPath));
- app.installTranslator(&qtTranslator);
- ...
- }
* This source code was highlighted with Source Code Highlighter.
Note the use of QLibraryInfo :: location () to detect Qt translations. The developer should request a translation path by processing QLibraryInfo :: TranslationsPath for this function instead of using the QTDIR environment variable in their applications. Although in cases where we are not sure that the user has the full version of Qt, it makes sense to supply this translation file with the program and download it from the program directory (or any other one to choose from).
Dynamic translation
Some applications must provide changes to the user's language settings during operation. To warn widgets about changes to QTranslators installed, you can redo the function of the changeEvent () widget to check if the event is a LanguageChange event, and update the text displayed by the widgets using the tr () function in the usual way. For instance:
- void MyWidget::changeEvent(QEvent *event)
- {
- if (e->type() == QEvent::LanguageChange) {
- titleLabel->setText(tr("Document Title"));
- ...
- okPushButton->setText(tr("&OK"));
- } else
- QWidget::changeEvent(event);
- }
* This source code was highlighted with Source Code Highlighter.
All other change events must be handled by calling the default implementation of this function.
The list of installed translations can be changed in response to the LocaleChange event, or the application can provide an interface to the user that allows him to change the current language of the application.
The default event handler for subclasses of QWidget responds to the QEvent :: LanguageChange event and will call this function if necessary; in other components of the application, you can also force widgets to update themselves by sending them a LanguageChange event.
UPD: As intellinside suggests , the graphical user interface classes generated by Qt Designer have a retranslateUi () function that can be called to dynamically change the application language.