Qt国际化(Q_DECLARE_TR_FUNCTIONS() 宏给非Qt类添加翻译支持,以前没见过QTextEncoder和QTextDecoder和QLibraryInfo::location()和QEvent::LanguageChange)

时间:2020-12-05 18:44:59

Internationalization with Qt

应用程序的国际化就是使得程序能在国际间可用而不仅仅是在本国可用的过程。

Relevant Qt Classes andAPIs

以下的类支持Qt的国际化。

QTextCodec

QTextDecoder

QTextEncoder

QTranslator

QLocale

Languages and WritingSystems

有时,国际化是比较简单的,例如,把美国的应用程序让澳大利亚或英国的用户可访问,只需要简单的改变拼写。但是,把美国的应用程序对日本用户可用,或者韩国的应用程序对德国人可用,不仅仅需要软件操作不同的语言,还需要使用不同的输入技术,不同的字符编码和显示规则。

对于开发者,Qt尽可能的把国际化简单化。Qt内置提供的所有的输入控制和文本绘制方法支持所有的语言。内置的字体引擎能同时正确和精美的显示来自各种不同的输入系统的文本。

Qt支持大多数现在用的语言,特别是:

·        所有的东亚国家的语言(中文,日文和韩文)

·        所有的西方国家的语言 (使用拉丁文)

·        阿拉伯语

·        斯拉夫字母语言

·        希腊语

·        希伯来语

·        泰国和老挝语

·        所有不需要特殊处理的 Unicode 6.2 编码的脚本

·        孟加拉语

·        缅甸语

·        梵文

·        吉吉拉特语

·        果鲁穆奇语

·        坎那达语

·        高棉语

·        马拉雅拉姆语

·        泰米尔语

·        泰卢固语

·        藏语

以上列表都是被支持和在所有的平台上都能工作的,只要系统有渲染这些安装的输入系统的字体。

在Windows, Linux 和 Unix系统,使用字体配置,下面的语言也能支持:

·        迪维希语

·        叙利亚语

·        西非书面语言

在Mac OS系统,下面的语言也被支持:

·        奥里雅语

·        僧伽罗语

许多的书写系统表现出特殊的功能:

·        特殊的自动换行形式:一些亚洲语言字之间没有空白,自动换行可以发生在任何字符的后面,比如中文,日文和韩文,或者在逻辑词边界的泰语。

·        双向书写:阿拉伯语和希伯来语是从右到左书写的,除了数字和嵌入的英文单词是从左到右书写之外。

·        非间距或变音符号(欧洲语言的口音或变音符号):一些语言比如越南语大量的使用这些符号,并且一些字符同时有超过一个标记来表明读音。

·        连字:在特殊的语境,一些成对的字符被一些字结合的连字替换。常见的例子是,在美国和欧洲书记中使用的 fl 和fi的连字。

Qt视图照顾到以上列出的特殊的特性,你通常不用担心这些特性,只要你使用Qt 的输入控制和Qt的显示控制。

支持这些书写系统对程序员是透明的,完全被封装在Qt's text engine. 。这意味着你在用特定的语言时不需要知道任何书写系统信息,除了以下几点:

·        QPainter::drawText(int x, inty, const QString &str)把绘制的字符串的左边缘在给定的x,y参数的位置。这通常会给你左对齐的字符串。阿拉伯语和希伯来语的应用程序的字符串通常是右对齐的,所以对于这些语言需要用接收QRect的drawText() 版本,这样才与语言一直。

·        当你自己写文本输入控制时,用QTextLayout。在某些语言,宽度和形状会根据周围的字符而改变,这就是QTextLayout所考虑的。写输入控制通常需要知道一种特定的脚本使用方法的知识。通常最简单的方法是继承QLineEdit 或 QTextEdit.。

Step by Step

用Qt写跨平台的国际化软件是一个温和,渐进的过程。在一些的阶段,你的软件可以变得国际化:

UseQString for All User-Visible Text

由于 QString 内部使用了Unicode编码,世上的所有语言都能用熟悉的文本处理操作进行透明的处理。因为所有向用户显示文本的Qt函数都是以Qstring作为参数的,所有不存在从 char * 转换到 QString的开销。

