Написание исходного кода для перевода
Написание кроссплатформенного международного ПО с Qt — это постепенный, поэтапный процесс. Ваше ПО можно интернационализировать на этапах, описанных в следующих разделах. Дополнительную информацию об интернационализации приложений Qt Quick см. в разделе Итернационализация и локализация с Qt Quick.
Использование QString для всего отображаемого пользователю текста
Поскольку QString использует кодировку Unicode внутри, все языки мира могут обрабатываться прозрачно с помощью знакомых операций обработки текста. Кроме того, поскольку все функции Qt, которые отображают текст пользователю, принимают QString в качестве параметра, нет char * преобразования QString.
Строки, которые находятся в «пространстве программиста» (например, имена QObject и тексты форматов файлов), не должны использовать QString; достаточно традиционного char * или класса QByteArray.
Вы вряд ли заметите, что используете Unicode; QString и QChar — просто более удобные версии грубых const char * и char из традиционного C.
char * строки в исходном коде предполагаются закодированными в UTF-8 при неявном преобразовании в QString. Если ваш строковый литерал C использует другую кодировку, используйте QString::fromLatin1() или QTextCodec для преобразования литерала в закодированную в Unicode QString.
Использование tr() для всего литерального текста
В любом месте программы, где используется строковый литерал (текст в кавычках), который будет отображаться пользователю, убедитесь, что он обрабатывается функцией QCoreApplication::translate(). По существу, для достижения этого достаточно использовать функцию tr() для получения переведенного текста для ваших классов, обычно для отображения. Эта функция также используется для указания, какие строки текста в приложении подлежат переводу.
Например, предположим, что LoginWidget — подкласс QWidget:
LoginWidget::LoginWidget()
{
QLabel *label = new QLabel(tr("Password:"));
...
} Это покрывает 99% строк, отображаемых пользователю, которые вы, скорее всего, напишете.
Если текст в кавычках не находится в методе члена подкласса 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);
} Qt индексирует каждую переводимую строку по контексту перевода, с которым она связана; это обычно имя подкласса QObject, в котором она используется.
Контексты перевода определяются для новых классов на основе QObject с помощью макроса Q_OBJECT в каждом новом определении класса.
При вызове tr() она ищет переводимую строку с помощью объекта QTranslator. Для работы перевода один или несколько из них должны быть установлены в объекте приложения, как описано в разделе Включение перевода.
Перевод строк в QML работает точно так же, как и в C++, за исключением того, что вам нужно вызвать qsTr(), а не tr(). См. также страницу Итернационализация и локализация с Qt Quick.
Определение контекста перевода
Контекст перевода для QObject и каждого подкласса QObject — это само имя класса. Разработчики, создающие подклассы QObject, должны использовать макрос Q_OBJECT в определении своего класса для переопределения контекста перевода. Этот макрос устанавливает контекст в имя подкласса.
Например, следующее определение класса включает макрос Q_OBJECT, реализуя новую функцию tr(), использующую MainWindow контекст:
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow();
... Если Q_OBJECT не используется в определении класса, контекст будет унаследован от базового класса. Например, поскольку все классы на основе QObject в Qt предоставляют контекст, новый подкласс QWidget, определенный без макроса Q_OBJECT, будет использовать QWidget контекст, если вызвана его функция tr().
Использование tr() для получения перевода
Следующий пример демонстрирует, как получить перевод для класса, показанного в предыдущем разделе:
void MainWindow::createMenus()
{
fileMenu = menuBar()->addMenu(tr("&File"));
... Здесь контекст перевода — MainWindow, потому что вызвана функция MainWindow::tr(). Текст, возвращаемый функцией tr(), — это перевод "&Файл", полученный из MainWindow контекста.
Когда инструмент перевода Qt, lupdate, используется для обработки набора исходных файлов, текст, заключённый в вызовы tr(), сохраняется в разделе файла перевода, который соответствует его контексту перевода.
В некоторых ситуациях полезно явно задать контекст перевода, полностью квалифицируя вызов tr(); например:
QString text = QScrollBar::tr("Page up"); Этот вызов получает переведенный текст для «Page up» из QScrollBar контекста. Разработчики также могут использовать функцию QCoreApplication::translate() для получения перевода для определенного контекста перевода.
Использование tr() для локализации чисел
Можно локализовать числа, используя соответствующие строки tr():
void Clock::setTime(const QTime &time)
{
if (tr("AMPM") == "AMPM") {
// 12-hour clock
} else {
// 24-hour clock
}
} В примере для США мы оставим перевод «AMPM» без изменений и, таким образом, будем использовать ветку 12-часовых часов; но в Европе мы переведём его по-другому, чтобы код использовал ветку 24-часовых часов.
Перевод классов, не являющихся Qt-классами
Иногда необходимо предоставить поддержку интернационализации для строк, используемых в классах, которые не наследуют QObject или не используют макрос Q_OBJECT для включения функций перевода. Поскольку Qt переводит строки во время выполнения на основе класса, к которому они относятся, и lupdate ищет переводимые строки в исходном коде, классы, не являющиеся Qt-классами, должны использовать механизмы, которые также предоставляют эту информацию.
Один из способов сделать это — добавить поддержку перевода в класс, не являющийся Qt-классом, с помощью макроса Q_DECLARE_TR_FUNCTIONS(); например:
class MyClass
{
Q_DECLARE_TR_FUNCTIONS(MyClass)
public:
MyClass();
...
}; Это предоставляет классу функции tr(), которые можно использовать для перевода строк, связанных с классом, и делает возможным для lupdate найти переводимые строки в исходном коде.
В качестве альтернативы, можно вызвать функцию QCoreApplication::translate() с конкретным контекстом, и это будет распознано lupdate и Qt Linguist.
Комментарии для переводчиков
Разработчики могут включать информацию о каждой переводимой строке, чтобы помочь переводчикам в процессе перевода. Они извлекаются при использовании lupdate для обработки исходных файлов. Рекомендуемый способ добавления комментариев — добавить комментарии к вызовам tr() в вашем коде в формате:
//: ...
или
/*: ... */
Примеры:
//: This name refers to a host name.
hostNameLabel->setText(tr("Name:"));
/*: This text refers to a C++ code example. */
QString example = tr("Example"); В этих примерах комментарии будут связаны со строками, переданными в tr(), в контексте каждого вызова.
Добавление метаданных к строкам
Дополнительные данные могут быть присоединены к каждому переводимому сообщению. Они извлекаются при использовании lupdate для обработки исходных файлов. Рекомендуемый способ добавления метаданных — добавить комментарии к вызовам tr() в вашем коде в формате:
//= <id>
Это можно использовать для присвоения сообщению уникального идентификатора для поддержки инструментов, которым он необходим.
Альтернативный способ прикрепления метаданных — использовать следующий синтаксис:
//~ <field name> <field contents>
Это можно использовать для добавления метаданных к сообщению. Имя поля должно состоять из префикса домена (возможно, обычного расширения файла формата файла, к которому относится поле), дефиса и фактического имени поля в формате с нижним подчеркиванием. Для хранения в файлах TS имя поля вместе с префиксом «extra-» образует имя XML-элемента. Содержимое поля будет экранировано в XML, но в противном случае будет отображаться дословно как содержимое элемента. Любое количество уникальных полей может быть добавлено к каждому сообщению.
Пример:
//: 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."); Ключевое слово TRANSLATOR можно использовать для комментариев переводчика. Метаданные, появляющиеся непосредственно перед ключевым словом TRANSLATOR, применяются ко всему файлу TS.
Разрешение неоднозначности
Если одна и та же переводимая строка используется в разных ролях в одном и том же контексте перевода, можно передать в вызов tr() дополнительную строку идентификации. Этот необязательный аргумент разбиения используется для различения в противном случае идентичных строк.
Пример:
MyWindow::MyWindow()
{
QLabel *senderLabel = new QLabel(tr("Name:"));
QLabel *recipientLabel = new QLabel(tr("Name:", "recipient"));
... В Qt 4.4 и более ранних версиях этот параметр разбиения был предпочтительным способом указания комментариев для переводчиков.
Обработка множественного числа
Некоторые переводимые строки содержат заполнитель для целых значений и должны переводиться по-разному в зависимости от используемых значений.
Чтобы помочь в этой проблеме, разработчики передают дополнительный целочисленный аргумент в функцию tr() и обычно используют специальную запись для множественного числа в каждой переводимой строке.
Если этот аргумент равен или больше нуля, все вхождения %n в результирующей строке заменяются десятичным представлением переданного значения. Кроме того, используемый перевод будет адаптироваться к значению в соответствии с правилами каждого языка.
Пример:
int n = messages.count();
showMessage(tr("%n message(s) saved", "", n)); В таблице ниже показано, какая строка возвращается в зависимости от активного перевода:
| Активный перевод | |||
|---|---|---|---|
| n | Без перевода | Французский | Английский |
| 0 | "Сохранено 0 сообщений" | "Сохранено 0 сообщений" | "Сохранено 0 сообщений" |
| 1 | "Сохранено 1 сообщение" | "Сохранено 1 сообщение" | "Сохранено 1 сообщение" |
| 2 | "Сохранено 2 сообщения" | "Сохранено 2 сообщения" | "Сохранено 2 сообщения" |
| 37 | "Сохранено 37 сообщений" | "Сохранено 37 сообщений" | "Сохранено 37 сообщений" |
Этот метод более гибкий, чем традиционный; например,
n == 1 ? tr("%n message saved") : tr("%n messages saved") потому что он также работает с целевыми языками, имеющими несколько форм множественного числа (например, ирландский имеет специальную форму «двойственного» числа, которая должна использоваться, когда n равно 2), и он правильно обрабатывает случай n == 0 для таких языков, как французский, для которых требуется единственное число.
Для обработки форм множественного числа на родном языке вам также необходимо загрузить файл перевода для этого языка. Утилита lupdate имеет опцию командной строки -pluralonly, которая позволяет создавать файлы TS, содержащие только записи с формами множественного числа.
Дополнительные сведения об этой проблеме см. в статье Qt Quarterly «Формы множественного числа в переводах».
Вместо %n, вы можете использовать %Ln, чтобы получить локализованное представление n. Преобразование использует системную локаль, установленную с помощью QLocale::setDefault(). (Если системная локаль не была указана, используется локаль системы.)
Подробное описание правил перевода строк, содержащих формы множественного числа, можно найти в документе Правила перевода для множественного числа.
Перевод текста, находящегося вне подкласса QObject
Использование QCoreApplication::translate()
Если текст в кавычках не находится в методе члена подкласса 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);
} Использование QT_TR_NOOP() и QT_TRANSLATE_NOOP() в C++
Если вам необходимо иметь текст, подлежащий переводу, полностью вне функции, существуют два макроса, которые помогут: QT_TR_NOOP() и QT_TRANSLATE_NOOP(). Они просто помечают текст для извлечения утилитой lupdate. Макросы расширяются только до текста (без контекста).
Пример использования 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]);
} Пример использования 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]);
} Если вы отключите const char * автоматическое преобразование в QString путем компиляции вашего программного обеспечения с определенным макросом QT_NO_CAST_FROM_ASCII, вы, скорее всего, обнаружите любые отсутствующие строки. Дополнительную информацию см. в QString::fromUtf8() и QString::fromLatin1().
Использование QKeySequence() для значений акселераторов
Значения акселераторов, такие как Ctrl+Q или Alt+F, также необходимо переводить. Если вы встроили Qt::CTRL + Qt::Key_Q для «выйти» в своем приложении, переводчики не смогут его переопределить. Правильный способ:
exitAct = new QAction(tr("E&xit"), this);
exitAct->setShortcuts(QKeySequence::Quit); Использование пронумерованных аргументов
Функции QString::arg() предоставляют простой способ замены аргументов:
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));
} В некоторых языках порядок аргументов может потребоваться изменить, и это легко достигается путем изменения порядка аргументов % . Например:
QString s1 = "%1 of %2 files copied. Copying: %3";
QString s2 = "Kopierer nu %3. Av totalt %2 filer er %1 kopiert.";
qDebug() << s1.arg(5).arg(10).arg("somefile.txt");
qDebug() << s2.arg(5).arg(10).arg("somefile.txt"); выводит правильный результат на английском и норвежском языках:
5 of 10 files copied. Copying: somefile.txt Kopierer nu somefile.txt. Av totalt 10 filer er 5 kopiert.
Дополнительные материалы
Справочник по Qt Linguist, Пример Hello tr(), Правила перевода для множественного числа
© The Qt Company Ltd
Licensed under the GNU Free Documentation License, Version 1.3.
https://doc.qt.io/archives/qt-5.11/i18n-source-translation.html