Введение
Основной цикл статей касался интернационализации desktop-приложений на Qt Widgets — в этой статье разберём специфику локализации QML-приложений: функцию qsTr() для перевода текстовых строк и динамическое переключение языка интерфейса без перезапуска приложения.
Концепция
qsTr() (аналог tr() для C++ из основного цикла статей) помечает строковый литерал как подлежащий переводу — инструменты Qt Linguist (тот же инструментарий, что используется и для Qt Widgets приложений) извлекают все вызовы qsTr() из QML-файлов для создания файлов перевода (.ts), которые затем переводятся и компилируются в файлы .qm, загружаемые приложением во время выполнения. Для динамического переключения языка без перезапуска приложения нужно явно вызвать qmlEngine->retranslate() после загрузки нового файла перевода, и обеспечить, что все текстовые свойства, использующие qsTr(), повторно пересчитываются — что естественно работает для строк, использующих qsTr() непосредственно в привязке свойства, но требует более явного внимания для строк, вычисленных и сохранённых заранее.
Пример кода: Локализация QML-приложений
// Использование qsTr() для текстовых строк, подлежащих переводу
import QtQuick
import QtQuick.Controls
ApplicationWindow {
title: qsTr("Главное окно приложения")
Column {
Label { text: qsTr("Введите ваше имя:") }
TextField { placeholderText: qsTr("Имя") }
Button {
text: qsTr("Сохранить")
onClicked: console.log(qsTr("Данные сохранены"))
}
// Перевод с параметром через arg()
Label { text: qsTr("Найдено %1 результатов").arg(searchResultsCount) }
}
}
// Динамическое переключение языка во время выполнения C++ кода приложения
#include <QQmlApplicationEngine>
#include <QTranslator>
class LanguageManager : public QObject
{
Q_OBJECT
public:
LanguageManager(QQmlApplicationEngine *engine, QObject *parent = nullptr)
: QObject(parent), m_engine(engine) {}
Q_INVOKABLE void switchLanguage(const QString &languageCode)
{
qApp->removeTranslator(&m_translator);
if (m_translator.load(":/translations/app_" + languageCode + ".qm")) {
qApp->installTranslator(&m_translator);
}
m_engine->retranslate(); // обязательный вызов — заставляет ВСЕ qsTr() в QML пересчитаться заново
}
private:
QQmlApplicationEngine *m_engine;
QTranslator m_translator;
};
Пояснения к коду
qsTr("Найдено %1 результатов").arg(searchResultsCount) показывает паттерн перевода строк с динамическими параметрами — текст шаблона ("Найдено %1 результатов") переводится целиком, с сохранением позиции %1, что даёт переводчику возможность естественно переставить порядок слов в переводе при необходимости (некоторые языки требуют иного порядка слов относительно вставляемого числового значения по сравнению с исходным языком). engine->retranslate() — критичный, легко упускаемый вызов для динамического переключения языка — без него уже загруженные и отображённые QML-привязки, использующие qsTr(), не пересчитываются автоматически даже после загрузки нового файла перевода через installTranslator(), и пользователь продолжил бы видеть текст на предыдущем языке до перезапуска приложения или явного вызова retranslate().
Подводные камни
- Сохранение результата
qsTr()в обычном, не являющемся привязкой свойстве (например, в JavaScript-переменной, вычисленной один раз при инициализации, а не в виде живой привязкиtext: qsTr(...)) — такое сохранённое значение не пересчитается автоматически при вызовеretranslate(), посколькуretranslate()повторно вычисляет именно сами привязки, содержащиеqsTr(), а не произвольный, ранее вычисленный и просто сохранённый результат вызова этой функции. - Конкатенация нескольких отдельных переведённых фрагментов строки вместо использования единого шаблона с параметрами (
qsTr("Найдено ") + count + qsTr(" результатов")вместоqsTr("Найдено %1 результатов").arg(count)) — это, аналогично подводным камням интернационализации в основном цикле статей, лишает переводчика возможности естественно изменить порядок слов для языков, где это грамматически необходимо, и единый шаблон с позиционным параметром (%1) — более гибкий и корректный подход для подавляющего большинства языков перевода. - Забытое включение QML-файлов проекта в процесс сканирования инструментами Qt Linguist для извлечения строк, подлежащих переводу — при настройке процесса локализации проекта важно убедиться, что используемые инструменты (
lupdate) действительно сканируют QML-файлы наравне с C++ файлами, а не только последние, иначе строки, помеченныеqsTr()исключительно в QML-коде, не попадут в файл перевода вовсе. - Отсутствие явного механизма уведомления QML-кода о смене языка для логики, зависящей не от текста напрямую, а от выбранного языка (форматирование чисел/дат, направление текста для языков с письмом справа налево) — переключение языка может требовать более широких изменений интерфейса, чем просто перевод текстовых строк (выравнивание текста, направление расположения элементов для языков RTL), и приложение с серьёзными требованиями к полноценной локализации должно явно учитывать эти более широкие аспекты, а не ограничиваться исключительно переводом отдельных текстовых строк через
qsTr().