Евгений Легоцкой30 мая 2016 г. 8:13

Qt/C++ - Урок 049. QTranslator - динамический перевод мультиязычного приложения на Qt

Рано или поздно разработчик приложений на Qt встречается с необходимостью поддержки мультиязычности в своём приложении. Вот тогда на помощь и приходит класс QTranslator и специализированное приложение для создания переводов Qt Linguist .

Объект класса QTranslator используется для загрузки переводов из специального файла с расширением .qm, который является шестнадцатеричным файлом переводов. Данный файл компилируется из файла переводов в XML формате, который имеет расширение ts и прописывается в pro файле проекта. Данный файл содержит все строки приложения, которые были заключены в функцию tr(). Рекомендую задавать весь интерфейс приложения на английском языке, который будет языком по умолчанию, а переводы уже с нужным языком подгружать из файлов переводов. Если нужный файл перевода не будет найден, то автоматически будет загружен перевод на английском языке. Хотя Вы, конечно, можете применить и другой язык в качестве языка по умолчанию.

Пример загрузки перевода

Структура имени файла перевода играет важную роль в работе с QTranslator. Разберём минимальный пример.

#include "mainwindow.h"
#include <QApplication>
#include <QTranslator>
#include <QLibraryInfo>

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

    QTranslator qtLanguageTranslator;
    qtLanguageTranslator.load(QString("QtLanguage_") + QString("ru_RU"));
    qApp->installTranslator(&qtLanguageTranslator);

    MainWindow w;
    w.show();

    return a.exec();
}

В данном примере создаётся объект класса QTranslator, в который загружается интересующий нас файл с указанием языка. В данном случае полное название файла переводов следующее QtLanguage_ru.qm. То есть дано общее для файла переводов название QtLanguage, а также префикс языка перевода _ru . Но вы уже заметили, что в качестве аргумента для метода load формируется строка QtLanguage_ru_RU. Это делается для того, чтобы определить нужный диалект языка, например американский (en_US) или британский (en_GB) английский. Но если в названии файла перевода нет указания диалекта, то будет выбран файл без уточнения диалекта, то есть QtLanguage_ru.qm.

После того, как файл перевода загружен, его необходимо установить в приложение. В данном случае использован глобальный указатель на приложение qApp->installTranslator(&qtLanguageTranslator).

Создание файла перевода

После того, как мы разобрались с минимальным способом установки перевода в приложение, давайте разберёмся с тем, а как вообще можно создать перевод. Для создания перевода необходимо пользоваться функциями tr(), trUtf8(), translate() в приложении и т.д. То есть весь текст, который будет требовать перевода необходимо обрамлять именно в эти функции, чтобы потом создать файл перевода. Выглядеть это может следующим образом:

label->setText(QApplication::translate("MainWindow", "Select Language", 0));
label_2->setText(tr("Hello"));
label_3->setText(trUtf8("Hello world"));

После того, как в приложении помечены все необходимые для перевода строки, необходимо будет создать файл переводов, сделать непосредственно сам перевод и скомпилировать итоговый файл. Для этого используется следующие программы:

  1. lupdate - программа для формирования файла перевода из поля TRANSLATIONS в pro файле, а также обновления информации о всех новых строках в приложении, требующих перевода.
  2. lrelease - программа для сборки итогового файла переводов, который будет использоваться в приложении.
  3. Qt Linguist - непосредственно сам пакет для создания переводов.

Первым шагом, который необходимо будет сделать, после того, как в приложении уже прописаны строки, требующие перевода, это добавить файл для перевода в pro файл проекта. Возможно, Вы также захотите указать информацию о кодировке, которая применяется для перевода.

TRANSLATIONS += QtLanguage_ru.ts

CODECFORSRC     = UTF-8

Далее необходимо воспользоваться утилитой lupdate для создания файла QtLanguage_ru.ts. В Qt Creator ищите здесь: Инструменты -> Внешние -> Linguist -> lupdate .

lupdate сообщит о результате поиска строк для перевода.

Запускается внешняя утилита «D:\Qt\5.6\mingw49_32\bin\lupdate.exe» D:/AndroidQT/QTProjects/QtLanguage/QtLanguage.pro
Updating 'QtLanguage_ru.ts'...
    Found 4 source text(s) (0 new and 4 already existing)

«D:\Qt\5.6\mingw49_32\bin\lupdate.exe» завершилась

Далее открываем Qt Linguist производим перевод всех строк, помечая, какие из них переведены (Это чисто служебная информация для самого переводчика, чтобы не вспоминать, что было переведено, а что нет.)

