如何在 Qt 中使用 UI 表单在运行时进行动态翻译?

How to do dynamic translation during runtime using UI forms in Qt?

我正在寻找一个如何动态翻译的相对最小的示例。我正在创建一个关于使用 Qt 资源系统以及 UI 和 TS 文件的教程。为此,我创建了一个基本的 UI 表单:

<?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 文件中:

<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>

我用以下方法管理我的项目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 

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 表单加载了以下代码(进行中):

#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();
}

小部件如下所示:

我想保持我的小部件的创建(UI 文件以 C++ 加载并添加了一些额外的功能)。我的 QTranslator 是全局的原因是因为这个加上翻译器需要在 QApplication 的实例存活时存活。

它应该工作的方式(但目前不是)是当QComboBox(屏幕截图的右上角)中当前选定的项目发生变化时,每个按钮的工具提示都会被翻译。我对 Qt 的翻译工具很陌生,在这方面的文档并不是很出色(个人意见)。

在我开始进行动态翻译之前,我在我的 main() 中加载了一个 QM 文件并且它起作用了 - 工具提示使用 QM 文件的语言。仅在添加新功能后我没有看到任何变化。

找了一圈,也在Qt官方论坛问了下。我得到的一些提示让我修改了我的代码:

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_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_AUTOUIC ON)

find_package(Qt5
    COMPONENTS
        Core
        Widgets
        Xml
        XmlPatterns
        Concurrent
        UiTools
        LinguistTools
    REQUIRED
)

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 ${TS_DIR}/fr.ts ${TS_DIR}/ru.ts)


add_executable(example_get_started
    example_get_started.cpp
    ${RESOURCES}
    ${TS_FILES}
)
target_link_libraries(example_get_started
    Qt5::Core
    Qt5::Widgets
    Qt5::Xml Qt5::XmlPatterns
    Qt5::Concurrent
    Qt5::UiTools
)
# Copy translations to binary directory where executable can access these
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)

CPP 文件

#include <vector>
#include <utility>

#include <QApplication>
#include <QtWidgets>
#include <QtUiTools>
#include <QFile>
#include <QTranslator>
#include <QDebug>

// Needs to be included so that the header file is generated. Note that if the UI file is in a subdirectory, that also needs to be specified here
#include "resources/ui/ui_widget.h"

QScopedPointer<QTranslator> translator(new QTranslator());

class MainWidget : public QWidget
{
    Q_OBJECT

    Ui::widget_form *ui;
    QString lang_curr = "en";

public:
    MainWidget(QWidget* parent=nullptr)
        : QWidget(parent),
          ui(new Ui::widget_form)
    {
        ui->setupUi(this);

        QRegularExpression regex("\(([^()]+)\)");
        QObject::connect(ui->cb_change_lang,
                         qOverload<int>(&QComboBox::currentIndexChanged),
                         [=](int idx)
                         {
                             retranslate(regex.match(ui->cb_change_lang->currentText()).captured(1));
                         });
    }

    ~MainWidget()
    {
        delete ui;
    }
protected:
    void changeEvent(QEvent *event) override
    {
        if (event->type() == QEvent::LanguageChange)
        {
            ui->retranslateUi(this);
        }

        return QWidget::changeEvent(event);
    }
private:
    void retranslate(QString lang_code)
    {
        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
                qDebug() << "Unable to load translation for " << lang_code;
        }
        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
                qDebug() << "Unable to load translation for " << lang_code;
        }
        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
                qDebug() << "Unable to load translation for " << lang_code;
        }
        else
        {
            if (translator->load("en", QApplication::applicationDirPath()))
            {
                QApplication::instance()->removeTranslator(translator.data());
                QApplication::instance()->installTranslator(translator.data());
                lang_curr = lang_code;
            }
            else
                qDebug() << "Unable to load translation for " << lang_code;
        }
    }
};

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

    MainWidget mw;
    mw.show();
    return app.exec();
}

#include "example_get_started.moc"

XML UI 表格文件

<?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 notr="true">Get Started</string>
  </property>
  <layout class="QVBoxLayout" name="verticalLayout_2">
   <item>
    <layout class="QHBoxLayout" name="horizontalLayout_2">
     <item>
      <widget class="QPushButton" name="btn_create_file">
       <property name="sizePolicy">
        <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
         <horstretch>0</horstretch>
         <verstretch>0</verstretch>
        </sizepolicy>
       </property>
       <property name="toolTip">
        <string>Create a new file</string>
       </property>
       <property name="styleSheet">
        <string notr="true">image: url(:/icons/files/create);</string>
       </property>
       <property name="text">
        <string/>
       </property>
      </widget>
     </item>
     <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="toolTip">
        <string>Import existing file</string>
       </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_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>
     </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="toolTip">
        <string>Check the opened file's contents for errors</string>
       </property>
       <property name="styleSheet">
        <string notr="true">image: url(:/icons/files/check);</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>
       <item>
        <property name="text">
         <string>English (en)</string>
        </property>
        <property name="icon">
         <iconset resource="../resources.qrc">
          <normaloff>:/icons/flags/gb</normaloff>:/icons/flags/gb</iconset>
        </property>
       </item>
       <item>
        <property name="text">
         <string>Deutsch (de)</string>
        </property>
        <property name="icon">
         <iconset resource="../resources.qrc">
          <normaloff>:/icons/flags/de</normaloff>:/icons/flags/de</iconset>
        </property>
       </item>
       <item>
        <property name="text">
         <string>Français (fr)</string>
        </property>
        <property name="icon">
         <iconset resource="../resources.qrc">
          <normaloff>:/icons/flags/fr</normaloff>:/icons/flags/fr</iconset>
        </property>
       </item>
       <item>
        <property name="text">
         <string>Руский (ru)</string>
        </property>
        <property name="icon">
         <iconset resource="../resources.qrc">
          <normaloff>:/icons/flags/ru</normaloff>:/icons/flags/ru</iconset>
        </property>
       </item>
      </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="toolTip">
          <string>Create CSV file</string>
         </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="toolTip">
          <string>Create script file</string>
         </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="toolTip">
          <string>Create JSON file</string>
         </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="toolTip">
          <string>Create XML file</string>
         </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>

