Cmake

Подключение Qt Linguist (переводы) в CMake-сборку автоматически

Введение

Локализация Qt-приложения строится вокруг трёх типов файлов: .ts (исходные файлы переводов в XML, редактируемые в Qt Linguist), .qm (скомпилированные бинарные файлы переводов, используемые в рантайме) и непосредственно вызовы tr() в коде, которые помечают строки для перевода. Процесс извлечения строк (lupdate) и компиляции переводов (lrelease) можно автоматизировать прямо в CMake-сборке, чтобы переводы обновлялись и пересобирались без ручного запуска консольных утилит. В статье рассмотрим, как это настроить.

Концепция

Qt предоставляет CMake-функции qt_add_translations() (современный, высокоуровневый способ в Qt 6.2+) или более ручной набор qt6_create_translation()/qt6_add_translation() для интеграции переводов в сборку. Эти функции автоматически вызывают lupdate для синхронизации .ts-файлов с исходным кодом (поиск новых вызовов tr()) и lrelease для компиляции в .qm, который затем подключается к ресурсам приложения или копируется отдельно для загрузки через QTranslator в рантайме.

Пример кода: Подключение Qt Linguist

find_package(Qt6 REQUIRED COMPONENTS Core Widgets LinguistTools)

set(TS_FILES
    translations/myapp_ru.ts
    translations/myapp_en.ts
    translations/myapp_de.ts
)

add_executable(MyApp main.cpp mainwindow.cpp mainwindow.h)
target_link_libraries(MyApp PRIVATE Qt6::Core Qt6::Widgets)

# Современный высокоуровневый способ (Qt 6.2+):
# автоматически вызывает lupdate/lrelease и подключает .qm файлы
qt_add_translations(MyApp
    TS_FILES ${TS_FILES}
    RESOURCE_PREFIX "/i18n"
)
// main.cpp — загрузка перевода в зависимости от системной локали
#include <QApplication>
#include <QTranslator>
#include <QLocale>
#include "mainwindow.h"

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    QTranslator translator;
    const QString locale = QLocale::system().name(); // например, "ru_RU"
    if (translator.load(QLocale(locale), ":/i18n/myapp", "_")) {
        app.installTranslator(&translator);
    }

    MainWindow window;
    window.show();
    return app.exec();
}
// mainwindow.cpp — пометка строк для перевода
#include "mainwindow.h"

MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
{
    setWindowTitle(tr("Главное окно приложения"));
    auto *button = new QPushButton(tr("Нажмите меня"), this);
    setCentralWidget(button);
}

Пояснения к коду

qt_add_translations() связывает список .ts-файлов с целью MyApp: при сборке CMake вызовет lupdate для обновления файлов переводов на основе вызовов tr() в исходниках, а затем lrelease для компиляции их в .qm-файлы, которые автоматически встраиваются в ресурсы приложения с указанным префиксом /i18n.

В main.cpp QTranslator загружает подходящий перевод на основе системной локали через QLocale::system().name() и устанавливает его в приложение через installTranslator() — после этого все вызовы tr() в интерфейсе автоматически возвращают переведённые строки, если соответствующий перевод найден в .qm-файле.

Подводные камни

  • lupdate не запускается автоматически при изменении исходников без явной пересборки. Если в проект добавлены новые строки с tr(), но .ts-файлы не обновлены и не пересобраны, переводчики не увидят новые строки для перевода — стоит включить обновление .ts в регулярный CI-процесс или pre-commit хук.
  • Незакрытые контексты перевода (tr() внутри методов классов, не наследующих QObject) не получают автоматического контекста для группировки в Qt Linguist — переводчику сложнее ориентироваться, в каком месте кода используется конкретная строка.
  • Отсутствие перевода для конкретной локали «тихо» откатывается на исходный (обычно английский или язык кода) текст, без явной ошибки — это легко не заметить при тестировании только на одной локали разработчика.
  • Множественное число (plural forms) обрабатывается неверно при использовании обычного tr() без QCoreApplication::translate с указанием количества. Для строк, зависящих от числа (например, «1 файл» / «5 файлов»), нужно использовать tr("%n файл(ов)", "", count) с правильным форматом, иначе перевод на языки с более сложными правилами множественного числа (как русский, с тремя формами) будет некорректным.