Spec-Zone.ru › Qt 5.15

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

Написание кроссплатформенного международного программного обеспечения с 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.15/i18n-source-translation.html

Spec-Zone.ru

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