在程序员空间的字符串(如QObject 名和文件格式文本)不需要用Qstring,传统的char * 或QByteArray 就足够了。

你可能没注意到你用Unicode编码:Qstring和 Qchar 就像用传统的C中的const char * 和char 一样那么简单。

当进行隐式转换为QString是,源码中的char *被假定是用UTF-8编码。如果你的C字面值字符串用的是不同的编码,用 QString::fromLatin1() 或QTextCodec把它转换为Unicode 编码的Qstring。

Usetr() for All Literal Text

如果你的程序有用“”引用的向用户显示的文本,确保用QCoreApplication::translate() 函数来处理它。要实现这一过程基本上都需要用 QObject::tr().。例如:假设LoginWidget 是QWidget:的子类:

LoginWidget::LoginWidget()
{
    QLabel*label =newQLabel(tr("Password:"));
    ...
}

这个占了你可能写的99%的用户可见字符串。

如果引用的文本不在 QObject 子类的成员函数中,用一个适当的类的tr()函数或直接用 QCoreApplication::translate() 函数。

void some_global_function(LoginWidget *logwid)
{
    QLabel*label =newQLabel(
                LoginWidget::tr("Password:"), logwid);
}
 
void same_global_function(LoginWidget *logwid)
{
    QLabel*label =newQLabel(
                qApp->translate("LoginWidget","Password:"), logwid);
}

如果你需要翻译的文本完全在函数的外部,那么可以用这两个宏:QT_TR_NOOP() 和QT_TRANSLATE_NOOP().。它们仅仅为以下介绍的 lupdate 工具提取的文本进行标记,宏只是扩展文本。

QT_TR_NOOP()的例子:

QString FriendlyConversation::greeting(int type)
{
    staticconst char *greeting_strings[]= {
        QT_TR_NOOP("Hello"),
        QT_TR_NOOP("Goodbye")
    };
    return tr(greeting_strings[type]);
}

QT_TR_NOOP()的例子:

staticconst 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_NO_CAST_FROM_ASCII定义来编译软件禁止从const char * 到Qstring 的转换,你就可能捕获任何漏掉的字符串。

UseQKeySequence() for Accelerator Values

快捷键值如 Ctrl+Q 或 Alt+F也需要翻译。如果你在程序中为“quit”退出用硬编码 Qt::CTRL + Qt::Key_Q ,转换器就不能重写它们。正确的做法是:

exitAct =newQAction(tr("E&xit"),this);
exitAct->setShortcuts(QKeySequence::Quit);

UseQString::arg() for Dynamic Text

QString::arg() 提供简单的替换参数的方法:

void FileCopier::showProgress(int done, int total,
                              constQString&currentFile)
{
    label.setText(tr("%1 of %2 files copied.\nCopying: %3")
                  .arg(done)
                  .arg(total)
                  .arg(currentFile));
}

在有的语言中参数的顺序可能需要改变,实现这个只需要改变参数中%的顺序就好。例如:

QString s1 ="%1 of %2 files copied. Copying: %3";
QString s2 ="Kopierer nu %3. Av totalt %2 filer er %1 kopiert.";
 
qDebug() << s1.arg(5).arg(10).arg("somefile.txt");
qDebug() << s2.arg(5).arg(10).arg("somefile.txt");

生成了正确的英语和挪威语的输出:

5 of 10 files copied. Copying: somefile.txt
Kopierer nu somefile.txt. Av totalt 10 filer er 5 kopiert.

ProduceTranslations

一旦你在整个程序中用了tr(),你可以在你的程序中开始对用户可见文本生成翻译。

翻译Qt程序需要三个步骤:

1.     运行lupdate工具 来提取Qt程序C++源代码中可以翻译的文本,生成给翻译器的一个消息文件(TS文件)。该工具识别tr()和以上提到的宏 QT_TR*_NOOP()并生成TS文件。

2.    用Qt Linguist.为TS文件中的源文本提供翻译,因为TS文件是XML格式,你也可以手工进行编辑。

3.    运行lrelease 从TS文件中得到一个轻型的消息文件(QM文件),只适合最终用途。想象TS文件是源文件,QM文件是目标文件。翻译器编辑TS文件,但是程序的用户只需要QM文件。这些文件都是独立于平台和地域的。

