Qt 的多语言支持的翻译机制
Qt 的多语言支持的翻译机制
来源 https://zhuanlan.zhihu.com/p/44536503
概述
根据“对象模型(Object Model)”所述,Qt 中有而 C++ 没有的特性就包括翻译这一部分。你试想一下用纯 C++ 写一个“Hello world”然后把它翻译,是不是就懵逼了?是不是不知道该怎么办了?Qt 已经为你提供了翻译的一条龙服务,使用起来非常的方便。本节的内容就和大家聊聊 Qt 中该如何进行翻译操作。
我们到底在翻译什么?
大多数情况我们可能只进行文字上的翻译,比如英语翻译成汉语、俄语、日语等等这些肉眼就可以看得到的东西。但是你有想过阿拉伯文可是从右往左书写的;各国的货币符号可是不一样的(人民币是¥,美元是$);各国的大数表示方式可是不一样的,比如我们写100万就是“100 0000”,美国人习惯于“1,000,000”,中间的逗号咋办呀?要是静态的数字还好办,那万一要显示的数字是个变量就没辙了。所以“区域”的设置也属于翻译的一部分。参考“QLocale 类 - 存储本机的区域设置”。
除了肉眼能看到的以外,还有就是眼睛看不到的字符编码转换问题。这部分不是给用户看的,而是给程序员看的。假设我们得到一段以“KIO8-R”为编码格式的俄文字节流,我要怎么把这段数据转换成比如 GBK18030 编码的中文啊?所以“编码的转换”也属于翻译的一部分。Qt 也同样提供了针对这个问题的类,参考“QTextCodec 类 - 编/解码小工具”。
综上所述,一个完整的翻译包括三部分:(1)文字的翻译;(2)区域格式的翻译;(3)语言编码的翻译。然鹅,绝大多数的新手可能只关注第一条而忽略了后两条。
和翻译相关的 Qt 类
Qt 的翻译功能很简单,所用到的工具类就那么几个,最常用的就是 QTranslator、QTextCodec、QLocale 这三个类。所有关于翻译的类及其说明如下:
- QTranslator:存储翻译文件,执行翻译操作。
- QLocale:存储本机的区域设置,还可以不同区域格式的转换。
- QTextCodec:一个编/解码的小工具。
- QTextDecoder:可以根据字节流的状态正确拼接字节流,从而进行解码操作,常用于网络。
- QTextEncoder:可以根据字节流的状态正确拼接字节流,从而进行编码操作,常用于网络。
- QCollator:基于不同区域来对比字符串的类。
- QCollatorSortKey:用于加速一个字符串的排序。
翻译流程框架图
大致的流程是这样的:首先源代码产生 ts 文件,然后送给 Qt Linguist(Qt语言家)这个 Qt 自带的小工具进行处理产生 qm 翻译文件,最后源代码里加载这个 qm 翻译文件。
感觉有一点迭代的意思,其实不影响翻译。因为最后一步进行加载 qm 翻译文件所写的代码已经没有和界面相关的字符串了。
如何进行翻译?
从这部分开始我们详细的说下每一个步骤该如何做。为方便讲述,这里有一个示例工程,MainWindow 类中就添加一个 QLabel 对象。
第一步:写规范的代码
(1)用 QString 包裹不需要翻译的文本。
因为 QString 内部采用 Unicode 编码格式,而 Unicode 几乎能表达世界上任何一个语言,并且很多 Qt 库函数的参数也是 QString 类型,所以处理起来会比较方便。
当然用 char* 也可以,但是便宜的时候 Qt 内部还是会转换成 QString,这就会带来一定的系统开销。关于 char* -> QString 的转换问题,Qt 默认会把 char* 当成 UTF-8 编码格式。因此如果 char* 中的内容是其他编码格式的,需要用 QTextCodec 类来转换。参考“QTextCodec 类 - 编/解码小工具”。
(2)用 tr() 包裹需要翻译的文本
回顾本文最开头的概述,那么凡是你要进行翻译的文本都要用 tr() 函数来包裹。这个 tr() 是 QObject 类的一个函数,用它包裹的文本会被 Qt Linguist(Qt语言家)捕捉到从而进行翻译工作。或者你也可以这样理解,用 tr() 包裹的文本会添加到 ts 文件中。关于 ts 文件在下文会说到。例如我们的示例工程就是这样写的:
QML 的翻译是用 qsTr() 来代替 tr() 函数。
(3)定义上下文
什么叫上下文?请看下图,上下文一般指这个要翻译的文本属于哪个类。QObject 类及其子类只要使用了 Q_OBJECT 宏,默认是当前类作为上下文。
当然你也可以显示的调用某个类的 tr() 函数来改变文本所属的上下文。比如还是下图,如果直接写 tr("Hello"),那么打开 Qt Linguist 的话这一条会属于 MainWindow 这个上下文。但我这里写 QScroller::tr("Hello") 的话,图中显示就是属于 QScroller 这个类作为上下文。
(4)如何翻译非 Qt 类
QObject 类有现成的 tr() 函数可以方便使用,但是如果是非 QObject 类没有 tr() 函数怎么办呢?方法有四种:
第一种:利用 QCoreApplication::tr() 函数
用 QCoreApplication::tr() 这个函数来包裹要翻译的文本,只有这样才能被 Qt Linguist 捕捉到。例如:
第二种:利用 QCoreApplication::translate() 函数
用 QCoreApplication 类的 translate() 函数包裹要翻译的文本也能被 Qt Linguist 捕捉到。
第三种:利用 QCoreApplication 类的 Q_DECLARE_TR_FUNCTIONS 宏
使用这个宏当然要 #include <QCoreApplication> 啦。使用之后就可以用 tr() 函数了。
内部的原理依靠元对象系统,使用该宏后会自动在该类添加如下两个静态函数。这样就可以用啦。
static inline QString tr(const char *sourceText, const char *comment = 0);
static inline QString trUtf8(const char *sourceText, const char *comment = 0);
第四种:使用 QT_TR_NOOP() 宏和 QT_TRANSLATE_NOOP() 宏
示例代码如下,可以被捕捉到。但是这个宏一般不这样用,你也发现了在 Qt Linguist 的短语和表单这个窗口内没有“world”字符串了。
正确做法是用一个数组来存储用该宏包裹的字符串,这个数组在运行时存储的就会变为翻译后的文本。正确做法如下:
QString FriendlyConversation::greeting(int type)
{
static const char *greeting_strings[] = {
QT_TR_NOOP("world"),
QT_TR_NOOP("hello")
}
return tr(greeting_strings[type]);
}
(5)给翻译添加个注释
添加注释可以更好的帮助翻译人员进行理解这个文本的含义,尤其是不同语境下有不同含义时。添加注释有两种方法,一种是采用固定的注释格式,另一种就是利用 tr() 的第二个参数。
第一种:添加注释的格式为:
//: ...
/*: ... */
第二种:tr() 函数的双参数
给翻译人员提供额外的信息来帮助理解不仅可以添加注释,在 Qt 4.4 之前最常用的方法是用 tr() 函数的第二个参数。
(6)如何翻译复数?
有时候会遇到这样一个场景,英语中有这样两句话:
I have 1 book.
I have 2 books.
此时就可以利用 tr() 的第三个参数,写代码就这样写:
int n = books.count();
showMessage(tr("I have %n book(s).", "", n));
上述代码的 tr() 函数,第一个参数是实际展示的文本,变量用 %n表示,就好比 %1、%2等之类的。book 的复数形式用括号括起来,翻译之后就会根据数来显示不同的形式;第二个参数是注释用的,这里面写不写看你自己,反正是给翻译人员看的;第三个就是这个变量 n。
第二步:转成 ts 文件
要想转成 ts 文件,一方面需要在 pro 文件中指定 ts 文件名,另一方面用 lupdate 功能生成。
在 pro 文件中指定 ts 文件
在 .pro 文件中添加如下代码:
TRANSLATIONS = app_zh.ts \
app_de.ts
注意 TS 文件名,标明区域语言对运行时加载何种语言会很有用。Qt Linguist 会根据 TS 文件名自动设置区域。例如 app_de.ts 会将最终的翻译语言设置为德语,app_de_ch.ts 会将语言设置为德语,国家设置为瑞士。啥意思呢?其实是方便翻译人员的一个小技巧,看下图的不同点。看到没,它会根据本机的区域设置自动显示对应的语言。如果文件名不遵循该规范,也可以在 Qt Linguist 的“编辑”-“翻译文件设置”中显式的设置区域信息。
用 lupdate 生成 ts 文件
在 Qt Creator 的菜单栏中依次点击“工具”-“外部”-“Qt语言家”-“更新翻译(lupdate)”,就会在源代码文件所在的目录生成 ts 文件。
第三步:翻译并生成 qm 文件
用 Qt Linguist 打开 ts 文件并翻译后记得时刻保存,至此 ts 文件制作完毕,接下来就是 qm 文件的生成。
qm 文件生成可以直接在 Qt Linguist 里点击“文件”-“发布”,或者在 Qt Creator 中点击“工具”-“外部”-“Qt语言家”-“发布翻译(lrelease)”都可以。
至此,qm 文件生成完毕。
最后一步:源代码中加载、安装翻译文件
还记得 QTranslator 类吗?参考“QTranslator 类 - 翻译文件的容器”。示例代码如下,很简单吧。
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QTranslator translator;
translator.load("app_zh");
app.installTranslator(&translator);
return app.exec();
}
多说两句,在上文第二步我们说过,翻译文件的命名如果遵循 Qt 的规范写的话会自动被识别这是哪个语言的翻译文件,比如“app_zh.ts”中的“zh”。所以高手们用 QTranslator:: load() 函数一般是这样的:
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QString locale = QLocale::system().name();
QTranslator translator;
translator.load(QString("app_") + locale);
app.installTranslator(&translator);
return app.exec();
}
你看,用 QLocale 直接就能获得计算机的区域设置“zh”了。如果这个程序在德语的操作系统环境下就会加载“app_de.qm”文件。
结语
整个有关 Qt 翻译的内容就是这么多了。笔者在写这篇文章时参考的官方内容有:
- Internationalization with Qt
- Writing Source Code for Translation
- Qt Linguist Manual
- Hello tr()示例
- Arrow Pad示例
- Troll Print示例
================= End
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南