Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. How to add dynamic UI translation using QM files?
QtWS25 Last Chance

How to add dynamic UI translation using QM files?

Scheduled Pinned Locked Moved Solved General and Desktop
translationruntimedynamic loading
9 Posts 3 Posters 2.1k Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • R Offline
    R Offline
    Red Baron
    wrote on 27 May 2022, 08:43 last edited by
    #1

    I have trouble understanding how translation works and in particular in the context of dynamic loading during runtime. The code below is work in progress but should provide enough to start:

    CMakeLists.txt

    cmake_minimum_required(VERSION 3.13)
    
    project(ExampleGetStarted LANGUAGES CXX)
    
    set(CMAKE_CXX_STANDARD 17)
    set(CMAKE_CXX_STANDARD_REQUIRED ON)
    
    set(CMAKE_POSITION_INDEPENDENT_CODE OFF)
    set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
    set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
    set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
    
    set(CMAKE_AUTOMOC ON)
    set(CMAKE_AUTORCC ON)
    set(CMAKE_AUTOUIC ON)
    
    if(CMAKE_VERSION VERSION_LESS "3.7.0")
        set(CMAKE_INCLUDE_CURRENT_DIR ON)
    endif()
    
    find_package(Qt5
        COMPONENTS
            Widgets
            Xml
            XmlPatterns
            Concurrent
            UiTools
            LinguistTools
        REQUIRED
    )
    #find_package(Qt5XmlPatterns REQUIRED)
    
    # TODO https://stackoverflow.com/questions/51217734/how-to-add-qt-translations-to-cmake
    
    set(RESOURCES ${CMAKE_CURRENT_SOURCE_DIR}/resources/resources.qrc)
    set(TS_DIR ${CMAKE_CURRENT_SOURCE_DIR}/resources/translations)
    qt5_create_translation(TS_FILES ${CMAKE_CURRENT_SOURCE_DIR} ${TS_DIR}/en.ts ${TS_DIR}/de.ts)
    #qt5_add_translation(TS_FILES_FINISHED ${TS_DIR}/en.ts ${TS_DIR}/de.ts)
    # TODO Convert configure_file to custom post-build action. QM files are initially not available
    #configure_file(${QM_FILES} ${CMAKE_BINARY_DIR} COPYONLY)
    #qt5_add_translation(QM_FILES ${TS_FILES})
    
    add_executable(example_get_started
        example_get_started.cpp
        ${RESOURCES}
        ${TS_FILES}
    )
    target_link_libraries(example_get_started
        Qt5::Widgets
        Qt5::Xml Qt5::XmlPatterns
        Qt5::Concurrent
        Qt5::UiTools
        #Qt5::LinguistTools
    )
    # Copy translations to binary directory where executable can access thoses
    foreach(TS_FILE ${TS_FILES})
        add_custom_command(
            TARGET example_get_started POST_BUILD
            COMMAND ${CMAKE_COMMAND} -E copy
                    ${TS_FILE}
                    ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}
            COMMENT "Copied ${TS_FILE} to binary directory"
            )
    endforeach(TS_FILE)
    

    UI form

    <?xml version="1.0" encoding="UTF-8"?>
    <ui version="4.0">
     <class>widget_form</class>
     <widget class="QWidget" name="widget_form">
      <property name="geometry">
       <rect>
        <x>0</x>
        <y>0</y>
        <width>860</width>
        <height>527</height>
       </rect>
      </property>
      <property name="windowTitle">
       <string>Get Started</string>
      </property>
      <layout class="QVBoxLayout" name="verticalLayout_2">
       <item>
        <layout class="QHBoxLayout" name="horizontalLayout_2">
         <item>
          <widget class="QPushButton" name="btn_import_file">
           <property name="sizePolicy">
            <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
             <horstretch>0</horstretch>
             <verstretch>0</verstretch>
            </sizepolicy>
           </property>
           <property name="styleSheet">
            <string notr="true">image: url(:/icons/files/import);</string>
           </property>
           <property name="text">
            <string/>
           </property>
          </widget>
         </item>
         <item>
          <widget class="QPushButton" name="btn_check_file">
           <property name="sizePolicy">
            <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
             <horstretch>0</horstretch>
             <verstretch>0</verstretch>
            </sizepolicy>
           </property>
           <property name="styleSheet">
            <string notr="true">image: url(:/icons/files/check);</string>
           </property>
           <property name="text">
            <string/>
           </property>
          </widget>
         </item>
         <item>
          <widget class="QPushButton" name="btn_delete_file">
           <property name="sizePolicy">
            <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
             <horstretch>0</horstretch>
             <verstretch>0</verstretch>
            </sizepolicy>
           </property>
           <property name="styleSheet">
            <string notr="true">image: url(:/icons/files/delete);</string>
           </property>
           <property name="text">
            <string/>
           </property>
          </widget>
         </item>
         <item>
          <widget class="QPushButton" name="btn_add_file">
           <property name="sizePolicy">
            <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
             <horstretch>0</horstretch>
             <verstretch>0</verstretch>
            </sizepolicy>
           </property>
           <property name="styleSheet">
            <string notr="true">image: url(:/icons/files/add);</string>
           </property>
           <property name="text">
            <string/>
           </property>
          </widget>
         </item>
         <item>
          <widget class="QComboBox" name="cb_change_lang">
           <property name="sizePolicy">
            <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
             <horstretch>0</horstretch>
             <verstretch>0</verstretch>
            </sizepolicy>
           </property>
           <property name="styleSheet">
            <string notr="true"/>
           </property>
          </widget>
         </item>
        </layout>
       </item>
       <item>
        <layout class="QHBoxLayout" name="horizontalLayout">
         <item>
          <widget class="QPlainTextEdit" name="pte_file_view">
           <property name="sizePolicy">
            <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
             <horstretch>1</horstretch>
             <verstretch>1</verstretch>
            </sizepolicy>
           </property>
          </widget>
         </item>
         <item>
          <widget class="Line" name="vline_right">
           <property name="orientation">
            <enum>Qt::Vertical</enum>
           </property>
          </widget>
         </item>
         <item>
          <layout class="QVBoxLayout" name="verticalLayout">
           <item>
            <widget class="QPushButton" name="btn_csv">
             <property name="sizePolicy">
              <sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
               <horstretch>0</horstretch>
               <verstretch>0</verstretch>
              </sizepolicy>
             </property>
             <property name="styleSheet">
              <string notr="true">image: url(:/icons/files/csv);</string>
             </property>
             <property name="text">
              <string/>
             </property>
            </widget>
           </item>
           <item>
            <widget class="QPushButton" name="btn_code">
             <property name="sizePolicy">
              <sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
               <horstretch>0</horstretch>
               <verstretch>0</verstretch>
              </sizepolicy>
             </property>
             <property name="styleSheet">
              <string notr="true">image: url(:/icons/files/code);</string>
             </property>
             <property name="text">
              <string/>
             </property>
            </widget>
           </item>
           <item>
            <widget class="QPushButton" name="btn_json">
             <property name="sizePolicy">
              <sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
               <horstretch>0</horstretch>
               <verstretch>0</verstretch>
              </sizepolicy>
             </property>
             <property name="styleSheet">
              <string notr="true">image: url(:/icons/files/json);</string>
             </property>
             <property name="text">
              <string/>
             </property>
            </widget>
           </item>
           <item>
            <widget class="QPushButton" name="btn_xml">
             <property name="sizePolicy">
              <sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
               <horstretch>0</horstretch>
               <verstretch>0</verstretch>
              </sizepolicy>
             </property>
             <property name="styleSheet">
              <string notr="true">image: url(:/icons/files/xml);</string>
             </property>
             <property name="text">
              <string/>
             </property>
            </widget>
           </item>
          </layout>
         </item>
        </layout>
       </item>
      </layout>
     </widget>
     <resources>
      <include location="../resources.qrc"/>
     </resources>
     <connections/>
    </ui>
    

    QRC file

    <RCC>
      <qresource prefix="icons/files">
        <file alias="xml">icons/file-formats/icons8-xml-96.png</file>
        <file alias="json">icons/file-formats/icons8-json-96.png</file>
        <file alias="csv">icons/file-formats/icons8-csv-96.png</file>
        <file alias="code">icons/file-formats/icons8-code-96.png</file>
      </qresource>
      <qresource prefix="icons/files">
        <file alias="import">icons/file-ops/icons8-import-96.png</file>
        <file alias="delete">icons/file-ops/icons8-delete-file-96.png</file>
        <file alias="check">icons/file-ops/icons8-check-file-96.png</file>
        <file alias="add">icons/file-ops/icons8-add-file-96.png</file>
      </qresource>
      <qresource prefix="icons/flags">
        <file alias="fr">icons/flags/icons8-france-80.png</file>
        <file alias="de">icons/flags/icons8-germany-80.png</file>
        <file alias="gb">icons/flags/icons8-great-britain-80.png</file>
        <file alias="ru">icons/flags/icons8-russian-federation-80.png</file>
      </qresource>
      <qresource prefix="forms">
         <file alias="main">ui/widget.ui</file>
      </qresource>
    </RCC>
    

    CPP file

    #include <vector>
    #include <utility>
    #include <iostream>
    
    #include <QApplication>
    #include <QtWidgets>
    #include <QtUiTools>
    #include <QFile>
    #include <QTranslator>
    
    QScopedPointer<QTranslator> translator(new QTranslator());
    
    static QWidget *loadUiFile(QWidget *parent)
    {
        QFile file(":/forms/main");
        file.open(QIODevice::ReadOnly);
    
        QUiLoader loader;
        return loader.load(&file, parent);
    }
    
    static void retranslate(QString lang_code)
    {
        std::cout << "Translating to " << lang_code.toStdString() << std::endl;
        std::cout << "Looking into directory \"" << QApplication::applicationDirPath().toStdString() << "\"" << std::endl;
        if (lang_code == QString("fr"))
        {
            if (translator->load("fr", QApplication::applicationDirPath()))
            {
                QApplication::instance()->installTranslator(translator.data());
                std::cout << "Switched to " << lang_code.toStdString() << std::endl;
            }
            else
                std::cout << "Unable to load translation for " << lang_code.toStdString() << std::endl;
        }
        else if (lang_code == QString("de"))
        {
            if (translator->load("de", QApplication::applicationDirPath()))
            {
                QApplication::instance()->installTranslator(translator.data());
                std::cout << "Switched to " << lang_code.toStdString() << std::endl;
            }
            else
                std::cout << "Unable to load translation for " << lang_code.toStdString() << std::endl;
        }
        else if (lang_code == QString("ru"))
        {
            if (translator->load("ru", QApplication::applicationDirPath()))
            {
                QApplication::instance()->installTranslator(translator.data());
                std::cout << "Switched to " << lang_code.toStdString() << std::endl;
            }
            else
                std::cout << "Unable to load translation for " << lang_code.toStdString() << std::endl;
        }
        else
        {
            QApplication::instance()->removeTranslator(translator.data());
        }
    }
    
    int main(int argc, char *argv[])
    {
        QApplication app(argc, argv);
    
        // Translations need to be created before widgets they affect
        /*
    if (!translator->load("en_GB.qm"))
        std::cout << "Unable to load translation file \"en_GB\"" << std::endl;
    app.installTranslator(translator.data());
    if (!translator->load("de_DE.qm"))
        std::cout << "Unable to load translation file \"de_DE\"" << std::endl;
    app.installTranslator(translator.data());
    std::cout << translator->language().toStdString() << std::endl;
    */
    
        QWidget *widget = loadUiFile(nullptr);
        widget->findChild<QPushButton *>("btn_add_file")->setToolTip(QObject::tr("Create a new file"));
        widget->findChild<QPushButton *>("btn_check_file")->setToolTip(QObject::tr("Check the opened file's contents for errors"));
        widget->findChild<QPushButton *>("btn_delete_file")->setToolTip(QObject::tr("Deletes the opened file's contents"));
        widget->findChild<QPushButton *>("btn_import_file")->setToolTip(QObject::tr("Import existing file"));
        QComboBox *cb_change_lang = widget->findChild<QComboBox *>("cb_change_lang");
        std::vector<std::pair<QString, QIcon>> langs = {
            std::pair<QString, QIcon>(QObject::tr("English (en)"), QIcon(":/icons/flags/gb")),
            std::pair<QString, QIcon>(QObject::tr("Deutsch (de)"), QIcon(":/icons/flags/de")),
            std::pair<QString, QIcon>(QObject::tr("Français (fr)"), QIcon(":/icons/flags/fr")),
            std::pair<QString, QIcon>(QObject::tr("Руский (ru)"), QIcon(":/icons/flags/ru"))};
        auto lang_idx = 0;
        for (auto lang : langs)
        {
            cb_change_lang->insertItem(lang_idx, lang.first);
            cb_change_lang->setItemIcon(lang_idx, lang.second);
            lang_idx++;
        }
    
        QRegularExpression regex("\\(([^()]+)\\)");
        QObject::connect(cb_change_lang,
                         qOverload<int>(&QComboBox::currentIndexChanged),
                         [=](int idx)
                         {
                             retranslate(regex.match(langs.at(cb_change_lang->currentIndex()).first).captured(1));
                         });
        std::cout << regex.match(langs.at(cb_change_lang->currentIndex()).first).captured(1).toStdString() << std::endl;
    
        widget->show();
        return app.exec();
    }
    

    afa72b39-f119-4072-b1a8-d893b1ef1411-image.png

    Basically I would like to (during runtime) translate the UI using a provided QM file by simply choosing a different item in the combo box.

    I also tried adding a separate translator for each language and for any given combo box item I would remove all translators and install the new one. In addition I also tried loading all QM files in the respective translators before I create my widget, e.g:

    ...
    
    int main(int argc, char *argv[])
    {
        QApplication app(argc, argv);
    
        // Translations need to be created before widgets they affect
        /*
    if (!translator->load("en_GB.qm"))
        std::cout << "Unable to load translation file \"en_GB\"" << std::endl;
    app.installTranslator(translator.data());
    if (!translator->load("de_DE.qm"))
        std::cout << "Unable to load translation file \"de_DE\"" << std::endl;
    app.installTranslator(translator.data());
    std::cout << translator->language().toStdString() << std::endl;
    */
        QLocale en_any(QLocale::Language::AnyLanguage, QLocale::Country::AnyCountry);
        QLocale de(QLocale::Language::German, QLocale::Country::Germany);
        QLocale fr(QLocale::Language::French, QLocale::Country::France);
        QLocale ru(QLocale::Language::Russian, QLocale::Country::RussianFederation);
        translatorEn->load(en_any, QString("en.qm"), QString(), QApplication::applicationDirPath());
        std::cout << "Loaded translation for " << translatorEn->language().toStdString() << std::endl;
        translatorDe->load(de, QString("de.qm"), QString(), QApplication::applicationDirPath());
        std::cout << "Loaded translation for " << translatorDe->language().toStdString() << std::endl;
        translatorFr->load(fr, QString("fr.qm"), QString(), QApplication::applicationDirPath());
        std::cout << "Loaded translation for " << translatorFr->language().toStdString() << std::endl;
        translatorRu->load(ru, QString("ru.qm"), QString(), QApplication::applicationDirPath());
        std::cout << "Loaded translation for " << translatorRu->language().toStdString() << std::endl;
        QLocale::setDefault(de);
    
        QWidget *widget = loadUiFile(nullptr);
    
      ...
    
      return app.exec();
    }
    
    1 Reply Last reply
    0
    • Christian EhrlicherC Online
      Christian EhrlicherC Online
      Christian Ehrlicher
      Lifetime Qt Champion
      wrote on 27 May 2022, 09:28 last edited by
      #2

      So what's the actual problem?
      Create a combobox, on selection of the corresponding item load the qm file with QTranslator and install it. Then wait for the LanguageChange event and do the re-translation of your widget as explained here.

      Qt Online Installer direct download: https://download.qt.io/official_releases/online_installers/
      Visit the Qt Academy at https://academy.qt.io/catalog

      R 1 Reply Last reply 28 May 2022, 17:19
      2
      • Christian EhrlicherC Christian Ehrlicher
        27 May 2022, 09:28

        So what's the actual problem?
        Create a combobox, on selection of the corresponding item load the qm file with QTranslator and install it. Then wait for the LanguageChange event and do the re-translation of your widget as explained here.

        R Offline
        R Offline
        Red Baron
        wrote on 28 May 2022, 17:19 last edited by
        #3

        @Christian-Ehrlicher I saw that article. However I don't get the part with the change event. Do I have to setText() of every single widget in my UI manually? Especially in my case where I'm using an UI file and adding only bare minimum logic to it this looks too much. I'm guessing in my case I just need to install an event filter where I do that but I was thinking that the loading of a QM and installation of a new translator would automatically update my UI.

        1 Reply Last reply
        0
        • Christian EhrlicherC Online
          Christian EhrlicherC Online
          Christian Ehrlicher
          Lifetime Qt Champion
          wrote on 29 May 2022, 17:00 last edited by
          #4

          If you use a ui file then you can call retranslateUi() from the generated class. The rest has to be done by yourself since noone can know what to do except you.

          Qt Online Installer direct download: https://download.qt.io/official_releases/online_installers/
          Visit the Qt Academy at https://academy.qt.io/catalog

          1 Reply Last reply
          0
          • SGaistS Offline
            SGaistS Offline
            SGaist
            Lifetime Qt Champion
            wrote on 29 May 2022, 19:51 last edited by
            #5

            Hi,

            If memory serves well, since you are using Designer, calling ui->retranslateUi(this); in reaction to the language change event should do the work.

            Interested in AI ? www.idiap.ch
            Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

            R 1 Reply Last reply 30 May 2022, 06:32
            0
            • SGaistS SGaist
              29 May 2022, 19:51

              Hi,

              If memory serves well, since you are using Designer, calling ui->retranslateUi(this); in reaction to the language change event should do the work.

              R Offline
              R Offline
              Red Baron
              wrote on 30 May 2022, 06:32 last edited by
              #6

              @SGaist @Christian-Ehrlicher I also found multiple sources stating that however I don't have access to this function. Perhaps it's because I'm using QUiLoader?

              Christian EhrlicherC 1 Reply Last reply 30 May 2022, 07:13
              0
              • R Red Baron
                30 May 2022, 06:32

                @SGaist @Christian-Ehrlicher I also found multiple sources stating that however I don't have access to this function. Perhaps it's because I'm using QUiLoader?

                Christian EhrlicherC Online
                Christian EhrlicherC Online
                Christian Ehrlicher
                Lifetime Qt Champion
                wrote on 30 May 2022, 07:13 last edited by
                #7

                See also https://doc.qt.io/qt-5/quiloader.html#setLanguageChangeEnabled

                Qt Online Installer direct download: https://download.qt.io/official_releases/online_installers/
                Visit the Qt Academy at https://academy.qt.io/catalog

                1 Reply Last reply
                0
                • R Offline
                  R Offline
                  Red Baron
                  wrote on 31 May 2022, 07:39 last edited by Red Baron
                  #8

                  @Christian-Ehrlicher said in How to add dynamic UI translation using QM files?:

                  retranslateUi

                  Ok, so I removed the QUiLoader and decided to go with the way I'm more familiar with in regards to using a UI form in source code. I've adapter my existing code to look like this:

                  QScopedPointer<QTranslator> translator(new QTranslator());
                  
                  class MainWidget : public QWidget
                  {
                      Q_OBJECT
                  
                      Ui::widget_form *ui;
                      const std::vector<std::pair<QString, QIcon>> langs = {
                          std::pair<QString, QIcon>(QObject::tr("English (en)"), QIcon(":/icons/flags/gb")),
                          std::pair<QString, QIcon>(QObject::tr("Deutsch (de)"), QIcon(":/icons/flags/de")),
                          std::pair<QString, QIcon>(QObject::tr("Français (fr)"), QIcon(":/icons/flags/fr")),
                          std::pair<QString, QIcon>(QObject::tr("Руский (ru)"), QIcon(":/icons/flags/ru"))
                      };
                      QString lang_curr = "en";
                  
                  public:
                      MainWidget(QWidget* parent=nullptr)
                          : QWidget(parent),
                            ui(new Ui::widget_form)
                      {
                          ui->setupUi(this);
                  
                          ui->btn_add_file->setToolTip(QObject::tr("Create a new file"));
                          ui->btn_check_file->setToolTip(QObject::tr("Check the opened file's contents for errors"));
                          ui->btn_delete_file->setToolTip(QObject::tr("Deletes the opened file's contents"));
                          ui->btn_import_file->setToolTip(QObject::tr("Import existing file"));
                  
                          auto lang_idx = 0;
                          for (auto lang : langs)
                          {
                              ui->cb_change_lang->insertItem(lang_idx, lang.first);
                              ui->cb_change_lang->setItemIcon(lang_idx, lang.second);
                              lang_idx++;
                          }
                  
                          QRegularExpression regex("\\(([^()]+)\\)");
                          QObject::connect(ui->cb_change_lang,
                                           qOverload<int>(&QComboBox::currentIndexChanged),
                                           [=](int idx)
                                           {
                                               retranslate(regex.match(langs.at(ui->cb_change_lang->currentIndex()).first).captured(1));
                                           });
                      }
                  
                      ~MainWidget()
                      {
                          delete ui;
                      }
                  
                  protected:
                      void changeEvent(QEvent *event) override
                      {
                          if (event->type() == QEvent::LanguageChange)
                          {
                              std::cout << "Event filter for language" << std::endl;
                  
                              ui->retranslateUi(this);
                          }
                  
                          return QWidget::changeEvent(event);
                      }
                  private:
                      void retranslate(QString lang_code)
                      {
                          std::cout << "Translating to " << lang_code.toStdString() << std::endl;
                          std::cout << "Looking into directory \"" << QApplication::applicationDirPath().toStdString() << "\"" << std::endl;
                          if (lang_code == QString("fr"))
                          {
                              if (translator->load("fr", QApplication::applicationDirPath()))
                              {
                                  QApplication::instance()->removeTranslator(translator.data());
                                  QApplication::instance()->installTranslator(translator.data());
                                  lang_curr = lang_code;
                              }
                              else
                                  std::cout << "Unable to load translation for " << lang_code.toStdString() << std::endl;
                          }
                          else if (lang_code == QString("de"))
                          {
                              if (translator->load("de", QApplication::applicationDirPath()))
                              {
                                  QApplication::instance()->removeTranslator(translator.data());
                                  QApplication::instance()->installTranslator(translator.data());
                                  lang_curr = lang_code;
                              }
                              else
                                  std::cout << "Unable to load translation for " << lang_code.toStdString() << std::endl;
                          }
                          else if (lang_code == QString("ru"))
                          {
                              if (translator->load("ru", QApplication::applicationDirPath()))
                              {
                                  QApplication::instance()->removeTranslator(translator.data());
                                  QApplication::instance()->installTranslator(translator.data());
                                  lang_curr = lang_code;
                              }
                              else
                                  std::cout << "Unable to load translation for " << lang_code.toStdString() << std::endl;
                          }
                          else
                          {
                              QApplication::instance()->removeTranslator(translator.data());
                              QApplication::instance()->installTranslator(translator.data());
                              lang_curr = lang_code;
                          }
                      }
                  };
                  
                  int main(int argc, char *argv[])
                  {
                      QApplication app(argc, argv);
                      MainWidget mw;
                      mw.show();
                      return app.exec();
                  }
                  

                  And here is an example for a TS file (in this case German):

                  <?xml version="1.0" encoding="utf-8"?>
                  <!DOCTYPE TS>
                  <TS version="2.1" language="de">
                  <context>
                      <name>QObject</name>
                      <message>
                          <location filename="../../example_get_started.cpp" line="111"/>
                          <source>English (en)</source>
                          <translation></translation>
                      </message>
                      <message>
                          <location filename="../../example_get_started.cpp" line="112"/>
                          <source>Deutsch (de)</source>
                          <translation></translation>
                      </message>
                      <message>
                          <location filename="../../example_get_started.cpp" line="113"/>
                          <source>Français (fr)</source>
                          <translation></translation>
                      </message>
                      <message>
                          <location filename="../../example_get_started.cpp" line="114"/>
                          <source>Руский (ru)</source>
                          <translation></translation>
                      </message>
                      <message>
                          <source>This is a test</source>
                          <translation type="vanished">Das ist ein Test</translation>
                      </message>
                      <message>
                          <location filename="../../example_get_started.cpp" line="125"/>
                          <source>Create a new file</source>
                          <translation>Neues Datei erstellen</translation>
                      </message>
                      <message>
                          <location filename="../../example_get_started.cpp" line="126"/>
                          <source>Check the opened file&apos;s contents for errors</source>
                          <translation>Geöffnetes Datei auf Fehler überprüfen</translation>
                      </message>
                      <message>
                          <location filename="../../example_get_started.cpp" line="127"/>
                          <source>Delete the opened file&apos;s contents</source>
                          <translation>Inhalt des geöffnetes Datei löschen</translation>
                      </message>
                      <message>
                          <source>Deletes the opened file&apos;s contents</source>
                          <translation type="vanished">Inhalt des geöffnetes Datei löschen</translation>
                      </message>
                      <message>
                          <location filename="../../example_get_started.cpp" line="128"/>
                          <source>Import existing file</source>
                          <translation>Existierendes Datei hinzufügen</translation>
                      </message>
                  </context>
                  <context>
                      <name>widget_form</name>
                      <message>
                          <location filename="../ui/widget.ui" line="14"/>
                          <source>Get Started</source>
                          <translation></translation>
                      </message>
                  </context>
                  </TS>
                  
                  

                  I'm not sure if the loading is in the right spot. Yet again I don't get the result I'm expecting even when using retranslateUi().

                  I noticed the following inside the retranslateUi() function of the generated header file:

                  void retranslateUi(QWidget *widget_form)
                      {
                          widget_form->setWindowTitle(QCoreApplication::translate("widget_form", "Get Started", nullptr));
                          btn_import_file->setText(QString());
                          btn_check_file->setText(QString());
                          btn_delete_file->setText(QString());
                          btn_add_file->setText(QString());
                          btn_csv->setText(QString());
                          btn_code->setText(QString());
                          btn_json->setText(QString());
                          btn_xml->setText(QString());
                      } // retranslateUi
                  

                  What I decided to do is to enter the tooltip of each button also in the UI form. A little bit redundant but worth a shot. I added the missing translations in Linguist (for the UI form) and then reconfigured and rebuilt the project. The header now looks like this:

                      void retranslateUi(QWidget *widget_form)
                      {
                          widget_form->setWindowTitle(QCoreApplication::translate("widget_form", "Get Started", nullptr));
                  #if QT_CONFIG(tooltip)
                          btn_import_file->setToolTip(QCoreApplication::translate("widget_form", "Create a new file", nullptr));
                  #endif // QT_CONFIG(tooltip)
                          btn_import_file->setText(QString());
                  #if QT_CONFIG(tooltip)
                          btn_check_file->setToolTip(QCoreApplication::translate("widget_form", "Check the opened file's contents for errors", nullptr));
                  #endif // QT_CONFIG(tooltip)
                          btn_check_file->setText(QString());
                  #if QT_CONFIG(tooltip)
                          btn_delete_file->setToolTip(QCoreApplication::translate("widget_form", "Delete the opened file's contents", nullptr));
                  #endif // QT_CONFIG(tooltip)
                          btn_delete_file->setText(QString());
                  #if QT_CONFIG(tooltip)
                          btn_add_file->setToolTip(QCoreApplication::translate("widget_form", "Import existing file", nullptr));
                  #endif // QT_CONFIG(tooltip)
                          btn_add_file->setText(QString());
                          btn_csv->setText(QString());
                          btn_code->setText(QString());
                          btn_json->setText(QString());
                          btn_xml->setText(QString());
                      } // retranslateUi
                  

                  and the TS file looks like this:

                  <?xml version="1.0" encoding="utf-8"?>
                  <!DOCTYPE TS>
                  <TS version="2.1" language="de">
                  <context>
                      <name>QObject</name>
                      <message>
                          <location filename="../../example_get_started.cpp" line="111"/>
                          <source>English (en)</source>
                          <translation></translation>
                      </message>
                      <message>
                          <location filename="../../example_get_started.cpp" line="112"/>
                          <source>Deutsch (de)</source>
                          <translation></translation>
                      </message>
                      <message>
                          <location filename="../../example_get_started.cpp" line="113"/>
                          <source>Français (fr)</source>
                          <translation></translation>
                      </message>
                      <message>
                          <location filename="../../example_get_started.cpp" line="114"/>
                          <source>Руский (ru)</source>
                          <translation></translation>
                      </message>
                      <message>
                          <source>This is a test</source>
                          <translation type="vanished">Das ist ein Test</translation>
                      </message>
                      <message>
                          <location filename="../../example_get_started.cpp" line="125"/>
                          <source>Create a new file</source>
                          <translation>Neues Datei erstellen</translation>
                      </message>
                      <message>
                          <location filename="../../example_get_started.cpp" line="126"/>
                          <source>Check the opened file&apos;s contents for errors</source>
                          <translation>Geöffnetes Datei auf Fehler überprüfen</translation>
                      </message>
                      <message>
                          <location filename="../../example_get_started.cpp" line="127"/>
                          <source>Delete the opened file&apos;s contents</source>
                          <translation>Inhalt des geöffnetes Datei löschen</translation>
                      </message>
                      <message>
                          <source>Deletes the opened file&apos;s contents</source>
                          <translation type="vanished">Inhalt des geöffnetes Datei löschen</translation>
                      </message>
                      <message>
                          <location filename="../../example_get_started.cpp" line="128"/>
                          <source>Import existing file</source>
                          <translation>Existierendes Datei hinzufügen</translation>
                      </message>
                  </context>
                  <context>
                      <name>widget_form</name>
                      <message>
                          <location filename="../ui/widget.ui" line="14"/>
                          <source>Get Started</source>
                          <translation></translation>
                      </message>
                      <message>
                          <location filename="../ui/widget.ui" line="28"/>
                          <source>Create a new file</source>
                          <translation>Neues Datei erstellen</translation>
                      </message>
                      <message>
                          <location filename="../ui/widget.ui" line="47"/>
                          <source>Check the opened file&apos;s contents for errors</source>
                          <translation>Geöffnetes Datei auf Fehler überprüfen</translation>
                      </message>
                      <message>
                          <location filename="../ui/widget.ui" line="66"/>
                          <source>Delete the opened file&apos;s contents</source>
                          <translation>Löscht des geöffneten Dateis löschen</translation>
                      </message>
                      <message>
                          <location filename="../ui/widget.ui" line="85"/>
                          <source>Import existing file</source>
                          <translation>Existierendes Datei hinzufügen</translation>
                      </message>
                      <message>
                          <source>This is a test</source>
                          <translation type="vanished">Das ist ein Test</translation>
                      </message>
                  </context>
                  </TS>
                  
                  

                  It's working fine now. I do wonder though if this is me doing something wrong or is it indeed intended to type all the text twice. If I can remove the e.g. tooltips in my source code that would be perfect since (correct me if I'm assuming wrong) a translator will normally have no access to the source code (since they are not a developer, duh :D). Note that from the very beginning I have marked all tooltips as translatable in my UI fomr file. However I cannot find anything resembling such setting inside the XML file. For example one of my buttons with a translatable tooltip looks like this in XML form:

                  <widget class="QPushButton" name="btn_delete_file">
                         <property name="sizePolicy">
                          <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
                           <horstretch>0</horstretch>
                           <verstretch>0</verstretch>
                          </sizepolicy>
                         </property>
                         <property name="toolTip">
                          <string>Delete the opened file's contents</string>
                         </property>
                         <property name="styleSheet">
                          <string notr="true">image: url(:/icons/files/delete);</string>
                         </property>
                         <property name="text">
                          <string/>
                         </property>
                        </widget>
                  

                  but as you can see in the Designer translation is on:

                  5a016c57-9a43-488c-811d-49ed38f683f2-image.png

                  Is this a bug?


                  UPDATE: Apparently a property notr is added to a text component of a widget if not translatable. If translatable the property is just not include (apparently under the hood this is the equivalent of notr="false").

                  1 Reply Last reply
                  0
                  • R Offline
                    R Offline
                    Red Baron
                    wrote on 1 Jun 2022, 07:38 last edited by
                    #9

                    Ok, so I removed all the tr() entries from my code and things are working. I guess the UI form file was the key to solving the issue. Thanks for the help, guys!

                    1 Reply Last reply
                    0

                    9/9

                    1 Jun 2022, 07:38

                    • Login

                    • Login or register to search.
                    9 out of 9
                    • First post
                      9/9
                      Last post
                    0
                    • Categories
                    • Recent
                    • Tags
                    • Popular
                    • Users
                    • Groups
                    • Search
                    • Get Qt Extensions
                    • Unsolved