Qt多语言基于CMake的自动化转换脚本

1、背景

Qt的多语言文件是xml格式,且需要包含源文件名,行数等等信息才可以被正确识别(有点奇怪),如下为一段标准的Qt多语言格式

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1" language="ts_ZA">
<context>
    <name>HomeView</name>
    <message>
        <location filename="../../../src/UI/Home/HomeView.cpp" line="90"/>
        <source>homepage_create</source>
        <translation type="unfinished"></translation>
    </message>
</context>
<context>
    <name>MacWindowHelper</name>
    <message>
        <location filename="../../../native/Mac/Mainwindow_Mac.mm" line="56"/>
        <source>About</source>
        <translation type="unfinished"></translation>
    </message>
</context>
</TS>

上述source字段即为key,translation对应的即为具体的多语言文案。

官方提供了工具将源文件名及行数信息写入xml中,也提供了一个GUI工具填写翻译文案(需要一个语言一个字段去填写),所以当项目中新增多语言,则需要调用工具更新多语言文件,然后再一个个更新对应多语言,非常麻烦。这里希望实现如下目标:编译期自动调用工具生成xml,自动调用脚本将多余文案填入translation字段。

2、Qt多语言实现流程

有两种运行机制。1、将xml多语言编译为一个cpp文件最终直接链接到二进制包中。2、程序运行期间动态加载解析xml。官方推荐第一种方式如下为第一种方式流程:

image.png

3、自动转换脚本实现流程

实现思路为当调用lupdate生成的多语言xml文件(空文案)后,调用填词脚本将多语言文案填入字段中。流程如下:


image.png

1、填词脚本

填词脚本代码为c++,它首先读解析现有项目ABS中(ios格式,也可以是其他格式,需自行实现)多语言文案文件,然后填入多语言文件.ts对应字段中。具体实现代码位于地址的tools/tsconvert目录下

2、多语言文案

上述填词脚本自动转换iOS多语言格式为Qt的xml格式。只需要将iOS多语言放入项目中即可

image.png

3、qrc文件

要实现从多语言文件.qm到多语言文件.cpp的转换,还需要借助qrc文件,它告诉cmake宏如找到qm文件和生成几种多语言。格式如下:

<RCC>
    <qresource prefix="/">
        <file>en.qm</file>
        <file>es.qm</file>
        <file>pt.qm</file>
        <file>ru.qm</file>
    </qresource>
</RCC>

file字段是qm文件与qrc文件的相对路径。

4、cmake配置

还需要cmake配置才能自动完成上述整个流程

  1. 添加填词脚本子任务
add_subdirectory(tools/tsconvert)
  1. 自动调用lupdate、-lrelease、填词脚本
set(languagesDir "${CMAKE_CURRENT_BINARY_DIR}/resources/languages")
file(MAKE_DIRECTORY ${languagesDir})
# 将qrc文件拷贝到指定目录;由于qrc指定了qm文件与其为同一目录,所以这里拷贝一下,不然会找不到
configure_file(resources/languages/language.qrc ${languagesDir} COPYONLY)
set(TS_FILES_DIR ${CMAKE_CURRENT_BINARY_DIR}/resources/languages)
set(TMP_TS_FILES
        ${TS_FILES_DIR}/en.ts.0.ts
        ${TS_FILES_DIR}/es.ts.0.ts
        ${TS_FILES_DIR}/pt.ts.0.ts
        ${TS_FILES_DIR}/ru.ts.0.ts
)
set(TS_FILES
        ${TS_FILES_DIR}/en.ts
        ${TS_FILES_DIR}/es.ts
        ${TS_FILES_DIR}/pt.ts
        ${TS_FILES_DIR}/ru.ts
)
# 该语句实际上通过add_custom_command调用lupdate指令;
qt5_add_lupdate("" ${TMP_TS_FILES} ${PROJECT_SOURCES})
foreach (lan_file tmp_ts_file ts_file IN ZIP_LISTS LAN_FILES TMP_TS_FILES TS_FILES)
    add_custom_command(
            OUTPUT ${ts_file}
            COMMAND ${CMAKE_CURRENT_BINARY_DIR}/tools/tsconvert/tsconvert "${lan_file}" "${tmp_ts_file}" "${ts_file}"
            DEPENDS tsconvert ${lan_file} ${tmp_ts_file}
            VERBATIM
    )
endforeach ()
# 将源文件(ts文件)的编译结果输出到指定目录(默认为可执行文件路径)
set_source_files_properties(${TS_FILES} PROPERTIES OUTPUT_LOCATION ${languagesDir})
# 该语句将会构建出lrease指令,在执行build时调用这些指令
qt5_add_translation(QM_FILES ${TS_FILES})
add_custom_target(qmTranslations DEPENDS ${QM_FILES})
add_executable(CrossPlatform
        ${PROJECT_SOURCES}
        ${ICONS_SOURCES}
        ${CMAKE_CURRENT_BINARY_DIR}/resources/languages/language.qrc
)
add_dependencies(CrossPlatform qmTranslations)

4、使用

1、tr()

所有要实现多语言的字符串需要用QObject::tr()或者tr()(该类需继承于QObject且添加Q_OBJECT关键字),它是实现多语言的前提条件。

createButton->setText(QObject::tr("homepage_create"));
class MyClass:public QObject {
Q_OBJECT
public:
    void func(){
        // 如果所在类承于QObject且添加了Q_OBJECT关键字,可简写
        createButton->setText(tr("homepage_create"));
    }
}

2、多语言文案

直接将iOS格式的多语言文件放在resources/languages下(如果是其它格式也放在这个目录下,不过需要自行实现前面的填词脚本)

2、切换为对应语言

代码如下:

QTranslator translator;
// :/代表相对路径。相对路径默认构成为 前缀(qrc文件中prefix字段)+qm相对于qrc的路径
translator->load(":/es.qm");
qApp->installTranslator(mTranslator);

5、问题

由于lupdate指令是针对源文件的,所以每次源文件发生改变,即使没有产生新的多语言文案代码,最终都会调用填词脚本,它会对整个所有的key-value进行遍历,这样就产生了编译期间不必要的时间消耗。经过测试,自己电脑有一万条多语言文案,花费时间大概100ms,应该在可接受范围内。

改进方案:
增加一个宏定义,只有需要进行多语言翻译的时候在进行转换

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,772评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,458评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,610评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,640评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,657评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,590评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,962评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,631评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,870评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,611评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,704评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,386评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,969评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,944评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,179评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,742评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,440评论 2 342

推荐阅读更多精彩内容