前言
本篇博文主要针对一下几个问题展开讨论:
- QT_TR_NOOP和QT_TRANSLATE_NOOP 以及QObject::tr() 的区别。
- 正常加载.qm文件,但无法正常翻译字符。
- 没有继承QObject的类如何使用国际化功能
框架结构解析
.ts .qm文件
.ts文件本质上是一个xml文件,它保存了以下信息:
-
需要翻译的字符串
-
字符串的上下文标记(context),一般是类名,也可以自定义
-
字符串所在文件,以及在文件中所在的行号
-
字符串被翻译之后的文本
其中1、2很重要,这里先按下不表。
.qm文件是将.ts文件中的一些关键信息发布为二进制之后的文件,主要是为了节省空间,这没啥好说的
在代码中需要用QT_TR_NOOP、QT_TRANSLATE_NOOP和QObject::tr()标记需要翻译的字符串文本,这样
Linguist Tools才能收集这些文本的信息生成.ts文件。
QCoreApplication::translate
为了方便理解下面的内容,需要知道QT中有这么一个函数
QString QCoreApplication::translate(const char *context, const char *sourceText, const char *disambiguation = nullptr, int n = -1)
这个函数用于查找最近安装的.qm文件中的字符串翻译文本并返回。
context参数为字符串所在的上下文,说人话就是一个标识,用于标记字符串文本在哪里被使用,通常是类的类名,也可以自定义。
sourceText参数是需要翻译的字符串。
disambiguation 参数也是一个标记,用于同一个上下文中,出现了多个相同的需要翻译的字符串,用于区分,不过一般没有那么多道道,都是使用默认参数。
n 用于与字符串中的%n结合,生成新的字符串,这个与QString中的arg()函数用法类似,可以自行翻阅文档。
从该函数以及.ts文件中可以窥得QT这套框架的一角,可以猜测,QT中通过某种方法将.ts文件中的收集的字符串、翻译文本,类名(context)信息建立了一个可以快速查找的表,在文本需要翻译的时候,调用translate来查询翻译文本的内容,实现多种语言的功能。具体的实现方法请继续往下看。
QT_TR_NOOP、QT_TRANSLATE_NOOP的区别
先来介绍QT_TR_NOOP和QT_TRANSLATE_NOOP,这是两个宏定义,在QT源码中可以找到他们的定义:
#define QT_TR_NOOP(x) x #define QT_TRANSLATE_NOOP(scope, x) x
可以看到,这两个宏定义其实什么也没有做,直接是标记的字符串本身。
在QT文档中可以看到这两个宏的介绍,QT_TR_NOOP 仅用于标记文本,lupdate通过这些标记来生成.ts文件,这个宏的作用仅止步于此。QT_TRANSLATE_NOOP则是在这个基础上增加了自定义context标记的功能,传入的第一个参数scope会在lupdate收集文本数据时作为context使用。
这两个宏一般用于延迟翻译,即在使用这两个宏标记了要翻译的字符串数据之后,再在真正要使用到翻译文本的地方调用QCoreApplication::translate来实现翻译的功能。
tr函数
在QObject源码中可以找到这样的声明:
static QString QObject::tr(const char *sourceText, const char *disambiguation = nullptr, int n = -1)
这个函数和QCoreApplication::translate类似,只是少了context参数的传入。因为是一个静态函数,如果不是在QObject类的派生类中,可以显示的使用QObject::tr来调用。
但是我们真正在使用的tr函数则不一定是在QObject类中的。
需要先了解Q_OBJECT宏,这个宏熟悉qt的朋友一定知道,它提供了很多qt元编译器需要的内容,这个宏中的内容很多,这里不展开,只说其中一个,就是QT_TR_FUNCTIONS,它的展开是这样的:
static inline QString tr(const char *s, const char *c = nullptr, int n = -1) \ { return staticMetaObject.tr(s, c, n); }
也就是说在使用Q_OBJECT宏的时候,已经在类中定义了一个静态的tr函数了,如果在类中直接调用tr,调用到的就是这个函数。
staticMetaObject的类型是一个QMetaObject,然而在QT的元编译器生成MOC文件的时候,在QMetaObject对象中是保存了当前类的名称的,说道这里你是不是恍然大悟了?
虽然我并没有在源码里找到QMetaObject::tr的实现,但是可以肯定的是它一定是使用类名调用QCoreApplication::translate来查找翻译文本的内容。
tr()函数与QT_TR_NOOP和QT_TRANSLATE_NOOP的区别就在于tr会实时的返回翻译文本的内容,不用在自己去调用QCoreApplication::translate来实现翻译的功能。
解答开篇的问题
QT_TR_NOOP和QT_TRANSLATE_NOOP 以及QObject::tr() 的区别?
三者皆可用于标记字符串文本,然后生成.ts文件,但是QT_TR_NOOP和QT_TRANSLATE_NOOP只有标记的功能,不能实现翻译内容。tr()函数可以实时的返回翻译文本的字符串实现翻译功能。
正常加载.qm文件,但无法正常翻译字符
1. 没有继承QObject 类
2. 没有使用Q_OBJECT 宏,在tr()函数的介绍中提到的问题,要实现翻译要依赖于Q_OBJECT宏中声明的tr函数,并且需要用到元编译器生成的类名,如果context无法正确的传递,就无法返回正确的翻译文本内容。
没有继承QObject的类如何使用国际化功能
这种情况需要使用延迟翻译,即使用QT_TR_NOOP和QT_TRANSLATE_NOOP标记文本后,在真正使用文本的地方调用QCoreApplication::translate来实现翻译,官方给出的示例代码:
QT_TRANSLATE_NOOP示例
static const char *greeting_strings[] = { QT_TRANSLATE_NOOP("FriendlyConversation", "Hello"), QT_TRANSLATE_NOOP("FriendlyConversation", "Goodbye") }; QString FriendlyConversation::greeting(int type) { return tr(greeting_strings[type]); } QString global_greeting(int type) { return qApp->translate("FriendlyConversation", greeting_strings[type]); }
QT_TR_NOOP示例
QString FriendlyConversation::greeting(int type) { static const char *greeting_strings[] = { QT_TR_NOOP("Hello"), QT_TR_NOOP("Goodbye") }; return tr(greeting_strings[type]); }
在QT_TRANSLATE_NOOP示例中使用QCoreApplication::translate来实现对内容的翻译,这种使用方法就不需要对QObject继承,但是如果每次都按照官方示例这么去写是要折磨死人的。因为QT_TRANSLATE_NOOP只是做文本标记的功能,所以可以用这一点,修改QT_TRANSLATE_NOOP的宏定义,来实现实时翻译的功能。
#define QT_TRANSLATE_NOOP(scope, x) QCoreApplication::translate(scope,x)
在使用QT_TRANSLATE_NOOP之前将宏定义修改为上述代码,就可以在代码中像使用tr函数一样了,得到的效果也是一样的。
如果想使用QT_TR_NOOP实现这样的功能会更麻烦,因为它默认使用类名来作为translate函数的context参数,在宏定义中暂时没有办法或许到准确的类名,所以只能手动去调用translate函数。
关于这个问题,再扩展一种情况,就是当继承了QObject,但是不想使用Q_OBJECT宏的时候,想实现多语言翻译的功能
对于这种情况下,QT提供了Q_DECLARE_TR_FUNCTIONS宏,它的定义是
#define Q_DECLARE_TR_FUNCTIONS(context) \ public: \ static inline QString tr(const char *sourceText, const char *disambiguation = Q_NULLPTR, int n = -1) \ { return QCoreApplication::translate(#context, sourceText, disambiguation, n); } \ QT_DECLARE_DEPRECATED_TR_FUNCTIONS(context) \
可以看到这个宏为类添加了一个tr函数,tr函数的实现就是去利用传入的类名,去调用了translate函数。不想使用Q_OBJECT宏时可以使用这个宏,一样可以实现使用tr函数来翻译文本的功能。
文章评论