Написание исходного кода для перевода
Написание кроссплатформенного международного программного обеспечения с 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"); Этот вызов получает переведённый текст для «Перейти на предыдущую страницу» из 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 для "quit" в своем приложении, переводчики не смогут его переопределить. Правильный подход:
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/qt-6.1/i18n-source-translation.html