zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    MySQL8.0设置远程访问权限
    MySQL创建用户与授权
    input lable水平对齐
    jquery datagrid加载后仅选定第一行
    jquery隐藏按钮
    C# 添加excel批注
    在ashx中使用Server对象
    jquery 获取datagrid行数
    弹出窗体中加载页面
    【MySQL】MySQL零碎积累
  • 原文地址:https://www.cnblogs.com/lsgxeva/p/12557656.html
Copyright © 2011-2022 走看看