После создания перевода сохраните файл и скомпилируйте шестнадцатеричный файл перевода, воспользовавшись утилитой lrelease. Её можно найти в Qt Creator там же, где и lupdate, либо запустить из Qt Linguist: File -> Release.

Для применения файла необходимо будет поместить его в каталог с исполняемым файлом приложения, а в коде прописать путь к этому файлу.

Динамический перевод приложения

Загрузка перевода приложения - это хорошо, но что если необходимо менять перевод динамически? Давайте рассмотрим небольшой пример, в котором имеется QComboBox с указанием двух языков: английского и русского, по изменению которого будет происходить смена перевода в приложении.

Структура проекта

  • QtLanguage.pro - профайл проекта;
  • main.cpp - файл проекта с функцией main
  • mainwindow.h - заголовочный файл главного окна приложения;
  • mainwindow.cpp - файл исходных кодов главного окна приложения;
  • mainwindow.ui - файл формы главного окна приложения.

mainwindow.ui

Я предпочитаю использовать графический дизайнер для создания графического интерфейса приложения, поскольку это ускоряет разработку, если не требуется чего-то сложного во внешнем виде приложения, поэтому создадим окно со следующим внешним видом.

В данном окне имеется:

  • label
  • label_2
  • comboBox

С переводом которых мы и будем работать. Удобство графического дизайнера заключается также и в том, что он автоматически формирует метод retranslateUi(), который используется для изменения перевода всех подписей, которые используются в приложении, хотя можно и самим прописать вручную подобный метод, но лично я не хочу терять время на то, что отлично может быть создано автоматически. Например в данном уроке он выглядит следующим образом:

void retranslateUi(QMainWindow *MainWindow)
{
    MainWindow->setWindowTitle(QApplication::translate("MainWindow", "MainWindow", 0));
    label_2->setText(QApplication::translate("MainWindow", "The QTranslator class provides internationalization support for text output.An object of this class contains a set of translations from a source language to a target language. QTranslator provides functions to look up translations in a translation file. Translation files are created using Qt Linguist.", 0));
    label->setText(QApplication::translate("MainWindow", "Select Language", 0));
} // retranslateUi

QtLanguage.pro

Как говорилось выше, указываем имя файла перевода в профайле проекта.

#-------------------------------------------------
#
# Project created by QtCreator 2016-05-22T14:34:42
#
#-------------------------------------------------

QT       += core gui

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

TARGET = QtLanguage
TEMPLATE = app


SOURCES += main.cpp\
        mainwindow.cpp

HEADERS  += mainwindow.h

FORMS    += mainwindow.ui

TRANSLATIONS = QtLanguage_ru.ts

CODECFORSRC     = UTF-8

main.cpp

А вот в этом файле ничего не будем менять.

#include "mainwindow.h"
#include <QApplication>

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

    MainWindow w;
    w.show();

    return a.exec();
}

mainwindow.h

В заголовочном файле главного окна приложения необходимо объявить объекта класса QTranslator и переопределить метод changeEvent(QEvent *event) , в котором будет определяться событие смены языка приложения.

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QTranslator>
#include <QEvent>

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

protected:
    // Метод получения событий в главном окне приложения
    // В нём будет производиться проверка события смены перевода приложения
    void changeEvent(QEvent * event) override;

private:
    Ui::MainWindow *ui;
    QTranslator qtLanguageTranslator;   // Выделяем перевод в отдельном поле, иначе ничего работать не будет
};

#endif // MAINWINDOW_H

mainwindow.cpp

Логика работы приложения будет следующей: при смене пункта в комбобоксе будет меняться перевод приложения.

Примечание. Не забудьте положить файл перевода с расширением qm рядом с исполняемым файлом приложения и собрать необходимые dll , иначе не заработает.

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QApplication>
#include <QTranslator>
#include <QLibraryInfo>
#include <QDebug>

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    // Задаём два пункта с текстом локалей в комбобоксе
    ui->comboBox->addItems(QStringList() << "ru_RU" << "en_US");

    // подключаем к сигналу изменения пункта комбобокса лямбда функцию,
    // в которой будет изменяться перевод приложения
    // Здесь имеется интересный момент. Поскольку QComboBox имеет перегрузку сигнатуры сигнала,
    // то нам необходимо скастовать сигнал к нужной сигнатуре.
    // В данном случае будем использовать название пункта при его изменении
    connect(ui->comboBox, static_cast<void (QComboBox::*)(const QString &)>(&QComboBox::currentIndexChanged),
            [=](const QString &str){
        qtLanguageTranslator.load("QtLanguage_" + str, ".");   // Загружаем перевод
        qApp->installTranslator(&qtLanguageTranslator);        // Устанавливаем перевод в приложение
    });

    // Сделаем первоначальную инициализацию перевода для окна прилоежния
    qtLanguageTranslator.load(QString("QtLanguage_") + QString("ru_RU"));
    qApp->installTranslator(&qtLanguageTranslator);
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::changeEvent(QEvent *event)
{
    // В случае получения события изменения языка приложения
    if (event->type() == QEvent::LanguageChange) {
        ui->retranslateUi(this);    // переведём окно заново
    }
}

