qtcmakeqmlqtquick2qt-linguist

Translatable qsTr() strings not translating in QtQuick app


I have been trying to create a minimal multilingual app using Qt Quick. Since I am new to Qt I tried to follow the implementation steps in this blog/tutorial post. However, I cannot get the qsTr() strings to translate when I change the UI language. How can I make all strings wrapped in qsTr() translate when the UI language is changed?

The application does build and run, but the strings are not translated when the button is clicked.

The only issues noted during the build process are the following warnings:

[cmake] Running C:\Qt\Tools\CMake_64\bin\cmake.exe -S C:/BasicMultilingualApp -B C:/BasicMultilingualApp/build/Desktop_Qt_6_8_1_MinGW_64_bit-Debug in C:\BasicMultilingualApp\build\Desktop_Qt_6_8_1_MinGW_64_bit-Debug.
[cmake] -- Could NOT find WrapVulkanHeaders (missing: Vulkan_INCLUDE_DIR) 
[cmake] -- Could NOT find WrapVulkanHeaders (missing: Vulkan_INCLUDE_DIR) 

and

C:/Qt/6.8.1/mingw_64/include/QtQml/qqmlprivate.h:348: Ignoring definition of undeclared qualified class
C:/Qt/6.8.1/mingw_64/include/QtQml/qqmlprivate.h:847: Ignoring definition of undeclared qualified class
C:/Qt/6.8.1/mingw_64/include/QtQml/qqmlprivate.h:861: Ignoring definition of undeclared qualified class
C:/Qt/6.8.1/mingw_64/include/QtQml/qqmlprivate.h:879: Ignoring definition of undeclared qualified class
C:/Qt/6.8.1/mingw_64/include/QtQml/qqmlprivate.h:893: Ignoring definition of undeclared qualified class
C:/Qt/6.8.1/mingw_64/include/QtQml/qqmlprivate.h:907: Ignoring definition of undeclared qualified class
C:/Qt/6.8.1/mingw_64/include/QtQml/qqmlprivate.h:922: Ignoring definition of undeclared qualified class
C:/Qt/6.8.1/mingw_64/include/QtQml/qqmlprivate.h:936: Ignoring definition of undeclared qualified class
C:/Qt/6.8.1/mingw_64/include/QtQml/qqmlprivate.h:950: Ignoring definition of undeclared qualified class
Updating '../../i18n/qml_de_DE.ts'...
    Found 2 source text(s) (0 new and 2 already existing)
Updating '.lupdate/qml_en.ts'...
    Found 2 source text(s) (2 new and 0 already existing)

System details:

cmake.exe --build C:/Users/<MyName>/Desktop/QT_Projects/multilingual_demo/build/Desktop_Qt_6_8_1_MinGW_64_bit-Debug --target all multilingual_demo_lrelease multilingual_demo_lupdate

Below I have included the major code components Main.qml and main.cpp along with CMakeLists.txt and the .ts translation file so that my implementation can be easily replicated.

Main.qml

import QtQuick
import QtQuick.Controls

Window {
    width: 640
    height: 480
    visible: true
    title: "Basic Multilingual App" // We choose the app title to be language indifferent

    Component.onCompleted: Qt.uiLanguage = "en" // Set the UI language to English (so that we all start in the same place)

    Column {
        anchors.centerIn: parent
        spacing: 10

        Text {
            id: stringToTranslate
            //: Example string to translate
            text: qsTr("This string must be translated on button click")
        }

        Button {
            id: translateText
            //: Clicking this button translates the text fields
            text: qsTr("Translate text")
            onClicked: {
                Qt.uiLanguage = Qt.uiLanguage === "en" ? "de" : "en" // If UI is English translate to German. Else translate to English
                console.log(Qt.uiLanguage) // Print the UI language to console so we can monitor its changes
            }
        }
    }
}

main.cpp

#include <QGuiApplication>
#include <QQmlApplicationEngine>

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

    QQmlApplicationEngine engine;
    QObject::connect(
        &engine,
        &QQmlApplicationEngine::objectCreationFailed,
        &app,
        []() { QCoreApplication::exit(-1); },
        Qt::QueuedConnection);
    engine.loadFromModule("BasicMultilingualApp", "Main");

    return app.exec();
}

