Написание исходного кода для перевода
Написание кроссплатформенного международного программного обеспечения с 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, чтобы преобразовать строку в QString с кодировкой Unicode.
Использование 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() для значений акселератoров
Значения акселератoров, такие как 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/qt-5.14/i18n-source-translation.html