Runtime translation / i18n - QML C++ engine



  • Hi there,

    I'm new in here, and it's about a couple of months I'm using Qt.

    What I'm doing now is playing with dynamic translation in QML.
    I started with those references:
    How to do dynamic translation in QML
    Dynamic translations for mobile apps at runtime?

    The instructions worked, in fact I'm able to change language at runtime.
    But...it doesn't apply to items defined in external qml files and included into the main qml.
    I guess I'm missing something related to C++ and QML linking.

    Here the code I'm using:


    translationtest.h

    #ifndef TRANSLATIONTEST_H
    #define TRANSLATIONTEST_H
    
    #include <QObject>
    #include <QTranslator>
    
    class TranslationTest : public QObject
    {
        Q_OBJECT
        Q_PROPERTY(QString emptyString READ getEmptyString NOTIFY languageChanged)
    public:
        explicit TranslationTest(QObject *parent = 0);
        QString getEmptyString();
        Q_INVOKABLE void selectLanguage(QString language);
    
        QTranslator * translator;
    
    signals:
        void languageChanged();
    
    private:
    
    };
    
    #endif // TRANSLATIONTEST_H
    

    main.cpp

    #include <QGuiApplication>
    #include <QQmlApplicationEngine>
    #include <QQmlContext>
    #include <QQmlComponent>
    
    #include "translationtest.h"
    
    TranslationTest::TranslationTest(QObject *parent) : QObject(parent) {
        translator = new QTranslator(this);
        translator->load(QLocale(QLocale::English), QLatin1String("provaTranslate"), QLatin1String("_"), QLatin1String(":/translations"));
    }
    
    QString TranslationTest::getEmptyString() {
        return "";
    }
    
    void TranslationTest::selectLanguage(QString language) {
        if(language == QString("it")) {
            qApp->removeTranslator(translator);
            translator->load(QLocale(QLocale::Italian), QLatin1String("provaTranslate"), QLatin1String("_"), QLatin1String(":/translations"));
            qApp->installTranslator(translator);
        }
    
        if(language == QString("de")) {
            qApp->removeTranslator(translator);
            translator->load(QLocale(QLocale::German), QLatin1String("provaTranslate"), QLatin1String("_"), QLatin1String(":/translations"));
            qApp->installTranslator(translator);
        }
    
        if(language == QString("en")) {
            qApp->removeTranslator(translator);
            translator->load(QLocale(QLocale::English), QLatin1String("provaTranslate"), QLatin1String("_"), QLatin1String(":/translations"));
            qApp->installTranslator(translator);
        }
    
        emit languageChanged();
    }
    
    
    int main(int argc, char *argv[])
    {
        QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
        QGuiApplication app(argc, argv);
    
        /*! Dynamic translation */
        TranslationTest translator;
        app.installTranslator(translator.translator);
    
        QQmlApplicationEngine engine;
        QQmlComponent component(&engine, QUrl(QLatin1String("qrc:/main.qml")));
        engine.rootContext()->setContextProperty("rootItem",&translator);
        component.create();
    
        return app.exec();
    }
    

    So, with those files "main.qml" is aware of "rootItem" context, which will in fact be linked to the TranslatorTest class.


    Now, that's my main.qml

    import QtQuick 2.7
    import QtQuick.Controls 2.0
    import QtQuick.Layouts 1.0
    
    ApplicationWindow {
        visible: true
        width: 640
        height: 480
        title: qsTr("Hello World") + rootItem.emptyString
    
        SwipeView {
            id: swipeView
            anchors.fill: parent
            currentIndex: tabBar.currentIndex
    
            Page1 {
            }
    
            Page {
                Label {
                    text: qsTr("Second page") + rootItem.emptyString
                    anchors.centerIn: parent
                }
            }
        }
    
        footer: TabBar {
            id: tabBar
            currentIndex: swipeView.currentIndex
            TabButton {
                text: qsTr("First") + rootItem.emptyString
            }
            TabButton {
                text: qsTr("Second") + rootItem.emptyString
            }
        }
    }
    

    The text contained here is successfully changing while choosing another language.
    But the "Page1" item, extenally defined, is not showing the same behaviour. It is simply not changing at all.


    Here my Page1.qml file (extending "Page1Form.ui.qml" functionalities)

    import QtQuick 2.7
    
    Page1Form {
        button1 {
            text: qsTr("Press Me") + rootItem.emptyString
            onClicked: console.log("Button Pressed. Entered text: " + textField1.text)
        }
    
        textField1.placeholderText: qsTr("Text Field") + rootItem.emptyString
    
        label {
            text: "Test: " + Number(23.7).toLocaleString(Qt.locale()) + " °C" + rootItem.emptyString
        }
    
        italiano.onClicked: {
            rootItem.selectLanguage("it")
        }
    
        deutsch.onClicked: {
            rootItem.selectLanguage("de")
        }
    
        english.onClicked: {
            rootItem.selectLanguage("en")
        }
    }
    

    So the C++ "selectLanguage" function is actually called even from "Page1.qml", while it's not receiving any updates given by "emptyString". Everything of "Page1" will be displayed as it's written into the code, even if the first language loaded into the constructor is e.g. "Italian".

    Do you know how to fix this?

    Thank you very much, even just for reading the whole story!



  • In the end everything was fine with the example above.
    I just had to update and release my translation files, because in the meantime I changed the name of a .qml file, and the filename is directly linked into the .ts files created by linguist and used by Qt.
    I feel a bit ashamed but yes, it was really trivial and kinda stupid :)



Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.