- 1. Структура проекта
- 2. QmlLanguage.pro
- 3. main.cpp
- 4. qmltranslator.h
- 5. qmltranslator.cpp
- 6. main.qml
- 7. Итог
- 8. Видеоурок
После того, как мы ознакомились с переводами интерфейса приложения на Qt/C++ , настало время изучить возможности динамического перевода приложения, интерфейс которого написан на QML.
При разработке на QML есть пара моментов, которые нужно учесть, в отличие от стандартного приложения на QWidgets, а именно:
- Требуется дополнительная настройка .pro файла проекта;
- Сам процесс загрузки переводов для нужного языка осуществляется в C++ слое;
- Переинициализация перевода интерфейса осуществляется в QML слое с помощью функции qsTr();
- После загрузки перевода, нужно отправить сигнал из C++ слоя в QML слой, чтобы заново перевести интерфейс приложения, что аналогично и для С++ приложения, но требует настройки взаимодействия С++ слоя с QML слоем.
Структура проекта
Создаём проект, в который будут входит следующие файлы:
- QmlLanguage.pro - профайл проекта;
- deployment.pri - файл настройки деплоя, создаётся по умолчанию;
- main.cpp - файл исходных кодов с функцией main;
- qmltranslator.h - заголовочный файл класса загрузки перевода;
- qmltranslator.cpp - файл исходных кодов класса загрузки перевода;
- main.qml - основной файл QML слоя.
QmlLanguage.pro
Создание файла перевода будет аналогичным тому, как это делается при создании переводов для Qt/C++ приложения. То есть нужно подключить файл перевода, с которым будет работать Qt Linguist :
- # Добавляем файл переводов,
- # который является по сути файлом "исходных кодов" для нашего перевода
- TRANSLATIONS += QmlLanguage_ru.ts
Далее запускаем утилиту lupdate, чтобы создать данный файл переводов, редактируем его в Qt Linguist , и запустил утилиту lrelease создаём бинарный файл перевода, который будет подключаться в приложение.
Подробнее об этом Вы можете прочитать в предыдущей статье по использованию QTranslator .
Самый важный момент для работы с переводами в QML то, что нужно подключить все файлы QML в качестве исходников, как обычные C++ файлы. Но с отметкой, что только для использования lupdate_only.
- lupdate_only {
- SOURCES += main.qml
- }
Ниже полный листинг .pro файла проекта.
- TEMPLATE = app
- QT += qml quick widgets
- CONFIG += c++11
- SOURCES += main.cpp \
- qmltranslator.cpp
- # Для того, чтобы создать файл переводов со строками из ресурсов qml
- # понадобится включить файл qml в качестве обычных исходников в pro файле
- # но только для использования утилиты lupdate, чтобы она могла узнать,
- # какие строки нуждаются в переводе
- lupdate_only {
- SOURCES += main.qml
- }
- RESOURCES += qml.qrc \
- translations.qrc
- # Additional import path used to resolve QML modules in Qt Creator's code model
- QML_IMPORT_PATH =
- # Default rules for deployment.
- include(deployment.pri)
- # Добавляем файл переводов,
- # который является по сути файлом "исходных кодов" для нашего перевода
- TRANSLATIONS += QmlLanguage_ru.ts
- HEADERS += \
- qmltranslator.h
main.cpp
Поскольку подключение файлов переводов осуществляется в C++ слое, то потребуется зарегистрировать объект класса переводов в контексте QML. В данном случае мы напишем класс QmlTranslator, который будет обёрткой над QTranslator, поскольку тот класс не имеет методов, с которыми мы могли бы работать через QML.
- #include <QApplication>
- #include <QQmlApplicationEngine>
- #include <QtQml>
- #include "qmltranslator.h"
- int main(int argc, char *argv[])
- {
- QApplication app(argc, argv);
- // Создаём объект для работы с переводами ...
- QmlTranslator qmlTranslator;
- QQmlApplicationEngine engine;
- // и регистрируем его в качестве контекста в Qml слое
- engine.rootContext()->setContextProperty("qmlTranslator", &qmlTranslator);
- engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
- return app.exec();
- }
qmltranslator.h
В классе присутствует метод установки перевода, в который передаётся префикс языка перевода. А из данного метода будет испускаться сигнал об изменении перевода, чтобы была возможность заново перевести весь интерфейс с новым переводом.
Метод установки перевода необходимо пометить макросом Q_INVOKABLE, чтобы была возможность использовать его в QML слое.
- #ifndef QMLTRANSLATOR_H
- #define QMLTRANSLATOR_H
- #include <QObject>
- #include <QTranslator>
- class QmlTranslator : public QObject
- {
- Q_OBJECT
- public:
- explicit QmlTranslator(QObject *parent = 0);
- signals:
- // Сигнал об изменении текущего языка для изменения перевода интерфейса
- void languageChanged();
- public:
- // Метод установки перевода, который будет доступен в QML
- Q_INVOKABLE void setTranslation(QString translation);
- private:
- QTranslator m_translator;
- };
- #endif // QMLTRANSLATOR_H
qmltranslator.cpp
- #include "qmltranslator.h"
- #include <QApplication>
- QmlTranslator::QmlTranslator(QObject *parent) : QObject(parent)
- {
- }
- void QmlTranslator::setTranslation(QString translation)
- {
- m_translator.load(":/QmlLanguage_" + translation, "."); // Загружаем перевод
- qApp->installTranslator(&m_translator); // Устанавливаем его в приложение
- emit languageChanged(); // Сигнализируем об изменении текущего перевода
- }
main.qml
Интерфейс приложения будет выглядеть следующим образом:
Логика приложения следующая: при смене языка в комбобоксе будет изменяться язык приложения.
Поскольку мы зарегистрировали qmlTranslator в контексте QML слоя, то в обработчике изменения текста в комбобоксе будем вызывать метод изменения языка в объекте qmlTranslator. А чтобы отслеживать сигнал изменения языка, необходимо с помощью типа Connections подключить к этому сигналу и написать обработчик, в котором будет вызываться функция retranslateUi(). В функции retranslateUi() вынесены все текстовые свойства всех объектов, которые необходимо подвергнуть переводу. Сделано это для упрощения логики и уменьшения избыточного кода.
- import QtQuick 2.6
- import QtQuick.Controls 1.5
- ApplicationWindow {
- id: applicationWindow
- visible: true
- width: 640
- height: 480
- Label {
- id: helloLabel
- height: 50
- anchors {
- top: parent.top
- left: parent.left
- right: parent.horizontalCenter
- margins: 10
- }
- }
- ComboBox {
- id: comboBox
- anchors {
- top: parent.top
- left: parent.horizontalCenter
- right: parent.right
- margins: 10
- }
- model: ["ru_RU", "en_US"]
- // При изменении текста, инициализируем установку перевода через С++ слой
- onCurrentTextChanged: {
- qmlTranslator.setTranslation(comboBox.currentText)
- }
- }
- Label {
- id: labelText
- wrapMode: Text.Wrap
- anchors {
- top: helloLabel.bottom
- left: parent.left
- right: parent.right
- margins: 10
- }
- }
- // Подключаемся к объекту переводчика
- Connections {
- target: qmlTranslator // был зарегистрирован в main.cpp
- onLanguageChanged: { // при получении сигнала изменения языка
- retranslateUi() // инициализируем перевод интерфейса
- }
- }
- // Функция перевода интерфейса
- function retranslateUi() {
- applicationWindow.title = qsTr("Hello World")
- helloLabel.text = qsTr("Hello World")
- labelText.text = qsTr("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.")
- }
- // Запускаем перевод приложения, когда окно приложения было создано
- Component.onCompleted: {
- retranslateUi();
- }
- }
Итог
В целом динамический перевод приложения на Qt/QML мало чем отличается от перевода приложения с использованием только Qt/C++. Ключевым моментом является то, что нужно правильно подключить сигналы и слоты и правильно вызывать методы для инициализации перевода.
Скачать QML приложение с динамическим переводом
Всё отлично :)
Только у меня вопрос: а как вместо ru_RU и en_US написать в ComboBox'e Russian и English чтобы сохранить функцию смены языка :)
UPD Разобрался
В comboBox model я пишу так:
И перед отправкой кода ru_RU или en_US в С++ делаю проверку
В Qt 5.10 была добавлена новая функция по переводу qml файлов. Теперь не нужно писать класс оболочку, достаточно вызвать функцию engine.retranslate(); и все обновится само.
Это хорошая новость!!!!