我做的是:

  • QUiLoader替换为加载UI表单文件的标准方式,我对此比较有经验(我对UI的经验很少一般形式 :D)

  • 将我在源代码中设置的所有工具提示移动到 UI 表单文件 - 更多或下一点

  • 添加 changeEvent() 到使用 UI 表单文件的源代码中的小部件 - 通过处理 QEvent::LanguageChanged 并使用这种方式加载 UI 可以访问 retranslateUi() 函数。这个函数是在生成代表UI表单文件的header(这里是ui_widget.h)时自动生成的。该函数可以自动重新翻译所有标记为可翻译的字符串(在 Qt Designer 中,这是 translateable 复选框)。最初(在上一点之前)我看到 retranslateUi() 不包含我的任何工具提示。显然,将源代码中的字符串与 UI 形式的字符串混合并不像我想象的那样有效。通过将工具提示移动到我从初始

    获得的 UI 表单文件
     void retranslateUi(QWidget *widget_form)
     {
         btn_create_file->setText(QString());
         btn_import_file->setText(QString());
         btn_delete_file->setText(QString());
         btn_check_file->setText(QString());
         cb_change_lang->setItemText(0, QCoreApplication::translate("widget_form", "English (en)", nullptr));
         cb_change_lang->setItemText(1, QCoreApplication::translate("widget_form", "Deutsch (de)", nullptr));
         cb_change_lang->setItemText(2, QCoreApplication::translate("widget_form", "Fran37ais (fr)", nullptr));
         cb_change_lang->setItemText(3, QCoreApplication::translate("widget_form", "001311020001 (ru)", nullptr));
         btn_csv->setText(QString());
         btn_code->setText(QString());
         btn_json->setText(QString());
         btn_xml->setText(QString());
         (void)widget_form;
     } // retranslateUi
    

     void retranslateUi(QWidget *widget_form)
     {
     #if QT_CONFIG(tooltip)
         btn_create_file->setToolTip(QCoreApplication::translate("widget_form", "Create a new file", nullptr));
     #endif // QT_CONFIG(tooltip)
         btn_create_file->setText(QString());
     #if QT_CONFIG(tooltip)
         btn_import_file->setToolTip(QCoreApplication::translate("widget_form", "Import existing file", nullptr));
     #endif // QT_CONFIG(tooltip)
         btn_import_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_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());
         cb_change_lang->setItemText(0, QCoreApplication::translate("widget_form", "English (en)", nullptr));
         cb_change_lang->setItemText(1, QCoreApplication::translate("widget_form", "Deutsch (de)", nullptr));
         cb_change_lang->setItemText(2, QCoreApplication::translate("widget_form", "Fran37ais (fr)", nullptr));
         cb_change_lang->setItemText(3, QCoreApplication::translate("widget_form", "001311020001 (ru)", nullptr));
    
     #if QT_CONFIG(tooltip)
         btn_csv->setToolTip(QCoreApplication::translate("widget_form", "Create CSV file", nullptr));
     #endif // QT_CONFIG(tooltip)
         btn_csv->setText(QString());
     #if QT_CONFIG(tooltip)
         btn_code->setToolTip(QCoreApplication::translate("widget_form", "Create script file", nullptr));
     #endif // QT_CONFIG(tooltip)
         btn_code->setText(QString());
     #if QT_CONFIG(tooltip)
         btn_json->setToolTip(QCoreApplication::translate("widget_form", "Create JSON file", nullptr));
     #endif // QT_CONFIG(tooltip)
         btn_json->setText(QString());
     #if QT_CONFIG(tooltip)
         btn_xml->setToolTip(QCoreApplication::translate("widget_form", "Create XML file", nullptr));
     #endif // QT_CONFIG(tooltip)
         btn_xml->setText(QString());
         (void)widget_form;
     } // retranslateUi
    

注意: 我忘记将组合框项目标记为不可翻译,所以这就是为什么它们出现在这里。通常认为将语言名称置于其原始语言中并使语言名称保持不变以适应语言环境的变化通常被认为是一种好习惯,否则用户会感到困惑(不懂英语但想切换到中文,语言名称用英文书写)是糟糕用户体验的保证)。

所以最后的结果是(使用 Google 翻译成法语和俄语 :P)