QtMiddleSystem design
Как обрабатывать интернационализацию (i18n) и локализацию в Qt?
Строки помечаются макросом tr(), извлекаются утилитой lupdate в .ts файлы, переводятся в Linguist, компилируются lrelease в .qm и загружаются через QTranslator::load() + QApplication::installTranslator().
Интернационализация и локализация в Qt
Qt предоставляет полный инструментарий для i18n: макрос tr() для пометки строк, утилиты lupdate / lrelease для работы с файлами переводов и класс QTranslator для загрузки перевода во время выполнения.
Шаг 1: пометка строк в коде
#include <QObject>
#include <QString>
class MyDialog : public QDialog {
Q_OBJECT
public:
MyDialog(QWidget *parent = nullptr) : QDialog(parent) {
// tr() — основной макрос для переводимых строк
setWindowTitle(tr("Settings"));
// Контекстная подсказка для переводчика
label->setText(tr("File", "menu item"));
// Множественное число
statusLabel->setText(tr("%n file(s) found", "", fileCount));
// tr() вне класса QObject
QString msg = QCoreApplication::translate("MyDialog", "Error");
}
};
Шаг 2: объявление файлов переводов в .pro / CMake
# CMakeLists.txt (Qt 6 + CMake)
find_package(Qt6 REQUIRED COMPONENTS Core Widgets LinguistTools)
set(TS_FILES
translations/myapp_ru.ts
translations/myapp_de.ts
)
qt_add_translations(
myapp # target
TS_FILES ${TS_FILES}
QM_FILES_OUTPUT_VARIABLE QM_FILES
)
# Ресурсы автоматически встраиваются в бинарник
qt_add_resources(myapp "translations"
PREFIX "/i18n"
FILES ${QM_FILES}
)
Шаг 3: извлечение и компиляция переводов
# Извлечь строки из исходников в .ts файлы
lupdate src/ -ts translations/myapp_ru.ts translations/myapp_de.ts
# Открыть GUI-редактор переводов
linguist translations/myapp_ru.ts
# Скомпилировать .ts -> .qm (бинарные файлы перевода)
lrelease translations/myapp_ru.ts -qm translations/myapp_ru.qm
Шаг 4: загрузка перевода в runtime
#include <QApplication>
#include <QTranslator>
#include <QLocale>
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
QTranslator translator;
// QLocale::system() определяет язык ОС
const QString locale = QLocale::system().name(); // например, "ru_RU"
// Загрузка из ресурсов Qt
if (translator.load(":/i18n/myapp_" + locale)) {
app.installTranslator(&translator);
}
// Можно загрузить Qt-переводы для стандартных диалогов
QTranslator qtTranslator;
qtTranslator.load(":/i18n/qt_" + locale);
app.installTranslator(&qtTranslator);
MainWindow w;
w.show();
return app.exec();
}
Динамическое переключение языка
void MainWindow::changeLanguage(const QString &locale) {
static QTranslator *current = nullptr;
if (current) {
qApp->removeTranslator(current);
delete current;
}
current = new QTranslator(this);
if (current->load(":/i18n/myapp_" + locale)) {
qApp->installTranslator(current);
}
// Qt автоматически пошлёт QEvent::LanguageChange всем виджетам
// Переопределите changeEvent() для повторной установки текстов
}
void MainWindow::changeEvent(QEvent *e) {
if (e->type() == QEvent::LanguageChange) {
ui->retranslateUi(this); // если используется Qt Designer
// или вручную: label->setText(tr("Settings"));
}
QMainWindow::changeEvent(e);
}
Форматирование чисел, дат, валют
QLocale locale(QLocale::Russian, QLocale::Russia);
// Числа
qDebug() << locale.toString(1234567.89, 'f', 2); // "1 234 567,89"
// Даты
qDebug() << locale.toString(QDate::currentDate(), QLocale::LongFormat);
// Валюта (Qt не имеет встроенного форматирования валюты — используйте ICU или собственный шаблон)
qDebug() << locale.currencySymbol(); // "руб."
Подводные камни
- Конкатенация строк вместо %1:
tr("Hello") + name— непереводимо; используйтеtr("Hello %1").arg(name). - tr() вне класса QObject:
tr()недоступна в глобальных функциях; используйтеQCoreApplication::translate("context", "string"). - Забытый lupdate после изменений: новые строки не появятся в .ts файле автоматически — нужно перезапускать
lupdate. - Отсутствие LanguageChange в динамическом переключении: если не переопределить
changeEvent(), уже показанные виджеты остаются с текстами предыдущего языка. - Жёстко закодированные строки в .ui файлах: Qt Designer помечает строки как переводимые по умолчанию, но если снять галочку —
lupdateих пропустит. - Множественное число без %n: ручная конкатенация вроде
tr("%1 файлов").arg(n)не работает для языков со сложными правилами множественного числа (русский, арабский); используйте толькоtr("%n", "", n). - Кодировка .ts файлов: .ts — это XML в UTF-8; открытие в редакторе с другой кодировкой портит строки с кириллицей.
- QLocale vs системный язык:
QLocale::system()на Windows может вернутьen_US, даже если пользователь выбрал другой язык интерфейса — проверяйтеQLocale::system().uiLanguages().
Common mistakes
- Объяснять i18n и localization только по синтаксису, без жизненного цикла и стоимости.
- Игнорировать ошибки, null/empty состояния, порядок инициализации или режим сборки.
- Давать пример, который работает в демо, но ломается при изменении владельца ресурса.
- Смешивать signals/slots с generic callbacks и забывать про thread affinity.
What the interviewer is testing
- Кандидат формулирует точную модель для i18n и localization, а не только определение.
- Пример компилируем, безопасен по lifetime и соответствует версии технологии.
- Названы trade-off, ограничения и диагностируемые симптомы ошибки.
- Понимает границу между C++ кодом, runtime/framework metadata и editor/UI слоем.