CMakeLists.txt

cmake_minimum_required(VERSION 3.16)

project(BasicMultilingualApp VERSION 0.1 LANGUAGES CXX)

set(CMAKE_CXX_STANDARD_REQUIRED ON)

find_package(Qt6 REQUIRED COMPONENTS Quick LinguistTools)

qt_standard_project_setup(REQUIRES 6.5 I18N_TRANSLATED_LANGUAGES de_DE)

qt_add_executable(appBasicMultilingualApp
    main.cpp
)

qt_add_qml_module(appBasicMultilingualApp
    URI BasicMultilingualApp
    VERSION 1.0
    QML_FILES
        Main.qml
)

qt_add_translations(appBasicMultilingualApp
    TS_FILE_BASE qml
    TS_FILE_DIR i18n
    RESOURCE_PREFIX qt/qml/BasicMultilingualApp/i18n
    LRELEASE_OPTIONS -idbased
)

# Qt for iOS sets MACOSX_BUNDLE_GUI_IDENTIFIER automatically since Qt 6.1.
# If you are developing for iOS or macOS you should consider setting an
# explicit, fixed bundle identifier manually though.
set_target_properties(appBasicMultilingualApp PROPERTIES
#    MACOSX_BUNDLE_GUI_IDENTIFIER com.example.appBasicMultilingualApp
    MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION}
    MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}
    MACOSX_BUNDLE TRUE
    WIN32_EXECUTABLE TRUE
)

target_link_libraries(appBasicMultilingualApp
    PRIVATE Qt6::Quick
)

include(GNUInstallDirs)
install(TARGETS appBasicMultilingualApp
    BUNDLE DESTINATION .
    LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
    RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)

