QT是支持多语言的,能够支持不同语言的动态切换,不同语言对应的编码格式转换,不同语言字体使用不同字库进行显示。
编码
QT对于字符串的处理使用了基本类型QString,它将所有传递给它的字符串或者字符使用UTF16格式存储起来。 那么问题来了,对于中文"你好"的编码格式可能是GBK或者UTF8或者UTF16等,对于英文则默认使用UTF8(ASCII兼容)。 因此需要将这些字符进行编码格式化转换。 格式转换大概会出现在下面几个地方:
1、代码编写时
QString message="你好".
在不同的编辑器中输入"你好",对应的编码可能不一样。使用QtCreator可以设置编辑器默认使用的编码格式,我喜欢使用UTF8格式(在项目的属性设置对话框中)。
代码中"你好"这时候实际上和"hello"没什么区别了,只是一个数组对应{0x11,0x12,0x13...};
2、char*和QString之间转换 QByteArray和QString之间转换
上面代码中,对QString进行赋值会调用到QString::operator(const QString&)函数,中间会将char[]转换成QString,调用QString(const char*)。 在这里它需要将char*中的字符串的编码格式进行转换了。现在问题来了,QString不知道char* 中字符串的编码格式。(编码格式那么多,不可能挨个去试)。 这时候使用
QTextCodec::setCodecForCStrings(QTextCodec::setCodecForCStrings("UTF-8"));
它就告诉我们怎么在QString和char*之间如何进行编码转换。
对于QByteArray和QString之间的转换也是一样的。
3、tr宏
对于多语言来说,需要使用到tr,这个函数的定义:
QString QObject::tr ( const char * sourceText, const char * disambiguation = 0, int n = -1 )
4、业务流程需要编码转换
现在"你好"以及以UTF16的编码格式存储在message中了,若是需要将这个字符串写入一个记事本文件中并且希望用户打开文件后看到的不是乱码?
在这里,我们输出的字符串可能是中文,且国内大部分操作系统中编辑器默认使用的编码格式是GBK,所以:
QTextCodec::codecForName("GBK")->fromUnicode(message)
上面代码返回的就是"你好"的GBK编码数据,将它写入文件后,字符才能正常显示。
当然,若是使用"BIG5“编码格式的编辑器的话,那么上面的代码就需要改为:
QTextCodec::codecForName("GBK")->fromUnicode(message)
翻译
对于需要进行翻译的文字,需要使用tr("")进行包裹,例如QString c = tr("汉字"),在不同的语言设置中,返回不同的字符。这个函数的源码:
static inline QString tr(const char *s, const char *c = 0) \
{ return staticMetaObject.tr(s, c); } \
static inline QString trUtf8(const char *s, const char *c = 0) \
{ return staticMetaObject.trUtf8(s, c); } \
static inline QString tr(const char *s, const char *c, int n) \
{ return staticMetaObject.tr(s, c, n); } \
static inline QString trUtf8(const char *s, const char *c, int n) \
{ return staticMetaObject.trUtf8(s, c, n); }
QString QMetaObject::tr(const char *s, const char *c) const
{
return QCoreApplication::translate(d.stringdata, s, c, <span style="color:#ff0000;">QCoreApplication::CodecForTr</span>);
}
QString QCoreApplication::translate(const char *context, const char *sourceText,
const char *disambiguation, Encoding encoding)
{
return translate(context, sourceText, disambiguation, encoding, -1);
}
QString QCoreApplication::translate(const char *context, const char *sourceText,
const char *disambiguation, Encoding encoding, int n)
{
QString result;
if (!sourceText)
return result;
if (self && !self->d_func()->translators.isEmpty()) {
QList<QTranslator*>::ConstIterator it;
QTranslator *translationFile;
for (it = self->d_func()->translators.constBegin(); it != self->d_func()->translators.constEnd(); ++it) {
translationFile = *it;
result = <span style="color:#ff0000;">translationFile->translate(context, sourceText, disambiguation, n);</span>
if (!result.isEmpty())
break;
}
}
if (result.isEmpty()) {
#ifdef QT_NO_TEXTCODEC
Q_UNUSED(encoding)
#else
if (encoding == UnicodeUTF8)
result = QString::fromUtf8(sourceText);
else if (QTextCodec::codecForTr() != 0)
result = QTextCodec::codecForTr()->toUnicode(sourceText);
else
#endif
result = QString::fromLatin1(sourceText);
}
replacePercentN(&result, n);
return result;
}
注意上面调用translate时会传入QCoreApplication::CodecForTr,它实际上控制着翻译不成功时如何将char*转为QString。
从上面可以看到,最终调用的是QTranslator::translate,它实际上调用的函数是:QString QTranslatorPrivate::do_translate(const char *context, const char *sourceText,
const char *comment, int n) const
{
if (context == 0)
context = "";
if (sourceText == 0)
sourceText = "";
if (comment == 0)
comment = "";
if (!offsetLength)
return QString();
/*
Check if the context belongs to this QTranslator. If many
translators are installed, this step is necessary.
*/
if (contextLength) {
quint16 hTableSize = read16(contextArray);
uint g = elfHash(context) % hTableSize;
const uchar *c = contextArray + 2 + (g << 1);
quint16 off = read16(c);
c += 2;
if (off == 0)
return QString();
c = contextArray + (2 + (hTableSize << 1) + (off << 1));
for (;;) {
quint8 len = read8(c++);
if (len == 0)
return QString();
if (match(c, context, len))
break;
c += len;
}
}
size_t numItems = offsetLength / (2 * sizeof(quint32));
if (!numItems)
return QString();
int numerus = 0;
if (n >= 0)
numerus = numerusHelper(n, numerusRulesArray, numerusRulesLength);
for (;;) {
quint32 h = elfHash(QByteArray(sourceText) + comment);
const uchar *start = offsetArray;
const uchar *end = start + ((numItems-1) << 3);
while (start <= end) {
const uchar *middle = start + (((end - start) >> 4) << 3);
uint hash = read32(middle);
if (h == hash) {
start = middle;
break;
} else if (hash < h) {
start = middle + 8;
} else {
end = middle - 8;
}
}
if (start <= end) {
// go back on equal key
while (start != offsetArray && read32(start) == read32(start-8))
start -= 8;
while (start < offsetArray + offsetLength) {
quint32 rh = read32(start);
start += 4;
if (rh != h)
break;
quint32 ro = read32(start);
start += 4;
QString tn = getMessage(messageArray + ro, messageArray + messageLength, context,
sourceText, comment, numerus);
if (!tn.isNull())
return tn;
}
}
if (!comment[0])
break;
comment = "";
}
return QString();
}
do_translate将从offsetArray指向的中查找对应的翻译字符串,中间涉及的存储结构以及如何查找和Hash表相关。
使用时步骤:
1、将所有需要翻译的字符使用tr("")包裹起来
2、在项目文件中定义TRANSLATIONS,指定翻译文件生成路径
3、使用lupdate生成ts文件
4、使用Linguist对ts文件进行翻译
5、使用lrelease将ts文件转换为qm文件
6、将qm文件部署到设备上
7、代码中添加对qm文件的加载,根据不同的业务加载不同的文件:
translatorpass = new QTranslator();
//removeTranslator(translatorpass); 如果之前添加过,需要先删除
translatorpass->load("en.ts","/usr/local/translation");
字库
除了编码和翻译问题,还需要解决字库部署问题。例如,在支持中文的基础上,需要添加对阿拉伯语的支持,除了涉及翻译和编码外,还需要部署支持阿拉伯的字库文件。
不然当切换到阿拉伯下,整个界面对于阿拉伯语的显示就有问题。
当然,也可以使用其他方式进行部署,例如指定fontdir的路径,然后在fontdir中定义字库名字。
业务逻辑实现时,需要动态切换字库,则需要调用
void QApplication::setFont(const QFont &font, const char *className)
它会指定整个应用的默认字库
对于整个系统支持的字库文件可以使用下面程序进行查询:
QFontDatabase database;
QStringList families=database.families();
文字输入/显示方向
上面提到的阿拉伯语在显示文字是从右往左,这个实现比较简单。
程序中声明
QApplication::tr("QT_LAYOUT_DIRECTION");
然后在ts文件中将它翻译成"RTL",即可。
因为在文字显示过程中,有这样一句代码:
static bool qt_detectRTLLanguage()
{
return force_reverse ^
(QApplication::tr("QT_LAYOUT_DIRECTION",
"Translate this string to the string 'LTR' in left-to-right"
" languages or to 'RTL' in right-to-left languages (such as Hebrew"
" and Arabic) to get proper widget layout.") == QLatin1String("RTL"));
}