目录
- 简介
- 发行说明
- 效果预览
- Qt本身的国际化
- 存在翻译不全的问题
- 新的方案
- 关于批量翻译
- 总结
简介
本文是《Qml组件化编程》系列文章的第十二篇,主要讨论多国语言动态翻译。
2019年,分享过使用Qt自带翻译的方案,但是效果不太好。这次分享一个非官方的多国语言方案。
发行说明
文章主要发布在涛哥的博客
涛哥的博客-gitee镜像
涛哥知乎专栏-Qt进阶之路。
效果预览
看一下最终效果
(原始字符串全部为英文,中文为人工翻译。
其它语言使用的百度翻译api批量翻译,不太准确,暂时先这样)
Qt本身的国际化
先来回顾一下,Qt的国际化方案:
- C++代码中的字符串使用QObject::tr()包起来,类本身是QObject的子类时可以省略作用域“QObject::”,直接写tr
- qml代码中使用qsTr把字符串包起来
- pro文件中添加一句TRANSLATIONS += trans_zh.qs ,这个名字起什么无所谓,关键是‘_zh’要有。
- 调用lrelease工具,扫描项目并生成trans_zh.qs 文件。这个文件是xml格式的,未经过翻译的,需要为这个文件做一些翻译工作。
- 翻译做好后,调用lupdate工具,生成trans_zh.qm文件。这个文件就是把xml压缩成了二进制。
- 将qm文件放在运行路径,或者资源文件里。
- 切换语言时, Qt/C++代码中使用QTranslater加载qm文件,QCoreApplication卸载旧的QTranslater,并安装新的QTranslater。调用
QmlEngine::retranslate函数
在5.10以前的版本,Qt是不能直接动态切换语言的,要么重新启动程序,要么把所有的text都set一遍,retranslate是5.10才有的接口。
存在翻译不全的问题
上面的方案,在TaoQuick中使用了。
明显的问题是,只能翻译静态的内容,动态加载的ListModel,动态切换语言时不能自动刷新。
按照Qt文档所说,Array或者其它数据结构中的内容,也不能自动刷新。
新的方案
这里抛弃Qt的翻译机制,使用自己实现的方案。
1、约定要用到的字符串,全部用英文。
2、翻译文件使用json文件,一个文件翻译一种语言。
文件命名格式language_xx.json, json内容格式如下;
{
其中lang字段表示当前语言,trans字段是所有的翻译项。
3、实现核心翻译器Trans
自己实现一个Trans类,用来加载翻译包、提供翻译数据,类声明如下:
//Trans.h
其中languages是加载过后支持的所有语言,currentLang是当前语言。
trans函数是用来做翻译的,传入要翻译的字符串,根据当前语言,返回翻译后的字符串。
因为软件到处都要翻译,所以trans函数会被频繁调用,使用QHash<QString, QHash<QString, QString>>这样的
嵌套Hash数据结构,保证查询的平均复杂度为O(1).
transString是一个特殊的属性,其值始终为空,在语言被切换时,会触发transStringChange信号。
这样有什么用呢?先知道这个设定,后面qml部分会详细解释。
cpp 实现如下:
//Trans.cpp
4、Qml中使用新的翻译语法
qml中的语法如下:
Text {
text: trans.trans("Welcome") + trans.transString
}
这是一个很常规的’Qml属性绑定’,或者叫’绑定表达式’, 这样写了以后,text的值依赖于trans.trans()函数返回值和 transString。
当text依赖的属性发出change信号时,qml引擎会重新对这个表达式求值,并把结果赋值给text。
一般情况下,text的值就是trans的返回值,后面的空值不会影响到结果。
当前语言被改变时,函数没有change信号,而transString属性的change信号会被触发,导致qml引擎会重新对这个表达式求值,
此时会重新调用trans函数,按照新的语言返回翻译结果。
Text组件的text属性变化时,会自己刷新UI。
于是,就实现了动态翻译多国语言。
对于ListModel,就把静态字符串换成动态的变量即可:
ListView {
...
delegate: Text {
text: trans.trans(modelData) + trans.transString
}
}
复杂一些的格式化字符串,也是没有问题的:
Text {
text: trans.trans("Today is %1, i feel %2").arg(trans.trans("Sunday")).arg(trans.trans("happy")) + trans.transString
}
对应的翻译文件:
{
关于批量翻译
翻译效果不太理想,不过还是可以分享一下方法。
首先是提取出了所有要翻译的字符串:
//key.json
其次是写了一个PowerShell脚本,逐个调用百度翻译API,并把结果按照前面的json输出。
# trans.ps1
结果如下,生成了一堆json文件
总结
Qml中带个尾巴的写法,虽然有些别扭,但是够用、能达到动态翻译的目标。
如果你有更好的思路,欢迎留言交流。