Итог

В результате Вы получите приложение с поддержкой двух язык в своём графическом интерфейсе.

Скачать пример приложения с QTranslator

Видеоурок

Рекомендуем хостинг TIMEWEB
Рекомендуем хостинг TIMEWEB
Стабильный хостинг, на котором располагается социальная сеть EVILEG. Для проектов на Django рекомендуем VDS хостинг.
Поддержать автора Donate
w

Не работает....

Очень содержательный комментарий. По моему опыту, не работает всегда потому, что кто-то что-то не правильно делает. А в видео показано, что все отлично работает.

АК

А к чему относится такая проблема? Есть QPlainTextEdit. У него есть горячие клавиши. Но они работает только на английском языке : Когда на клавиатуре нажимается ctrl + 'x', горячая клавиша "вырезать" срабатывает. Но при нажатии ctrl + 'ч' не срабатывает. Стоит создать тему на форуме или это что-то само собой разумеющиеся?
Данный код в main.cpp не помог :

    QString translatorFileName = QLatin1String("qt_");
    translatorFileName += QLocale::system().name();
    QTranslator *translator = new QTranslator(&a);
    if (translator->load(translatorFileName, QLibraryInfo::location(QLibraryInfo::TranslationsPath)))
        a.installTranslator(translator);

А вы случаем не переводили эти сочетания клавиш там, где они устанавливаются? Если где-то есть этот перевод, то есть там подставлена tr() b trUtf8() функция, то могут быть проблемы, лучше те куски кода не оборачивать в функции перевода и не делать перевода, тем более, что это и не нужно. Скорее всего коды клавиш неправильно определяются.

Скачал пример, увы, не работает. Комбобокс исправно пашет, переключения языков нет.

Ещё подскажите, как без шаманства кастования переключать язык? Например, как это обычно бывает, после перезагрузки приложения.

Странно. Должен был бы работать... проверю на досуге.

Вообще сохраняют инфоромацию о языке в QSettings, который нужно установить. Закрывают приложение и после запуска из QSettings подтягивается инофрмация о локализации приложения, после чего загружается требуемый язык.

Если использовать QComboBox для выбора языка, то с новым синтаксисом сигналов и слотов без каста не получится, но можно использовать шаблонный функционал Qt для упрощения работы с сигналами и слотам. Подробнее в этой статье про QOverload

  • #
  • 13 июля 2018 г. 7:55
Хорошая статья. Только один вопрос как это сделать для CMake?

Интересует именно запись в CMakeList

  1. TRANSLATIONS += QtLanguage_ru.ts
  2. CODECFORSRC = UTF-8
Пытался так, но не работает и хотя в документации написано так.

