以启用本地化应用程序的方式,编写 QML 和 Qt C++ 源代码:
当开发 C++ 应用程序时,另请参阅 用于 C++ 代码的额外注意事项 .
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,翻译上下文是 QML 类型名称。
可以使用下列函数以标记 .qml 文件中的用户可见翻译字符串:
通常,使用
qsTr()
函数:
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()
返回原始字符串。
在 C++,使用 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.
当
tr()
is called, it looks up the translatable string using a
QTranslator
object, which you must install on the application object, as described in
启用翻译
.
例如,假定
LoginWidget
是子类化的
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 标记可翻译数据文本字符串 .
若引号文本不在成员函数中对于
QObject
子类,使用
tr()
函数从适当类,或
QCoreApplication::translate
() 函数直接:
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); }
注意:
若禁用
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
() 和
QString::fromLatin1
() 了解更多信息。
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.
The following QML snippet has a string with two number parameters
%1
and
%2
. These parameters are inserted with the
.arg()
函数。
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++,使用 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 .
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
被替换采用
n
's value.
For example, the English and French translations of the string
%n message(s) saved
require different plural forms.
n | 不翻译 | 法语 | English |
---|---|---|---|
0 | "0 message(s) saved" | "0 message sauvegardé" | "0 message s saved" |
1 | "1 message(s) saved" | "1 message sauvegardé" | "1 message saved" |
2 | "2 message(s) saved" | "2 message s sauvegardé s " | "2 message s saved" |
37 | "37 message(s) saved" | "37 message s sauvegardé s " | "37 message s 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
复数形式翻译规则
.
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.
The following QML code snippet replaces
%n
with the value of
total
:
Text { text: qsTr("%n message(s) saved").arg(total) }
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));
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.
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) }
若
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++,可以使用
%Ln
以产生本地化表示
n
。使用
QLocale::setDefault
() 以设置默认区域设置。
Present date, time, and currency using the locally preferred formats.
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()
返回
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++,使用
QLocale::timeFormat
() 或
QLocale::toString
(
QTime
) 或
toString(QDate)
:
QLabel *label = new QLabel(this); label->setText(tr("Date %1").arg(QLocale().toString(QDate::currentDate()));
使用
_NoOp
函数 (在 QML) 和
_NOOP
macros (in C++) to mark translatable string literals for extraction by the
lupdate
工具。
In QML, use the following functions to mark translatable string literals:
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.
例如:
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 }
For translatable text completely outside a function, use the QT_TR_NOOP () 和 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]); }
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.
注意: 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.
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"); }
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.
范例:
//: 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.
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.
In QML, add a disambiguating string as the second parameter of the
qsTr()
函数。
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") }
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")); ...
In its most common form, a keyboard shortcut describes a combination of keys that you press to perform some action. For 标准快捷键 , 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:
Menu { id: fileMenu title: qsTr("&File") MenuItem { objectName: "quitMenuItem" text: qsTr("E&xit") onTriggered: Qt.quit() } }
在 c++,使用 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 上下文。
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
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++ 代码片段使用
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"));
TS file names must contain ISO language and country codes:
例如,
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,使用
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_
。例如,
qml_en_US.qm
.
Applications reload translations when the QJSEngine::uiLanguage or Qt.uiLanguage property value changes.
In C++, the TS file names must contain the application name. For example,
app_de_DE.ts
.
Typically, your Qt C++ application's
main()
函数看起来像这样:
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 对象, load a translation according to the user's UI display locale at runtime, and install the translator object into the application.
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.
默认情况下,
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.
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 in the usual way. 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); }
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.
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.
Some classes are neither derived from QWidget nor from QQuickItem , but might still need to handle language change events. In that case, install 事件过滤器 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 项模型 ), or when the class acts as a container of Widgets or Quick Items, and is therefore responsible for forwarding the event to them.
The following sections contain more information about using the Qt C++ classes and functions in translatable applications:
QString
使用
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.
The translation context for QObject and each QObject subclass is the class name itself. If you subclass QObject ,使用 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
上下文:
class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(); ...
若不使用
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()
函数。
You must provide extra information for
lupdate
about strings in classes that do not inherit
QObject
或使用
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.
若引号文本不在成员函数中对于
QObject
子类,使用
tr()
function of an appropriate class or the
QCoreApplication::translate
() 函数直接:
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); }