典型的,你每次发布程序都需要重复这些步骤。Linguist 工具尽可能重用之前的版本的翻译。

在运行lupdate 之前,你应该准备工程文件。例如:

HEADERS         = funnydialog.h \
                  wackywidget.h
SOURCES         = funnydialog.cpp \
                  main.cpp \
                  wackywidget.cpp
FORMS           = fancybox.ui
TRANSLATIONS    = superapp_dk.ts \
                  superapp_fi.ts \
                  superapp_no.ts \
                  superapp_se.ts

当你运行 lupdate 或 lrelease,时,你必须把工程文件名作为命令行的参数。

在这个例子中,支持四种语言:丹麦,芬兰,挪威和瑞典。如果你用qmake,lupdate通常不需要额外的工程文件。一旦你添加了TRANSLATIONS条目qmake工程文件就能正常工作。

在你的程序中,必须为用户的语言适当的调用QTranslator::load() 装载翻译文件,并用QCoreApplication::installTranslator()进行安装。

linguistlupdate 和 lrelease被安装到Qt安装的根目录中的bin子目录。点击QtLinguist 的Help|Manual进入用户手册。它包含一个开始的教程。

Qt本身包含超过400个需要翻译成目标语言的字符串。你会找到很多个翻译文件,以及在QtTranslations 模块中翻译成其他语言的模板。

典型的,你的程序的main()函数看起来像这样:

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
 
    QTranslator qtTranslator;
    qtTranslator.load("qt_"+QLocale::system().name(),
            QLibraryInfo::location(QLibraryInfo::TranslationsPath));
    app.installTranslator(&qtTranslator);
 
    QTranslator myappTranslator;
    myappTranslator.load("myapp_"+QLocale::system().name());
    app.installTranslator(&myappTranslator);
 
    ...
    return app.exec();
}

注意:用 QLibraryInfo::location() 来定位Qt translations。开发者应该在运行时传递QLibraryInfo::TranslationsPath 到这个函数而不是用程序的环境变量QTDIR来请求该该路径。

Supportfor Encodings

QTextCodec类和 QTextStream工具很容易的支持许多用户数据的输入输出编码。当程序启动时,当处理外部的8位数据,程序的语言环境奖觉得使用的8位编码。QTextCodec::codecForLocale()返回一个可以用来转换本地编码和Unicode的解码器。

程序偶尔需要除了默认8位编码之外的编码。例如,一个程序在 Cyrillic KOI8-R地区可能需要输出 Cyrillic 的ISO 8859-5 编码;可能的代码如下:

QString string =...; // some Unicode text
 
QTextCodec*codec =QTextCodec::codecForName("ISO 8859-5");
QByteArray encodedString = codec->fromUnicode(string);

把Unicode转换为本地的8位编码,一个简便的方法是: QString::toLocal8Bit() 函数返回这样的8位编码数据。另外的捷径是 QString::toUtf8(), 返回8位的UTF-8编码的文本。这个很好的保存了Unicode 信息,如果文本完全是ASCII看起来像纯的ASCII文本。

其他的转换方法,有QString::fromUtf8() 和 QString::fromLocal8Bit()方便的函数,或者是通常代码,从ISO 8859-5 Cyrillic 转换到 Unicode的演示:

QByteArray encodedString =...; // some ISO 8859-5 encoded text
 
QTextCodec*codec =QTextCodec::codecForName("ISO 8859-5");
QString string = codec->toUnicode(encodedString);

Unicode I/O 应用来使全世界范围用户的文档可移植性最大化。由于用户可能需要处理已经存在的文档,有时它也需要支持其他编码。需要支持的最重要的额外编码就是QTextCodec::codecForLocale(),返回的。因为它是用户最可能用来与其他人或应用程序交互的。

Qt支持大多数本地最经常使用的编码。

有时对于不怎么经常使用的编码你可能需要写自己的QTextCodec子类。如果比较紧急,可以联系Qt技术支持团队或在Qt兴趣列表里找是否有人已经实现了对该编码的支持。

Localize

