我正在寻找一个相对简单的示例,以了解如何进行动态翻译。我正在创建一个关于使用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 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表单使用以下代码进行加载(正在进行中):
#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();
}
小部件长这样:
我想保留我的小部件的创建方式(在C++中加载UI文件并添加一些额外功能)。我的QTranslator
是全局的原因是需要让翻译器与QApplication
实例同生共死。
它应该工作的方式(但目前没有)是每当更改QComboBox
(屏幕截图右上角)中当前选定的项目时,每个按钮的工具提示都会被翻译。我对Qt的翻译设施非常陌生,而且文档在这方面并不真正出色(这是个人意见)。
在开始动态翻译之前,我在main()
中加载了一个QM文件,它可以工作-工具提示以QM文件所用的语言显示。只有在添加新功能后,我才看不到任何变化。