Writing Source Code for Translation
Write QML and Qt C++ source code in a way that enables you to localize applications:
- Mark Strings for Translation
- Use Parameters Instead of Concatenating Strings
- Handle Plural Forms
- Use Regional Number Settings
- Internationalize Date, Time, and Currency
- Mark Translatable Data Text Strings
- Add Comments for Translators
- Disambiguate Identical Text
- Make Keyboard Shortcuts Translatable
- Use Locale to Extend Localization Features
- Enable Translation
- Prepare for Dynamic Language Changes
When developing C++ applications, see also Additional Considerations for C++ Code.
Mark Strings for Translation
Most of the text that must be translated in an application consists of either single words or short phrases. These typically appear as window titles, menu items, tooltips, and labels to buttons, check boxes, and radio buttons.
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.
You can create applications that switch language at runtime, but it requires an effort and comes with a runtime performance cost.
Use translation functions to mark user-visible UI text for translation in QML and C++ code. Qt indexes each translatable string by the translation context it is associated with. The same phrase may occur in more than one context without conflict. If a phrase occurs more than once in a particular context, it is translated only once and the translation is applied to every occurrence within the context.
QML: Use qsTr()
In QML, you can use the following functions to mark user-visible strings for translation in .qml files:
- qsTr()
- qsTranslate()
- qsTrId()
Usually, you use the qsTr()
function:
Text {
id: txt1
text: qsTr("Back")
}
This code makes Back a key entry in the translation source (TS) files. At runtime, the translation system looks up the keyword Back and then gets the corresponding translation value for the current system locale. The result is returned to the text
property and the UI shows the appropriate translation of Back for the current locale. If no translation is found, qsTr()
returns the original string.
The translation context can be set for a given file with:
pragma Translator: ChosenContext
or
pragma Translator: "Chosen::Context"
The context set via qsTranslate()
takes precedent over the context set via pragma
Translator
. In QML, by default, the translation context is the file name.
C++: Use tr()
In C++, use the tr() function to mark text as translatable and to display translated text. The translation context is the name of the QObject subclass the string is used in. To define translation context for new QObject-based classes, use the Q_OBJECT macro in each new class definition.
When tr()
is called, it looks up the translatable string using a QTranslator object, which you must install on the application object, as described in Enable Translation.
For example, assuming the LoginWidget
is a subclass of QWidget:
LoginWidget::LoginWidget() { QLabel *label = new QLabel(tr("Password:")); ... }
This accounts for 99% of the user-visible strings you're likely to write. For information about marking string literals translatable, see Mark Translatable Data Text Strings.
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 QCoreApplication::translate() function directly:
void some_global_function(LoginWidget *logwid) { QLabel *label = new QLabel( LoginWidget::tr("Password:"), logwid); } void same_global_function(LoginWidget *logwid) { QLabel *label = new QLabel( QCoreApplication::translate("LoginWidget", "Password:"), logwid); }
Note: If you disable the const char *
to QString automatic conversion by compiling your application with the macro QT_NO_CAST_FROM_ASCII
defined, you will most likely catch any strings you are missing. See QString::fromUtf8() and QString::fromLatin1() for more information.
Use Parameters Instead of Concatenating Strings
Different languages arrange words differently in phrases, clauses, and sentences, so do not create strings by concatenating words and data. Instead, use %
to insert parameters into strings.
For example, in the string After processing file %1, file %2 is next in line
, %1
and %2
are numbered parameters. At runtime, %1
and %2
are replaced with the first and second file names, respectively. The same numbered parameters must appear in the translation, but not necessarily in the same order. A German translation of the string might reverse the phrases. For example, Datei %2 wird bearbeitet, wenn Datei %1 fertig ist
. Both numbered parameters appear in the translation, but in the reverse order.
QML: Use .arg()
The following QML snippet has a string with two number parameters %1
and %2
. These parameters are inserted with the .arg()
functions.
Text { text: qsTr("File %1 of %2").arg(counter).arg(total) }
%1
refers to the first parameter and %2
refers to the second parameter, so this code produces output like: File 2 of 3.
C++: Use QString::arg()
In C++, use the QString::arg() functions to substitute parameters:
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 code produces output like: 5 of 10 files copied. Copying: somefile.txt.
Handle Plural Forms
You can pass an additional integer parameter (n) to the translation functions and use a special notation for plural forms (%n
) in each translatable string.
Depending on the value of n, the translation function returns a different translation, with the correct grammatical number for the target language. Also, any occurrence of %n
is replaced with n's value.
For example, the English and French translations of the string %n message(s) saved
require different plural forms.
n | No Translation | French | English |
---|---|---|---|
0 | "0 message(s) saved" | "0 message sauvegardé" | "0 messages saved" |
1 | "1 message(s) saved" | "1 message sauvegardé" | "1 message saved" |
2 | "2 message(s) saved" | "2 messages sauvegardés" | "2 messages saved" |
37 | "37 message(s) saved" | "37 messages sauvegardés" | "37 messages saved" |
This idiom also works with target languages that have several plural forms, such as a dual form. In addition, the idiom handles the n == 0
case correctly for languages such as French that require the singular.
For a summary of the rules that Qt Linguist and lrelease
use to translate strings that contain plural forms, see Translation Rules for Plural Forms.
To handle plural forms in the native language, load a TS file for this language, too. Use the lupdate
tool -pluralonly
command line option, to create TS files that contain only entries with plural forms.
Alternatively, you can use the lconvert
tool's -pluralonly
command line option to remove all non-plural forms from an existing TS file.
QML Example
The following QML code snippet translates the source text into the correct plural form and replaces %n
with the value of total
:
Text { text: qsTr("%n message(s) saved", "", total) }
C++ Example
The following C++ code snippet replaces %n
with the value that the count()
function returns:
int n = messages.count(); showMessage(tr("%n message(s) saved", "", n));
Use Regional Number Settings
If you include the %L
modifier when you specify a parameter, the number is localized according to the current regional settings. The conversion uses the default locale if you set it or the system-wide locale, otherwise.
QML: Use %L
For example, in the following QML snippet, %L1
formats the first parameter according to the number formatting conventions of the currently selected locale (geographical region):
Text { text: qsTr("%L1").arg(total) }
If total
is the number 4321.56, with English regional settings (locale) the output is 4,321.56, whereas with German regional settings it is 4.321,56.
C++: Use %Ln
In C++, you can use %Ln
to produce a localized representation of n
. Use QLocale::setDefault() to set the default locale.
Internationalize Date, Time, and Currency
Present date, time, and currency using the locally preferred formats.
QML: Use QtQml Functions
QML does not have special in-string modifiers for formatting dates and times. Instead, you need to query the current locale (geographical region) and use the methods of Date to format the string.
Qt.locale()
returns a Locale object which contains information about the locale. In particular, the Locale.name property contains the language and country of the current locale. You can use the value as is or parse it to determine the appropriate content for the current locale.
The following snippet gets the current date and time with Date()
, then converts that to a string for the current locale. Then it inserts the date string into the %1
parameter for the appropriate translation.
Text { text: qsTr("Date %1").arg(Date().toLocaleString(Qt.locale())) }
To localize currency numbers, use the Number type. It has similar functions as the Date
type for converting numbers into localized currency strings.
C++: Use QLocale Class
In C++, use QLocale::timeFormat() or QLocale::toString(QTime) or toString(QDate)
:
QLabel *label = new QLabel(this); label->setText(tr("Date %1").arg(QLocale().toString(QDate::currentDate()));
Mark Translatable Data Text Strings
Use _NoOp
functions (in QML) and _NOOP
macros (in C++) to mark translatable string literals for extraction by the lupdate
tool.
QML: Use _NoOp Functions
In QML, use the following functions to mark translatable string literals:
- qsTrNoOp()
- qsTranslateNoOp()
- qsTrIdNoOp()
If the user changes the system language without a reboot, depending on the system, the strings in arrays and list models and other data structures might not be refreshed automatically. To force the texts to be refreshed when they are displayed in the UI, you need to declare the strings with the qsTrNoOp()
function. Then, when you populate the objects for display, you need to explicitly retrieve the translation for each text.
For example:
ListModel { id: myListModel ListElement { //: Capital city of Finland name: qsTrNoOp("Helsinki") } } ... Text { text: qsTr(myListModel.get(0).name) // Get the translation of the name property in element 0 }
C++: Use _NOOP Macros
For translatable text completely outside a function, use the QT_TR_NOOP() and QT_TRANSLATE_NOOP() macros that expand to just the text without the context.
An example of QT_TR_NOOP()
:
QString FriendlyConversation::greeting(int type) { static const char *greeting_strings[] = { QT_TR_NOOP("Hello"), QT_TR_NOOP("Goodbye") }; return tr(greeting_strings[type]); }
An example of QT_TRANSLATE_NOOP()
:
static const char *greeting_strings[] = { QT_TRANSLATE_NOOP("FriendlyConversation", "Hello"), QT_TRANSLATE_NOOP("FriendlyConversation", "Goodbye") }; QString FriendlyConversation::greeting(int type) { return tr(greeting_strings[type]); } QString global_greeting(int type) { return QCoreApplication::translate("FriendlyConversation", greeting_strings[type]); }
Add Comments for Translators
You can add comments in the source code before a string you mark as translatable to clarify its purpose. The comments are included in the TS files that you deliver to the translator.
Note: The TS files are XML files with the source texts and a place for the translated text. The updated TS files are converted into binary translation files and included as part of the final application.
QML: Use //: and //~
In the following code snippet, the text on the //:
line is the main comment for the translator.
The text on the //~
line is optional extra information. The first word of the text is used as an additional identifier in the XML element in the TS file so make sure the first word is not part of the sentence. For example, the comment Context Not related to back-stepping is converted to <extra-Context>Not related to back-stepping
in the TS file.
Text { id: txt1; // This UI string is only used here //: The back of the object, not the front //~ Context Not related to back-stepping text: qsTr("Back"); }
C++: Use Comment Characters
To add comments in C++, annotate the tr()
calls in your code with comments of the form //:
or by marking the beginning and end of the comment.
In the following examples, the comments are associated with the strings passed to tr()
in the context of each call:
//: This name refers to a host name. hostNameLabel->setText(tr("Name:")); /*: This text refers to a C++ code example. */ QString example = tr("Example");
To add optional comments, use:
//~ <field name> <field contents>
The field name should consist of a domain prefix (possibly the conventional file extension of the file format the field is inspired by), a hyphen, and the actual field name in underscore-delimited notation. For storage in TS files, the field name together with the prefix extra-
will form an XML element name. The field contents will be XML-escaped, but otherwise appear verbatim as the element's contents. You can add any number of unique fields to each message.
Example:
//: This is a comment for the translator. //= qtn_foo_bar //~ loc-layout_id foo_dialog //~ loc-blank False //~ magic-stuff This might mean something magic. QString text = MyMagicClass::tr("Sim sala bim.");
In C++, you use an equals sign to add a unique identifier:
//= <id>
You can use the keyword TRANSLATOR for translator comments. Metadata appearing right in front of the TRANSLATOR keyword applies to the whole TS file.
Disambiguate Identical Text
The translation system consolidates the UI text strings into unique items to avoid having to translate the same text multiple times. However, a text might look identical to another text but have a different meaning. For example, in English, back means both a step backward and the part of an object opposite to the front. You need to tell the translation system about these two separate meanings, so the translator can create two separate translations.
QML: Add a Disambiguator to qsTr()
In QML, add a disambiguating string as the second parameter of the qsTr()
function.
In the following code snippet, the ID not front
differentiates this Back text from the backstepping Back text:
Text { id: txt1 // This UI string is used only here //: The back of the object, not the front //~ Context Not related to back-stepping text: qsTr("Back", "not front") }
C++: Add a Disambiguator to tr()
In C++, pass a disambiguating string in the call to tr().
In the following code snippet, the ID recipient
differentiates the name of the recipient from that of the sender:
MyWindow::MyWindow() { QLabel *senderLabel = new QLabel(tr("Name:")); QLabel *recipientLabel = new QLabel(tr("Name:", "recipient")); ...
Make Keyboard Shortcuts Translatable
In its most common form, a keyboard shortcut describes a combination of keys that you press to perform some action. For standard shortcuts, use a standard key to request the platform-specific key sequence associated with each shortcut.
For custom shortcuts, use human-readable strings, such as Ctrl+Q or Alt+F. You can translate them into the appropriate shortcuts for the speakers of different languages.
If you hard-code keyboard shortcuts in your application, translators cannot override them.
When you use keyboard shortcuts in menu item and button text, a mnemonic character (marked by underlining) indicates that pressing Alt or Ctrl with the underlined character performs the same action as clicking the menu item or pressing the button.
For example, applications often use F as the mnemonic character in the File menu, so you can either click the menu item or press Alt+F to open the menu. To define the mnemonic character in the translatable string ("File"), prefix it with an ampersand: "&File"
. The translation for the string should also have an ampersand in it, preferably in front of the same character.
QML Example
In QML:
Menu { id: fileMenu title: qsTr("&File") MenuItem { objectName: "quitMenuItem" text: qsTr("E&xit") onTriggered: Qt.quit() } }
C++: Use the QKeySequence Class
In C++, use QAction and QKeySequence objects to specify the keyboard shortcuts that trigger actions:
exitAct = new QAction(tr("E&xit"), this); exitAct->setShortcuts(QKeySequence::Quit);
The translations of keyboard shortcuts are associated with the QShortcut context.
Use Locale to Extend Localization Features
You might find different graphics or audio more suitable for different geographical regions.
Generally, try to avoid localizing images. Create icons that are globally appropriate, rather than relying on local puns or stretched metaphors. However, you might have to reverse images of left and right pointing arrows for Arabic and Hebrew locales.
Locale is one of the default file selectors, so you can use file selection to display different images that you deliver as resources depending on the system locale.
The QML and C++ code examples in the following sections assume that you deliver the following files in the application resources and use language and country codes as the subfolder names:
images ├── language-icon.png ├── +en_GB │ └── language-icon.png └── +fi_FI └── language-icon.png
QML: Set Image Source
The following QML code snippet shows how to select an icon source image according to the current locale:
icon.source: "qrc:/images/language-icon.png"
C++: Use QFileSelector
The following C++ code snippet uses QFileSelector to pick a language icon from the images
folder according to the system locale:
const QFileSelector selector; const QIcon languageIcon(selector.select(":/images/language-icon.png"));
Enable Translation
TS file names must contain ISO language and country codes:
- language is an ISO-639 language code in lowercase.
- country is an ISO-3166 two-letter country code in uppercase.
For example, qml_de.ts
sets the target language to German, and qml_de_CH.ts
sets the target language to German and the target country to Switzerland. The lrelease
tool generates QM files called qml_de.qm
and qml_de_CH.qm
that the application loads depending on the system locale.
QML: Use QQmlApplicationEngine
In QML, use QQmlApplicationEngine to automatically load translation files from a subdirectory called i18n
in the directory that contains the main QML file. The translation file names must have the prefix qml_
. For example, qml_en_US.qm
.
Applications reload translations when the QJSEngine::uiLanguage or Qt.uiLanguage property value changes.
C++: Use QTranslator
In C++, the TS file names must contain the application name. For example, app_de_DE.ts
.
Typically, your Qt C++ application's main()
function will look like this:
int main(int argc, char *argv[]) { QApplication app(argc, argv); QTranslator myappTranslator; if (myappTranslator.load(QLocale::system(), u"myapp"_s, u"_"_s, u":/i18n"_s)) app.installTranslator(&myappTranslator); return app.exec(); }
For a translation-aware application, you create a QTranslator object, load a translation according to the user's UI display locale at runtime, and install the translator object into the application.
Prepare for Dynamic Language Changes
Both Qt Widgets and Qt Quick use Qt's event system to inform classes about translation changes.
LanguageChange events are posted when you use the QCoreApplication::installTranslator() function to install a new translation. Other application components can also force widgets or QML types derived from the Item type to update themselves by posting LanguageChange
events to them.
By default, LanguageChange
events are propagated to all top-level windows, and from there they're propagated through the entire tree of widgets or QML types derived from Item.
Qt Widgets: Override changeEvent
The default event handler for QWidget subclasses responds to the QEvent::LanguageChange event and calls the changeEvent()
function when necessary.
To make Qt widgets aware of changes to the installed QTranslator objects, reimplement the widget's changeEvent() function to check whether the event is a LanguageChange event and update the text displayed by widgets using the tr() function. For example:
void MyWidget::changeEvent(QEvent *event) { if (event->type() == QEvent::LanguageChange) { titleLabel->setText(tr("Document Title")); ... okPushButton->setText(tr("&OK")); } else QWidget::changeEvent(event); }
When using Qt Designer UI files (.ui) and uic
, you can read the new translation files and call ui.retranslateUi(this)
directly:
void MyWidget::changeEvent(QEvent *event) { if (event->type() == QEvent::LanguageChange) { ui.retranslateUi(this); } else QWidget::changeEvent(event); }
To pass on other change events, call the default implementation of the function.
The list of installed translators might change in response to a LocaleChange event, or the application might provide a UI that allows the user to change the current application language.
QML: Override event for Types Derived from Item
For plain QML applications without any custom C++ registered types, using QQmlApplicationEngine is enough to trigger an update of all translation bindings.
However, if you registered a type derived from QQuickItem, and one of its properties exposes translated text (or is otherwise language dependent), override its event method and emit the property's change signal in it (or call notify in case of bindable properties). For example:
class MyItem : public QQuickItem { Q_OJBECT QML_ELEMENT Q_PROPERTY(QString greeting READ greeting NOTIFY greetingChanged) public signals: void greetingChanged(); public: QString greeting() const { return tr("Hello World!"); } bool event(QEvent *ev) override { if (ev->type() == QEvent::LanguageChange) emit greetingChanged(); return QQuickItem::event(ev); } };
This ensures that any binding in QML in which the property is used is reevaluated and takes the language change into account.
Generic QObject-derived Classes: Use Event Filters
Some classes are neither derived from QWidget nor from QQuickItem, but might still need to handle language change events. In that case, install an event filter on QCoreApplication.
class CustomObject : public QObject { Q_OBJECT public: QList<QQuickItem *> managedItems; CustomObject(QOject *parent = nullptr) : QObject(parent) { QCoreApplication::instance()->installEventFilter(this); } bool eventFilter(QObject *obj, QEvent *ev) override { if (obj == QCoreApplication::instance() && ev->type() == QEvent::LanguageChange) { for (auto item : std::as_const(managedItems)) QCoreApplication::sendEvent(item, ev); // do any further work on reaction, e.g. emit changed signals } return false; } };
This might be necessary when translated strings are provided by the class that later get displayed in a user interface (for example, a custom item model), or when the class acts as a container of Widgets or Quick Items, and is therefore responsible for forwarding the event to them.
Additional Considerations for C++ Code
The following sections contain more information about using the Qt C++ classes and functions in translatable applications:
- Use QString for All User-Visible Text
- Define a Translation Context
- Translate Non-Qt Classes
- Translate Text That is Outside of a QObject Subclass
Use QString for All User-Visible Text
QString uses the Unicode encoding internally, and therefore you can use familiar text processing operations to transparently process all languages in the world. Also, since all Qt functions that present text to the user take a QString object as a parameter, there is no char *
to QString conversion overhead.
Define a Translation Context
The translation context for QObject and each QObject subclass is the class name itself. If you subclass QObject, use the Q_OBJECT macro in the class definition to override the translation context. The macro sets the context to the name of the subclass.
For example, the following class definition includes the Q_OBJECT macro, implementing a new tr()
function that uses the MainWindow
context:
class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(); ...
If you do not use Q_OBJECT in a class definition, the context is inherited from the base class. For example, since all QObject-based classes in Qt provide a context, a new QWidget subclass defined without a Q_OBJECT macro uses the QWidget
context if you invoke its tr()
function.
Translate Non-Qt Classes
You must provide extra information for lupdate
about strings in classes that do not inherit QObject or use the Q_OBJECT macro. To add translation support to a non-Qt class, you can use the Q_DECLARE_TR_FUNCTIONS() macro. For example:
class MyClass { Q_DECLARE_TR_FUNCTIONS(MyClass) public: MyClass(); ... };
This provides the class with tr() functions that you can use to translate strings associated with the class, and enables lupdate
to find translatable strings in the source code.
Alternatively, you can call the QCoreApplication::translate() function with a specific context that lupdate
and Qt Linguist recognize.
Translate Text That is Outside of a QObject Subclass
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 QCoreApplication::translate() function directly:
void some_global_function(LoginWidget *logwid) { QLabel *label = new QLabel( LoginWidget::tr("Password:"), logwid); } void same_global_function(LoginWidget *logwid) { QLabel *label = new QLabel( QCoreApplication::translate("LoginWidget", "Password:"), logwid); }