1. Qt的Quick项目实现多语言加载主要步骤是什么:
1.1CMake怎么指定创建TS文件
# Add other components as you want.
find_package(Qt5 REQUIRED COMPONENTS Quick Qml LinguistTools)
target_link_libraries(${PROJECT_NAME} PRIVATE Qt5::Quick Qt5::Qml)
set(TS_FILES
${CMAKE_SOURCE_DIR}/translations/${PROJECT_NAME}_en.ts
${CMAKE_SOURCE_DIR}/translations/${PROJECT_NAME}_zh-CN.ts
${CMAKE_SOURCE_DIR}/translations/${PROJECT_NAME}_de.ts
${CMAKE_SOURCE_DIR}/translations/${PROJECT_NAME}_es.ts
${CMAKE_SOURCE_DIR}/translations/${PROJECT_NAME}_fr.ts
${CMAKE_SOURCE_DIR}/translations/${PROJECT_NAME}_it.ts
${CMAKE_SOURCE_DIR}/translations/${PROJECT_NAME}_ja.ts
${CMAKE_SOURCE_DIR}/translations/${PROJECT_NAME}_ko.ts
${CMAKE_SOURCE_DIR}/translations/${PROJECT_NAME}_pt-PT.ts)
# Generate TS file for App.
add_custom_target(
lupdate
COMMAND ${Qt5_LUPDATE_EXECUTABLE} ${CMAKE_SOURCE_DIR} -ts ${TS_FILES})
# Generate QM files with TS files include plugins.
add_custom_target(
lrelease
COMMAND ${Qt5_LRELEASE_EXECUTABLE} ${TS_FILES})
查阅官网资料你可能会发现生成ts文件和qm文件并不是上面创建target的方式,而是以下的方式,只不过Qt 6版本前功能是有缺陷的。
qt5_create_translation(QM_FILES ${CMAKE_SOURCE_DIR} app_zh_CN.ts)
add_executable(app main.cpp qml.qrc ${QM_FILES})
1.2如何代码中指定加载指定的qm文件
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QLocale>
#include <QTranslator>
int main(int argc, char *argv[]) {
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
// ======== Make sure installing translation before loading qml. ============= //
QTranslator translator;
QString filePath = qApp->applicationDirPath() + "/translations/xxxApp_zh_CN.qm";
translator.load(filePath);
app.installTranslator(&translator);
// ======== Make sure installing translation before loading qml. ============= //
QQmlApplicationEngine engine;
const QUrl url(QStringLiteral("qrc:/main.qml"));
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
&app, [url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl)
QCoreApplication::exit(-1);
}, Qt::QueuedConnection);
engine.load(url);
return app.exec();
}
如果软件多语言切换无需立即生效,那么以上代码就能满足,需要注意的是
app.installTranslator(&translator)
必须在加载qml之前,否则会失效。
1.3如何操作生成TS文件和QM文件
- 生成ts文件:在build里选择target lupdate,然后项目右键rebuild,ts文件将产生在项目根目录的translations里。
- 生成qm文件:在build里选择target lrelease,然后项目右键rebuild,qm文件将产生在ts所在的目录里,即:根目录的translations里。
2. 语言切换如何立即生效(无需重启)
2.1 为了方便集成多语言切换,我们写了一个多语言切换的实现类
Localization.h
内容如下:
#ifndef LOCALIZATION_H
#define LOCALIZATION_H
#include <QObject>
#include <QMap>
#include <QTranslator>
class Localization : public QObject {
Q_OBJECT
Q_PROPERTY(QString currentLanguage READ currentLanguage WRITE setCurrentLanguage NOTIFY currentLanguageChanged)
public:
const QMap<QString, int> SUPPORT_LANGS = {{"en", 0x0009},{"zh-CN", 0x0004}, {"ja", 0x0011}, {"de", 0x0007},
{"es", 0x000A}, {"fr", 0x000C}, {"it", 0x0010}, {"pt-PT", 0x0016},
{"ko", 0x0012}};
explicit Localization(const QString &moduleName, QObject *parent = nullptr);
QString currentLanguage() const;
void setCurrentLanguage(const QString &language);
/**
* @brief Call it once App started that notified from Qml,
* so that plugins in App can load current language.
*/
Q_INVOKABLE virtual void loadCurrentLanguge() = 0;
/**
* @brief Override to implement saving current language into config file.
*/
virtual bool saveCurrentLanguage(const QString &language) = 0;
private:
bool loadLanguage(const QString &language);
signals:
void currentLanguageChanged();
protected:
QString mModuleName{};
QTranslator mTranslator{};
QString mCurrentLanguage{};
};
#endif // LOCALIZATION_H
Localization.cpp
内容如下:
#include "Localization.h"
#include <QCoreApplication>
#include <QFile>
#include "Logger.h"
Localization::Localization(const QString &moduleName, QObject *parent)
: QObject{parent},
mModuleName(moduleName) {
}
QString Localization::currentLanguage() const { return mCurrentLanguage; }
void Localization::setCurrentLanguage(const QString &language) {
if (mCurrentLanguage != language && loadLanguage(language)) {
mCurrentLanguage = language;
saveCurrentLanguage(language);
emit currentLanguageChanged();
}
}
bool Localization::loadLanguage(const QString &language) {
if (language.isEmpty()) {
LOGERROR("Language to load is empty.");
return false;
}
if (!SUPPORT_LANGS.contains(language)) {
LOGERROR("Language to load is not supported.");
return false;
}
QString filePath = qApp->applicationDirPath() + QString("/translations/%1_%2.qm").arg(mModuleName, language);
if (!QFile(filePath).exists()) {
LOGERROR("Langauge file to load is not exist: %s", filePath.toStdString().c_str());
return false;
}
if (!mTranslator.load(filePath)) {
LOGERROR("Load language failed: %s", filePath.toStdString().c_str());
return false;
}
qApp->installTranslator(&mTranslator);
LOGINFO("Load langauge file successfully: %s", filePath.toStdString().c_str());
emit currentLanguageChanged();
return true;
}
需要注意的是:
在头文件里有两个虚函数需要自己实现,假如你的项目里有个名字类似AppSettings.h
的实现用于集中实现App的所有配置,那么你可以考虑让他继承Localization
并实现此虚函数,即:
- saveCurrentLanguage():将设置的当前语言存入你当前项目的配置文件或者数据库中。
- loadCurrentLange()**: App启动过程中,从你当前项目的配置文件或者数据库种查询当前配置的语言并调用
setCurrentLanguage(language)
缓存。
class AppSettings : public Localization {
// other code
public:
// Save selected language to config file or database in your App.
bool saveCurrentLanguage(const QString &language) override;
// Find current language from your App's config file or database.
void loadCurrentLanguage() overload;
}
读了代码,你会知道最终程序切换语言所加载的qm文件存在可执行文件目录下的translations里,并且切换成功后会发信号告知外面,通知外部为的是告知main.cpp
里的QQmlApplicationEngine
执行retranslate()
,这样App和Plugin将全局刷新翻译,后面再讲。
2.2 如何在main.cpp里集成Localizations使得App切换翻译能立即生效
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QLocale>
#include <QTranslator>
int main(int argc, char *argv[]) {
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
const QUrl url(QStringLiteral("qrc:/main.qml"));
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
&app, [url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl) {
QCoreApplication::exit(-1);
}
}, Qt::QueuedConnection);
// Hot reload translation overall.
AppSettings appSettings{};
QObject::connect(&appSettings, &AppSettings::currentLanguageChanged, &engine, [&engine]() -> void {
// Refresh entire App's translation even plugins.
engine.retranslate();
});
engine.load(url);
return app.exec();
}
2.3 App加载好后通知App以及Plugin全局刷新翻译
import QtQuick 2.15
import QtQuick.Window 2.15
Window {
width: 640
height: 480
visible: true
title: qsTr("Hello World")
Component.onCompleted: {
appSettings.loadCurrentLanguage()
}
}
如果你的项目无需支持翻译切换即时生效,也可以在main.cpp里
QQmlApplicationEngine engine
创建之前直接调用appSettings.loadCurrentLanguage()。
2.4 Plugin是如何做到跟随App一起切换语言的呢?
如标题所说,plugin的语言切换是跟随App一起切换的:
ImagePlugin {
id: imagePlugin
currentLanguage: appSettings.currentLanguage
}
一般你的plugin可能会有个对外暴露的class,需要让其继承Localization
,只不过无需实现Localization
内置的两个虚函数,因为plugin是无需记录语言配置的,这个工作是交给App来做的:
class ImagePlugin : public Localization {
public:
ImagePlugin (QObject *parent = nullptr);
}