Spec-Zone.ru › Qt 5.9

Написание исходного кода для перевода

Написание кроссплатформенного международного программного обеспечения с 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 для «выйти» в своём приложении, переводчики не смогут его переопределить. Правильный способ:

exitAct = new QAction(tr("E&xit"), this);
exitAct->setShortcuts(QKeySequence::Quit);

Использование пронумерованных аргументов

Функции QString::arg() предлагают простой способ подстановки аргументов:

void FileCopier::showProgress(int done, int total,
                              const QString &currentFile)
{
    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.9/i18n-source-translation.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API