file(GLOB TRANSLATION_FILES ${CMAKE_CURRENT_SOURCE_DIR}/*.ts)

qt5_add_translation(TRANSLATION_QM ${TRANSLATION_FILES})
add_custom_target(translations DEPENDS ${TRANSLATION_QM})
Ругается на
qt5_add_translation
пишет - неизвестная команда.

qt5_create_translation понимает, но ничего не происходит.

У меня на руках есть один проект, где какие-то потуги с переводами и подключением этого добра в CMAKE делались.

Но там файл перевода добавляется прямо в ресурсы проекта. То есть бинарных qm файл переводов добавлялся в qrc файл. То есть при компилировании перевод сохранялся как ресурс, а потом уже из ресурсов забирался.
 
qt5_add_resources(RCC_RESOURCES
        resources/translations.qrc
        )

add_executable({RCC_RESOURCES})
Как понимаю,
qt5_add_translation или qt5_create_translation
Должны ещё создать этот самый бинарный qm файл переводов

Спасибо,  попробую.

Работает так:

find_package (Qt5LinguistTools)
file (GLOB TS_FILES ${SOURCE_DIR}/translations/*.ts)
qt5_add_translation (QM_FILES ${TS_FILES})
add_custom_target (translations ALL DEPENDS ${QM_FILES})
......
add_executable (${PROJECT_NAME} ${SOURCE_FILES} ${HEADER_FILES} ${UI_FILES} ${RESOURCES} ${QM_FILES})
  • #
  • 13 августа 2019 г. 10:17

Здравствуйте!

Все работает, кроме одного.
Есть у меня ряд строковых локализуемых констант. Например:

static const QString V01 = "Вариант 01";
static const QString V02 = "Вариант 02";

Потом, они используются в мэпе:

QMap<int, QString> Variants =
{
    std::pair<int, QString> (1, V01)
  , std::pair<int, QString> (2, V02)
}

В конце есть функция, возвращающая сроку по коду:

QString variant(const int varCode)
{
    return Variants.value(varCode);
}

Как быть в такой ситуации? Константы в tr() не обернешь.

Сделал так:

static const QString V01 = QCoreApplication::tr("Вариант 01");
static const QString V02 = QCoreApplication::tr("Вариант 02");

Лингвист видит эти значения в контексте QCoreApplication,
позволяет их перевести, но доступа к переводу нет,
выводится на русском языке.
Что я делаю не так?

Самая главная проблема в том, что у вас это константные переменные, и инициализируется они один единственный раз при запуске программы.
Поэтому делать динамический перевод в таком случае у вас не получится. Вам нужно иначе подходить к этой проблеме.

Напишите код так.

QString variant(const int varCode)
{
    switch (varCode)
    {
        case 1: return QCoreApplication::tr("Вариант 01");
        case 2: return QCoreApplication::tr("Вариант 02");
        default: return QString();
    }
}
  • #
  • 14 августа 2019 г. 1:27
  • (ред.)

Спасибо большое. Получилось

P
  • #
  • 19 ноября 2019 г. 14:10

Здравствуйте.
Спасибо за статью. Сделал перевод программы. Все работает.
Только я делал не динамический, но это пока и не надо.
Но с одной проблемой все же столкнулся.
В программе есть сцена. На ней создаются некие графические элементы.
в конструкторе элементов устанавливаю всплывающую подсказку,
например такую

this->setToolTip(tr("Кнопка"));

Все переводится кроме тултипов.
Не подскажете куда копать?

Добрый день.

Вы используете QGraphicsItem? вообще побольшу бы кусок кода, чтобы иметь большее представление. Мысли есть, но не уверен

P
  • Pan
  • #
  • 20 ноября 2019 г. 3:10
  • (ред.)

да, есть классы наследуемые от QGraphicsItem.
в программе при нажатии определенных кнопок(слева на картинке), на сцене появляются Item-ы
с разными настройками. в конструкторе ни чего особого нет,только координаты,название,тултип.

перевод загружается так

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    QTranslator translator;
    translator.load(QString("Language_en"));
    a.installTranslator(&translator);
    MainWindow w;
    w.show();

    return a.exec();
}

Вообще, метод tr является частью класса QObject, если он у вас используется в конструкторе, то вы должны были использовать множественное наследование для вашей кастомной кнопки. Поскольку графические объекты одни из немногих классов, которые не наследуются от QObject, кроме QGraphicsObject, но там также множественное наследование используется.

То есть заголовочный код вашей кнопки должен выглядеть так

class Button : public QObject, public QGraphicsItem
{
    Q_OBJECT
public:
    /* и так далее */
}

Убедитесь, что у вас присутсвует макрос Q_OBJECT, он отвечает за мета информацию и включение возможности перевода.

Если же вы не наследовались от QObject, то перевод можно также включить с помощью другого макроса

class Button : public QGraphicsItem
{
    Q_DECLARE_TR_FUNCTIONS(Button)
public:
    /* и так далее */
}

И в качестве дополнения из личного опыта. Пишите лучше все тексты на английском языке в функциях tr )))

P

у меня множественное наследование.
сначала основной класс myitem

