Qt - 语言家
发布者:承担了全面发布应用程序的责任。通常,他们协调开发者和翻译者的工作,可以使用lupdate工具同步源代码,进行翻译,使用lrelease同步工具为发布应用程序创建运行时使用的翻译文件。
翻译者:可以使用Qt Linguist工具翻译应用程序的文本。当然,这必须要有专业的翻译知识。
开发者:必须创建Qt应用程序能够使用的翻译文本。也应该帮助翻译者识别短语出现的场景。
以上有三种角色-发布者、翻译者、开发者,当然很多情况下,其实就是一种,那就是程序猿自己,大家都懂的。
二、使用流程
1. 使用tr包裹字符串
如果想让你的程序实现国际化,那么就在用户所有可见的字符串处都使用QObject::tr()。
[static] QString QObject::tr(const char *sourceText, const char *disambiguation = nullptr, int n = -1)
2. 修改pro文件
在.pro文件中添加翻译文件名称。
TRANSLATIONS += translations/Chinese.ts \
translations/English.ts \
translations/Japanese.ts
3. 生成翻译文件
点击 菜单栏->工具->外部->Qt语言家->更新翻译(lupdae),此时会在translations目录下面生成Chinese.ts和English.ts,Japanese.ts文件。
4. 打开翻译文件
4.1 修改xml,翻译
ts文件实际上是一个xml文件,直接可以用文本编辑器打开。
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1" language="en"> <!-- 版本和语言 -->
<context>
<name>MainWindow</name>
<message>
<location filename="../mainwindow.ui" line="14"/> <!-- 文本位置-->
<source>MainWindow</source> <!-- 源文本-->
<translatorcomment>title</translatorcomment> <!-- 注释 -->
<translation>Maye</translation> <!-- 翻译文本 -->
</message>
<message>
<location filename="../mainwindow.ui" line="27"/>
<source>用户名</source>
<translation>username</translation>
</message>
<message>
<location filename="../mainwindow.ui" line="40"/>
<source>密码</source>
<translation>password</translation>
</message>
<message>
<location filename="../mainwindow.ui" line="73"/>
<source>登录</source>
<translation>login</translation>
</message>
<message>
<location filename="../mainwindow.ui" line="86"/>
<source>注册</source>
<translation>register</translation>
</message>
<message>
<location filename="../mainwindow.ui" line="100"/>
<source>中文</source>
<translation>chinese</translation>
</message>
<message>
<location filename="../mainwindow.ui" line="105"/>
<source>英文</source>
<translation type="unfinished">English</translation>
</message>
<message>
<location filename="../mainwindow.ui" line="119"/>
<source>语言</source>
<translation type="unfinished">language</translation>
</message>
</context>
</TS>
4.1 使用语言家翻译
找到Linguist预言家打开,点击菜单栏->文件->打开,选择上面生成的.ts文件,并打开。
编辑-》翻译文件设置,可以设置源语言和目标语言。
打开之后可以看到如下界面,主要就是对语言进行翻译。
翻译前面的图标及含义:
图标 | 含义 |
---|---|
![]() |
未翻译 |
![]() |
已翻译,未标记完成 |
![]() |
已翻译,且已标记完成 |
4.2 使用短语书翻译
短语书就是一个提前写好的翻译对照文件,在翻译ts文件的时候,可以打开短语书,很方便的就可以看到翻译。
(1)新建短语书
然后把需要的翻译提前写好,以后也可以重复使用。选择源语言和目标语言。
(2)编辑短语书
短语书创建完成之后,就可以开始编辑短语书了。
如下:先新建条目,再写上源文、译文、以及准确度(可以不写,也可以随便写点东西),最后点击保存,就可以关掉窗口了
(3)使用短语书
如果使用已经写好的短语书,而不是刚刚创建的,那么需要先打开短语书。如果是刚在预言家上创建的,则是打开状态的。
5. 发布翻译文件
翻译完成之后,就可以发布翻译了,为什么要发布呢?发布是什么意思?
-
ts文件是文本文件,占用内存比较大,发布之后会生成对应的二进制文件,内存较小
-
通过发布就可以把ts文件转换成二进制文件
发布的方式有两种:
-
通过语言家发布
-
点击 菜单栏->文件->发布全部即可发布所有ts文件
-
-
通过Qt Creator发布
-
点击 菜单栏->工具->外部->Qt语言家->发布翻译(lrelease),会在translations目录中生成zc_CN.qm和en.qm两个文件。
-
发布成功后也会在translations目录下面生成Chinese.qm和English.qm,Japanese.qm文件。
6. 加载语言文件
QTranslator类为文本输出提供国际化支持。
该类的对象包含一组从源语言到目标语言的翻译。 QTranslator提供了在翻译文件中查找翻译的功能。 翻译文件使用Qt Linguist创建。
QTranslator translator;
if(translator.load("linguist_en.qm","F:\\MyCode\\QtCode\\Lingguist\\translate"))
{
qApp->installTranslator(&translator);
ui->retranslateUi(this);
}
注意:翻译文件加载的位置必须在界面实例化之前完成,否则是没有效果的.
7. 动态切换语言
注意:如果界面是通过Ui生成的,切换语言之后,可以通过调用函数retranslateUi翻译界面,否则需要重启程序.
-
下拉框切换语言
void MainWindow::on_comboBox_currentIndexChanged(int index)
{
QString filename;
switch (index)
{
case 0:
filename = "linguist_zh_CN.qm";
break;
case 1:
filename = "linguist_en.qm";
break;
}
QTranslator translator;
if(!translator.load(filename,"F:\\MyCode\\QtCode\\Lingguist\\translate"))
{
qDebug()<<"翻译文件加载失败";
return;
}
if(qApp->installTranslator(&translator))
{
qDebug()<<"安装成功";
}
else
{
qDebug()<<"安装失败";
}
ui->retranslateUi(this);
}
-
保存语言选择和恢复语言
保存
void MainWindow::on_comboBox_currentIndexChanged(int index)
{
QString filename;
switch (index)
{
case 0:
filename = "linguist_zh_CN.qm";
break;
case 1:
filename = "linguist_en.qm";
break;
}
//保存配置
QSettings setting("config.ini",QSettings::Format::IniFormat);
setting.setValue("ts",filename);
...
}
恢复
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QSettings setting("config.ini",QSettings::Format::IniFormat);
QString filename = setting.value("ts").toString();
QTranslator translator;
if(!translator.load(filename,"F:\\MyCode\\QtCode\\Lingguist\\translate"))
{
qDebug()<<"翻译文件加载失败";
}
if(qApp->installTranslator(&translator))
{
qDebug()<<"安装成功";
}else
{
qDebug()<<"安装失败";
}
MainWindow w;
w.show();
return a.exec();
}
-
重启程序
//重启程序才能生效,是否重启
auto ret = QMessageBox::information(this,"hit","是否重启",QMessageBox::StandardButton::Ok,QMessageBox::No);
if(ret == QMessageBox::Ok)
{
QString exe = QApplication::applicationDirPath()+"/"+qAppName()+".exe";
QProcess::startDetached(exe);
qApp->quit();
}
在论坛中漂,经常遇到有人遇到tr相关的问题。用tr的有两类人:
-
(1)因为发现中文老出问题,然后搜索,发现很多人用tr,于是他也开始用tr
-
(2)另一类人,确实是出于国际化的需要,将需要在界面上显示的文件都用tr包起来,这有分两种:
-
(2a) 用tr包住英文(最最推荐的用法,源码英文,然后提供英文到其他语言的翻译包)
-
(2b) 用tr包住中文(源码用中文,然后提供中文到其他语言的翻译包)
-
注意哦,如果你正在用tr包裹中文字符,却不属于(2b),那么:
-
你在误用tr
-
你需要的是QString,而不是tr
如果你确实属于(2b),请做好心理准备,你可能还会遇到很多困难,
tr 是做什么的?下面二者的区别是什么?
QString text1 = QObject::tr("hello");
QString text2 = QString("hello");
tr是用来实现国际化,如果你为这个程序提供了中文翻译包(其中hello被翻译成中文"你好"),那么text1的内容将是中文"你好";如果你为程序提供且使用日文翻译包,那么text1的内容将是日文。
tr是经过多级函数调用才实现了翻译操作,是有代价的,所以不该用的时候最好不要用。
8. 项目示例代码
工程文件预览
MyApplication.pro
QT += core gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
CONFIG += c++11
# The following define makes your compiler emit warnings if you use
# any Qt feature that has been marked deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the
# deprecated API in order to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS
# You can also make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
SOURCES += \
controller.cpp \
global_function.cpp \
main.cpp \
mainwindow.cpp \
settingsdialog.cpp
HEADERS += \
controller.h \
global_function.h \
mainwindow.h \
settingsdialog.h
FORMS += \
mainwindow.ui \
settingsdialog.ui
TRANSLATIONS += translations/Chinese.ts \
translations/English.ts \
translations/Japanese.ts
# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target
RESOURCES += \
res.qrc
controller.h
#ifndef CONTROLLER_H
#define CONTROLLER_H
#include <QObject>
class SettingsDialog;
class AutoCloseFrame;
class Titlebar;
class Controller : public QObject
{
Q_OBJECT
public:
~Controller();
static Controller* GetInstance();
void RetranslateUi();//重译所有界面(总接口)
//注册界面,用处是Controller可以访问其他子界面的接口
void RegisterVolumeAutoCloseFrame(AutoCloseFrame *w);
void RegisterSettingWindow(SettingsDialog *w);
void RegisterTitleBar(Titlebar* w);
signals:
private:
Controller(QObject *parent = nullptr);
SettingsDialog *m_SettingsDialog;
AutoCloseFrame *m_AutoCloseFrame;
Titlebar *m_Titlebar;
};
#endif // CONTROLLER_H
global_function.h
#ifndef GLOBAL_FUNCTION_H
#define GLOBAL_FUNCTION_H
#include <QString>
extern void g_SetAppStyle(const QString strStyle);
extern QString g_GetStrStyle();
#endif // GLOBAL_FUNCTION_H
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include "controller.h"
#include "settingsdialog.h"
#include "ui_mainwindow.h"
class MainWindow : public QMainWindow,public Ui_MainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
public slots:
void SettingsSlot();
void slotSwitchLanguage(LANGUAGE language);
protected:
//void changeEvent(QEvent *event);
private:
//Ui::MainWindow *ui;
Controller* m_Controller;
SettingsDialog* m_SettingsDialog;
};
#endif // MAINWINDOW_H
settingsdialog.h
#ifndef SETTINGSDIALOG_H
#define SETTINGSDIALOG_H
#include <QDialog>
#include "ui_settingsdialog.h"
#include "controller.h"
typedef enum{
UI_ZH,
UI_EN,
UI_JP
}LANGUAGE;
Q_DECLARE_METATYPE(LANGUAGE)
class SettingsDialog : public QDialog,public Ui_SettingsDialog
{
Q_OBJECT
public:
explicit SettingsDialog(Controller* ctrl,QWidget *parent = nullptr);
~SettingsDialog();
void CreateListWidget();
void InitGeneralSetting();//初始化常规设置
void InitVideo(); //初始化视频设置
void InitAudio(); //初始化音频设置
void InitSubtitle(); //初始化字幕设置
void InitHotkeys(); //初始化快捷键设置
void RetranslateUi();
signals:
void signalSwitchLanguage(LANGUAGE language);
public slots:
void onIndexChanged(int index);
//void onSelectChanged(int);
protected:
//void changeEvent(QEvent *event);
private:
//Ui::SettingsDialog *ui;
Controller* m_ctrl;
QListWidgetItem *General;
QListWidgetItem *Video;
QListWidgetItem *Audio;
QListWidgetItem *Subtitles;
QListWidgetItem *Shortcuts;
};
#endif // SETTINGSDIALOG_H
controller.cpp
#include "controller.h"
#include <QDebug>
#include "settingsdialog.h"
//Controller类的作用:就是相当于主界面与其他界面中间的一个桥梁,Controller可以访问子界面的接口,子界面也可以访Controller的接口
Controller::Controller(QObject *parent) : QObject(parent)
{
qDebug() << "Controller构造函数";
}
Controller::~Controller()
{
qDebug() << "~Controller析构函数";
}
Controller* Controller::GetInstance()
{
static Controller* controller = NULL;
if (controller == NULL)
{
controller = new Controller;
}
return controller;
}
void Controller::RetranslateUi()//重译所有界面(总接口)
{
m_SettingsDialog->RetranslateUi(); //刷新设置页面的翻译
//m_Titlebar->RetranslateUi(); //刷新标题栏的翻译
//m_PlayListWidget->RetranslateUi(); //刷新播放列表的翻译
}
void Controller::RegisterVolumeAutoCloseFrame(AutoCloseFrame *w)
{
m_AutoCloseFrame = w;
}
void Controller::RegisterSettingWindow(SettingsDialog *w)
{
m_SettingsDialog = w;
}
void Controller::RegisterTitleBar(Titlebar* w)
{
m_Titlebar = w;
//connect(m_Titlebar, SIGNAL(sigMove(int)), this, SLOT(slotPLMove(int)));
}
global_function.cpp
#include "global_function.h"
#include <QApplication>
#include <QFont>
#include <QFile>
void g_SetAppStyle(const QString strStyle)//设置App的整体样式
{
qApp->setStyleSheet(strStyle);//设置App样式
}
QString g_GetStrStyle()
{
QFile qss("C:\\Users\\Administrator\\Documents\\MyApplication\\qss\\Style.qss");
if(!qss.open(QFile::ReadOnly| QIODevice::Text))
{
qWarning("Style.css open falied");
return 0;
}
QString strStyle = qss.readAll();
qss.close();
return strStyle;
}
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>
#include <QTranslator>
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
{
setupUi(this);
m_Controller = NULL;
m_Controller = Controller::GetInstance();
/*嵌入窗体*/
m_SettingsDialog = new SettingsDialog(m_Controller,this);
m_SettingsDialog->setHidden(true);
connect(BtnSettings, SIGNAL(clicked()), this, SLOT(SettingsSlot()));
connect(m_SettingsDialog, SIGNAL(signalSwitchLanguage(LANGUAGE)), this, SLOT(slotSwitchLanguage(LANGUAGE)));
}
MainWindow::~MainWindow()
{
//delete ui;
}
void MainWindow::SettingsSlot()
{
m_SettingsDialog->exec();
}
void MainWindow::slotSwitchLanguage(LANGUAGE index)
{
QString filename;
switch(index)
{
case UI_ZH:
filename = QString("Chinese.qm");
break;
case UI_EN:
filename = QString("English.qm");
break;
case UI_JP:
filename = QString("Japanese.qm");
break;
}
QTranslator translator;
if(!translator.load(filename,"C:\\Users\\Administrator\\Documents\\MyApplication\\translations"))
{
qDebug()<<"翻译文件加载失败";
return;
}
else
{
if(qApp->installTranslator(&translator))
{
qDebug()<<"安装成功";
}
else
{
qDebug()<<"安装失败";
}
}
m_Controller->RetranslateUi();
retranslateUi(this);
}
settingsdialog.cpp
#include "settingsdialog.h"
SettingsDialog::SettingsDialog(Controller* ctrl,QWidget *parent) :QDialog(parent), m_ctrl(ctrl)
{
setupUi(this);
ctrl->RegisterSettingWindow(this);//注册界面
CreateListWidget();
InitGeneralSetting();
connect(combo_language, SIGNAL(activated(int)), this, SLOT(onIndexChanged(int)));
connect(listWidget, SIGNAL(currentRowChanged(int)), stackedWidget, SLOT(setCurrentIndex(int)));
}
SettingsDialog::~SettingsDialog()
{
//delete ui;
}
void SettingsDialog::CreateListWidget()
{
General = new QListWidgetItem(QIcon(":/resources/general.png"), tr("General"), listWidget);
Video = new QListWidgetItem(QIcon(":/resources/video.png"), tr("Video"), listWidget);
Audio = new QListWidgetItem(QIcon(":/resources/audio.png"), tr("Audio"), listWidget);
Subtitles = new QListWidgetItem(QIcon(":/resources/subtitle.png"), tr("Subtitles"), listWidget);
Shortcuts = new QListWidgetItem(QIcon(":/resources/shortcut.png"), tr("KeyShortcuts"), listWidget);
General->setSizeHint(QSize(80,60));
Video->setSizeHint(QSize(80,60));
Audio->setSizeHint(QSize(80,60));
Subtitles->setSizeHint(QSize(80,60));
Shortcuts->setSizeHint(QSize(80,60));
listWidget->addItem(General);
listWidget->addItem(Video);
listWidget->addItem(Audio);
listWidget->addItem(Subtitles);
listWidget->addItem(Shortcuts);
}
void SettingsDialog::InitGeneralSetting()
{
combo_language->clear();
combo_sking->clear();
combo_language->addItem("Chinese", QVariant::fromValue(UI_ZH));
combo_language->addItem("English", QVariant::fromValue(UI_EN));
combo_language->addItem("Japanese", QVariant::fromValue(UI_JP));
combo_language->setCurrentIndex(UI_EN);
combo_sking->addItem(tr("Dark"));
combo_sking->addItem(tr("Light"));
}
void SettingsDialog::RetranslateUi()
{
this->setWindowTitle(tr("setting dialog"));
General->setText(tr("General"));
Video->setText(tr("Video"));
Audio->setText(tr("Audio"));
Subtitles->setText(tr("Subtitles"));
Shortcuts->setText(tr("KeyShortcuts"));
combo_language->setItemText(UI_ZH, tr("Chinese"));
combo_language->setItemText(UI_EN, tr("English"));
combo_language->setItemText(UI_JP, tr("Japanese"));
combo_sking->setItemText(0,tr("Dark"));
combo_sking->setItemText(1,tr("Light"));
retranslateUi(this);
}
void SettingsDialog::onIndexChanged(int index)
{
LANGUAGE language = (LANGUAGE)index;
emit signalSwitchLanguage(language);
}
main.cpp
#include "mainwindow.h"
#include <QApplication>
#include <QDebug>
#include <QFile>
#include "global_function.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
//qApp->setStyleSheet(g_GetStrStyle());//错误,需要界面显示后再设置app的全部样式
MainWindow w;
w.show();
qApp->setStyleSheet(g_GetStrStyle());
return a.exec();
}
运行结果:
翻译后:
9. 各国语言代码
查看各国语言代码网页链接:http://www.lingoes.cn/zh/translator/langcode.htm
.ts文件中language字段对应的就是语言代码,如下图所示:
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构