qml_de_DE.ts (German translation file, located in the folder 'i18n'

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1" language="de_DE">
<context>
    <name>Main</name>
    <message>
        <location filename="../Main.qml" line="19"/>
        <source>This string must be translated on button click</source>
        <extracomment>Example string to translate</extracomment>
        <translation>Diese Zeichenfolge muss beim Klicken auf die Schaltfläche übersetzt werden</translation>
    </message>
    <message>
        <location filename="../Main.qml" line="25"/>
        <source>Translate text</source>
        <extracomment>Clicking this button translates the text fields</extracomment>
        <translation>Text übersetzen</translation>
    </message>
</context>
</TS>

Solution

  • Disclaimer: This answer may not be entirely accurate, as I am not fully up to date with both CMake and the Linguist system in Qt 6. What I am about to show you is based on my understanding of the Linguist system in Qt 5, with an attempt to extrapolate it to Qt 6 to provide you with a starting point.

    1. Create ts files for default and German languages:
    C:\Qt\Apps\BasicMultilingualApp>C:\Qt\Qt6.8.2\6.8.2\mingw_64\bin\lupdate . -ts myapp.ts -ts myapp_de.ts
    Scanning directory '.'...
    Updating 'myapp.ts'...
        Found 3 source text(s) (0 new and 3 already existing)
    Updating 'myapp_de.ts'...
        Found 3 source text(s) (0 new and 3 already existing)
    
    1. Edit myapp_de.ts supplying German strings (using C:\Qt\Qt6.8.2\6.8.2\mingw_64\bin\linguist.exe or equivalent)
    <?xml version="1.0" encoding="utf-8"?>
    <!DOCTYPE TS>
    <TS version="2.1" language="de_DE">
    <context>
        <name>Main</name>
        <message>
            <location filename="build/Desktop_Qt_6_8_2_MinGW_64_bit-Debug/BasicMultilingualApp/Main.qml" line="8"/>
            <location filename="Main.qml" line="8"/>
            <source>Basic Multilingual App</source>
            <translation>Grundlegende mehrsprachige App</translation>
        </message>
        <message>
            <location filename="build/Desktop_Qt_6_8_2_MinGW_64_bit-Debug/BasicMultilingualApp/Main.qml" line="19"/>
            <location filename="Main.qml" line="19"/>
            <source>This string must be translated on button click</source>
            <extracomment>Example string to translate</extracomment>
            <translation>Diese Zeichenfolge muss beim Klicken auf die Schaltfläche übersetzt werden.</translation>
        </message>
        <message>
            <location filename="build/Desktop_Qt_6_8_2_MinGW_64_bit-Debug/BasicMultilingualApp/Main.qml" line="25"/>
            <location filename="Main.qml" line="25"/>
            <source>Translate text</source>
            <extracomment>Clicking this button translates the text fields</extracomment>
            <translation>Text übersetzen</translation>
        </message>
    </context>
    </TS>
    
    1. Generate qm files
    C:\Qt\Apps\BasicMultilingualApp>C:\Qt\Qt6.8.2\6.8.2\mingw_64\bin\lrelease myapp.ts myapp_de.ts
    Updating 'myapp.qm'...
        Generated 0 translation(s) (0 finished and 0 unfinished)
        Ignored 3 untranslated source text(s)
    Updating 'myapp_de.qm'...
        Generated 3 translation(s) (3 finished and 0 unfinished)
    
    1. Add qm files to your cmake project
    qt_add_resources(appBasicMultilingualApp "translations"
        PREFIX "/"
        FILES myapp.qm myapp_de.qm
    )
    
    1. Create the following TranslateHelper class
    // translatehelper.h
    #ifndef TRANSLATEHELPER_H
    #define TRANSLATEHELPER_H
    
    #include <QObject>
    #include <QGuiApplication>
    #include <QQmlApplicationEngine>
    #include <QTranslator>
    #include <QDebug>
    
    class TranslateHelper : public QObject
    {
        Q_OBJECT
    public:
        QGuiApplication& m_app;
        QQmlApplicationEngine& m_engine;
    
        TranslateHelper(QGuiApplication& app, QQmlApplicationEngine& engine) :
            m_app(app),
            m_engine(engine)
        {
        }
    
        Q_INVOKABLE void setLanguage(const QString& name)
        {
            QLocale locale(name);
            QLocale::setDefault(locale);
            QTranslator translator;
            bool ok = translator.load(":/myapp_" + locale.name());
            qDebug() << Q_FUNC_INFO << "translator.load: " << ok;
            m_app.installTranslator(&translator);
            m_engine.retranslate();
        }
    };
    
    #endif // TRANSLATEHELPER_H
    
    1. Add an instance of the TranslateHelper to root context of your QML engine, e.g.
    // main.cpp
    #include <QGuiApplication>
    #include <QQmlApplicationEngine>
    #include <QQmlContext>
    #include "translatehelper.h"
    
    int main(int argc, char *argv[])
    {
        QGuiApplication app(argc, argv);
    
        QQmlApplicationEngine engine;
        TranslateHelper translateHelper(app, engine);
        engine.rootContext()->setContextProperty("translateHelper", &translateHelper);
        QObject::connect(
            &engine,
            &QQmlApplicationEngine::objectCreationFailed,
            &app,
            []() { QCoreApplication::exit(-1); },
            Qt::QueuedConnection);
        engine.loadFromModule("BasicMultilingualApp", "Main");
    
        return app.exec();
    }
    
    1. Update Main.qml to call TranslateHelper
    import QtQuick
    import QtQuick.Controls
    
    Window {
        width: 640
        height: 480
        visible: true
        title: qsTr("Basic Multilingual App") // We choose the app title to be language indifferent
    
        Component.onCompleted: Qt.uiLanguage = "en" // Set the UI language to English (so that we all start in the same place)
    
        Column {
            anchors.centerIn: parent
            spacing: 10
    
            Text {
                id: stringToTranslate
                //: Example string to translate
                text: qsTr("This string must be translated on button click")
            }
    
            Button {
                id: translateText
                //: Clicking this button translates the text fields
                text: qsTr("Translate text")
                onClicked: {
                    Qt.uiLanguage = Qt.uiLanguage === "en" ? "de" : "en" // If UI is English translate to German. Else translate to English
                    console.log(Qt.uiLanguage) // Print the UI language to console so we can monitor its changes
                    translateHelper.setLanguage(Qt.uiLanguage);
                }
            }
        }
    }
    

    Here's a screen recording of it running:

    QtTranslateDemo.gif

    Here's a GitHub link: https://github.com/stephenquan/QtBasicMultilingualApp