class myItem : public QObject, public QGraphicsItem
{
    Q_OBJECT
    Q_INTERFACES(QGraphicsItem)
public:
    myItem(int x, int y, int w, int h);
    .....
    .....
    ..

со всякими переменными.

а потом от него все остальные , которые появляются на сцене.

"Пишите лучше все тексты на английском языке в функциях tr"
понимаю, просто программа очень спецефическая(это конфигуратор для устройства на микроконтроллере)
и не планировалась для других языков. но потом подтянулись иностранные граждане и попросили перевести..

Покажите, как выглядит реализация конструктора. Поскольку на данный момент я не вижу причин, чтобы переводы не работали.

P
myButton::myButton(int x, int y, int w, int h) : myItem (x, y, w, h)
{
  static int i = 1;
  myVar.text = "Btn" + QString::number(i++);
  myVar.myType = typeSimpleButton;
  this->setToolTip(tr("Кнопка"));
  myVar.func_read = 1;
  configVisibleWidgets |= 1<<visibleSettings::Hsize | 1<<visibleSettings::Wsize
      | 1<<visibleSettings::Color1 | 1<<visibleSettings::ColorText | 1<<visibleSettings::Color2
      | 1<<visibleSettings::TextEdit | 1<<visibleSettings::ModbusWrite
      | 1<<visibleSettings::Toggle | 1<<fontSizeSelect | 1<<itSlave;
}
myItem::myItem(int x, int y, int w, int h)
{
  myVar.rect.x =x;
  myVar.rect.y=y;
  myVar.rect.w=w;
  myVar.rect.h=h;
  setFlags(QGraphicsItem::ItemIsMovable |
           QGraphicsItem::ItemIsSelectable |
           QGraphicsRectItem::ItemIsFocusable);
}
P

при этом тултипы заданные в редакторе форм переводятся...

проблема в конструкторе, напишите так

myItem::myItem(int x, int y, int w, int h) : QObject()
{
  myVar.rect.x =x;
  myVar.rect.y=y;
  myVar.rect.w=w;
  myVar.rect.h=h;
  setFlags(QGraphicsItem::ItemIsMovable |
           QGraphicsItem::ItemIsSelectable |
           QGraphicsRectItem::ItemIsFocusable);
}
P

добавил : QObject()
ни чего не изменилось.

ладно. что-нибудь придумаю

Тогда хоть убейте, не знаю. Где-то не хватает или макроса или корректной реализации конструктора.

Комментарии

Только авторизованные пользователи могут публиковать комментарии.
Пожалуйста, авторизуйтесь или зарегистрируйтесь
Timeweb

Позвольте мне порекомендовать вам отличный хостинг, на котором расположен EVILEG.

В течение многих лет Timeweb доказывает свою стабильность.

Для проектов на Django рекомендую VDS хостинг

Посмотреть Хостинг
VD

C++ - Тест 001. Первая программа и типы данных

  • Результат:73баллов,
  • Очки рейтинга1
Ds

C++ - Тест 003. Условия и циклы

  • Результат:64баллов,
  • Очки рейтинга-1
o

C++ - Тест 001. Первая программа и типы данных

  • Результат:86баллов,
  • Очки рейтинга6
Последние комментарии
RK
РГ

QML - Урок 016. База данных SQLite и работа с ней в QML Qt

Добрый день! можно как то обойтись без метода updateModel()? После вызова этого метода происходит перерисовка страницы(если я правильно понимаю), и все элементы, например, CheckBox перерисовываю…
D:

QML - Урок 016. База данных SQLite и работа с ней в QML Qt

Добрый день, пытаюсь разобраться и подргнать пример под себя. Есть бд с огромным количеством полей. В приложении на виджетах при использовании QTableView все работает и путем простого sql запрос…

Django - Урок 039. Добавление личных сообщений и чатов на сайте - Часть 2 (Счётчик диалогов и чатов с непрочитанными сообщениями)

Добавляйте поле файла в модель сообщения. И в форме сообщения указывайте, что поле с файлом.
Сейчас обсуждают на форуме
ДК

Уйти от gtk

ошибка: Gtk-Message: 15:56:06.190: Failed to load module "atk-bridge" Привет. Начало истории здесь Кратко: на АЛЬТ линукс при запуске в консоли приложения по…
ДК

применяется некорректное разрешение для стилей под обычным пользователем

Привет. Такая проблема на ALT Linux: если запускать приложение от руута, то со стилями и размером шрифта всё в полном порядке. Если же мы запускаем приложение под обычным пользователем, то …

Наследование QWidget

Это утверждение ничего не значит. Наличие методов и т.д. не делает обязательным наследование в том виде, в котором вы его изначально попытались сделать. Тем более, если у вас будет два видж…
  • BlinCT
  • 7 августа 2020 г. 9:05

Динамическое заполнение StackLayout в qml

Всем привет. Пытаюсь решить такую задачку, есть TabBar и его кнопки. StackLayout{ currentIndex: tabBar.currentIndex A {id: tabA} B {id: tabB} C {id: tabC} D {id: ta…
М

QML: изменение стиля при наведении и при нажатии на кнопку

enabled = false перестанет быть активной и не будет ни на что реагировать) Хм.. по-моему пробовал такое. Проверю ещё раз после работы. Ура, спасибо большо…
О нас
Услуги
© EVILEG 2015-2020
Рекомендует хостинг TIMEWEB