本地化是适应本地规则的过程,例如显示数据和时间用本地的首选格式。这样的本地化可以使用适当的tr()字符串达到。

void Clock::setTime(const QTime &time)
{
    if (tr("AMPM") =="AMPM") {
        // 12-hour clock
    } else {
        // 24-hour clock
    }
}

在例子中,对于美国,判断为真并会进入12小时的分支。但是在欧洲翻译的结果会是其他所以进入23小时分支。

使用 QLocale 类对数字,日期,货币字符串进行本地化。

不建议本地化图片,选择适合所有地方的清晰的图标,而不是依据本地的双关语或伸展隐喻。例外的是向左向右图片对于阿拉伯和希伯来语本地化需要进行翻转。

Dynamic Translation

一些应用程序如Qt Linguist,,当它们还在运行时必须支持转到用户设置的语言。要让Widgets意识到转换到安装的 Qtranslators,重新实现widget的 changeEvent()函数检查是否是 LanguageChange事件,并用tr()函数更新显示文本。例如:

void MyWidget::changeEvent(QEvent *event)
{
    if (e->type() == QEvent::LanguageChange) {
        titleLabel->setText(tr("Document Title"));
        ...
        okPushButton->setText(tr("&OK"));
    } else
        QWidget::changeEvent(event);
}

其他所有的事件通过调用默认实现的函数。

安装的translators 列表可能随着 LocaleChange事件相应的改变,或应用程序可以提供一个接口运行用户改变当前的程序语言。

QWidget子类的默认事件处理响应 QEvent::LanguageChange事件,必要时调用这个函数。

当用 QCoreApplication::installTranslator() 函数安装新的一个翻译时会发出LanguageChange事件。此外,其他程序组件也可以强制widget通过发出LanguageChange事件来更新它们自己。

Translating Non-Qt Classes

有时有必要对不是继承 QObject或用Q_OBJECT 宏的类中的字符串支持国际化开启翻译功能。由于Qt在运行时翻译字符串基于它们关联的类和 lupdate 在源码中查找可以翻译的字符串,非Qt类必须用一种机制可以提供这些信息。

一种方法是用Q_DECLARE_TR_FUNCTIONS() 宏给非Qt类添加翻译支持:

class MyClass
{
    Q_DECLARE_TR_FUNCTIONS(MyClass)
 
public:
    MyClass();
    ...
};

这使得该类可以用tr()函数翻译与该类相关联的字符串和使得lupdate 可以在源码中查找可以翻译的字符串。

另外的方法:在特定的上下午中可以调用 QCoreApplication::translate() 函数,而且该函数可以被lupdate 和 Qt Linguist.识别。

System Support

在Qt运行的一些操作系统和windows系统仅仅是有限的支持Unicode。在下面的系统的支持的级别对Qt在这些平台上的支持有一定的影响,即使通常Qt程序不需要太关心平台限制:

Unix/X11

n  语言环境导向的字体和输入方法,Qt隐藏了这些并提供了Unicode 的输入输出。

n   文件系统约定如 UTF-8如今在Unix大部分变体默认使用。所有的Qt函数都接受Unicode,但是把文件名转换为本地的8位编码,这就是Unix的约定。

n   文件的IO默认为当地的8位编码,在QTextStream与Unicode选项。

n  一些老的Unix发布版本仅仅包含支持一些语言环境。例如,如果你有一个/usr/share/locale/ja_JP.EUC 目录,这不意味着可以显示日文,你必须有安装的日文字体并且 /usr/share/locale/ja_JP.EUC 必须是完整的。为了达到最好的效果,从系统供应商那里获取完整的语言环境。

Linux

Qt提供全部的Unicode 支持,包括输入方法,字体,剪贴板和拖放。

n   在所有现代的Linux发布版文件系统通常用UTF-8编码,而且不会有问题。文件IO默认为UTF-8。

Windows

n  Qt提供全部的Unicode 支持,包括输入方法,字体,剪贴板,拖放和文件名。

n  文件IO默认为Latin1,与在  QTextStream的Unicode选项。注意,一些windows程序不理解大端模式的Unicode 文本文件即使这是在没有高级别协议的Unicode标准规定顺序。

http://blog.csdn.net/hai200501019/article/details/9192967