以Qt国际化

Qt 3.0.5

主页 | 所有的类 | 主要的类 | 注释的类 | 分组的类 | 函数

Qt国际化

应用的国际化就是使应用成为能被非本国的人使用的过程。

有的情况下,国际化很简单,例如,使一个US应用可被Australian或者British用户理解,工作可能少于几个拼写修正。但是使一个US应用可以被Japanese用户使用,或者一个Korean应用可被German用户使用,就会需要软件不仅工作于不同语言下,还要使用不同的输入技术、字符编码和表达习惯。

也参见Qt语言学家手册。

Step by Step

以Qt来编写多平台国际化的软件是一个和缓的、逐步增加的过程。你的软件可以按下面的几个阶段实现国际化:

对所有用户可见的文本使用QString

既然QString内部就使用Unicode编码,那么使用常见的文本处理手段,就可以透彻地处理世界上的每种语言。同时,既然所有向用户呈现文本的Qt函数都把QString作为参数,也就没有了char*到QString的转换的时间开销。

在“程序员空间”(例如QObject的名称和文件格式文本)的字符串不需要使用QString;传统的char*或者QCString类就够用了。

你不大可能注意到你在使用Unicode;QString和QChar就如同早期版本的传统C中的粗糙的const char*和char。

对所有文字形式的文本使用tr()

无论你的程序在哪里对将会呈现给用户的文本使用了"quoted text",要确保它被QApplication::translate()函数处理过。其实做到这一点只需要使用QObject::tr()。例如,假设LoginWidget是QWidget的一个子类:

    LoginWidget::LoginWidget()
    {
        QLabel *label = new QLabel( tr("Password:"), this );
        ...
    }

这就解决了你可能要写的用户可见的字符串的99%。

如果这些quoted text不是在QObject子类的成员函数中,可以使用一个适当的类的tr()函数,或者直接使用QApplication::translate()函数:

    void some_global_function( LoginWidget *logwid )
    {
        QLabel *label = new QLabel(
                LoginWidget::tr("Password:"), logwid );
    }

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

如果你需要不在函数里的可翻译文本,有两个宏可以帮忙:QT_TR_NOOP()和QT_TRANSLATE_NOOP()。它们仅仅标示出文本,以便于被下面描述的lupdate工具提取。宏扩展为只是文本(没有上下文)。

QT_TR_NOOP()的例子:

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

QT_TRANSLATE_NOOP()的例子:

    static const char* greeting_strings[] = {
        QT_TRANSLATE_NOOP( "FriendlyConversation", "Hello" ),
        QT_TRANSLATE_NOOP( "FriendlyConversation", "Goodbye" )
    };

    QString FriendlyConversation::greeting( int greet_type )
    {
        return tr( greeting_strings[greet_type] );
    }

    QString global_greeting( int greet_type )
    {
        return qApp->translate( "FriendlyConversation",
                                greeting_strings[greet_type] );
    }

如果你使用定义的宏QT_NO_CAST_ASCII编译你的软件,从而关闭了从const char*到QString的自动转换,你很可能会捕捉到你错过的字符串。更多信息参见QString::fromLatin1()。关闭这个转换会使编程有点儿麻烦。

如果你的源码语言使用Latin-1之外的字符集,你会发现QObject::trUtf8()比QObject::tr()更好用,因为tr()依赖于QApplication::defaultCodec(),这使它比QObject::trUtf8()更脆弱。

对于加速键值(Accelerator value)使用QKeySequence()

加速键值,例如Ctrl+Q或者Alt+F,也需要翻译。 如果你的应用给“Quit”直接编码(hardcode)为CTRL+Key_Q,翻译者就不能重载它了。正确的习惯用法是

    QPopupMenu *file = new QPopupMenu( this );
    file->insertItem( tr("&Quit"), this, SLOT(quit()),
                      QKeySequence(tr("Ctrl+Q", "File|Quit")) );

对简单参数使用QString::arg()

对于国际化的文本,在字符串中类似printf()风格的插入参数一般是不好的选择,因为有时候有必要在翻译时改变参数的顺序。不管怎样,QString::arg()函数为参数替换提供了一种简单的途径:

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

创作译本

在应用中通篇使用tr()之后,你就可以开始创作程序中用户可见的文本的译本了。

Qt语言学家手册提供了关于Qt翻译工具、Qt语言学家lupdatelrelease的进一步信息。

Qt应用的翻译过程分为三步:

  1. 运行lupdate,以提取出Qt应用的C++源代码中的可翻译文本,会产生一个给翻译者的信息文件(.ts文件)。该工具识别出tr()结构和上面描述的QT_*_NOOP宏,产生.ts文件(通常每种语言一个)。
  2. 使用Qt语言学家提供.ts文件中源文本的译文。既然.ts文件是XML格式,你也可以手工编辑它们。
  3. 运行lrelease,以从.ts文件中得到只适用于最后使用的轻量级的信息文件(.qm文件)。你可以把.ts文件看成“源文件”,把.qm文件看成“目标文件”。翻译者编辑的是.ts文件,可是你的应用的用户只需要.qm文件。这两种文件都是平台和地区(locale)无关的。

典型地,你将对应用地每个发表版本重复这几步。lupdate工具会尽力重用以前的发表版本的译文。

在你运行lupdate之前,你应该准备一个项目文件。这是一个项目文件(.pro文件)的例子:

    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时,你必须以命令行参数给出项目文件的名称。

在本例中,支持四种语言:Danish、Finnish、Norwegian和Swedish。如果你使用qmake(或者tmake),你一般不需要给lupdate的附加项目文件;只要你加上TRANSLATIONS条目,你的qmake项目文件就会正常工作。

在应用中,你必须使用QTranslator::load()来装载对应用户语言的译文文件,再使用QApplication::installTranslator()来安装它们。

如果你一直使用以前的Qt工具(findtr、msg2qm和mergetr),可以使用qm2ts来转换你以前的.qm文件。

语言学家lupdatelrelease安装在$QTDIR/bin。在Qt语言学家中点击Help|Manual,就可访问用户手册,它包含了让你起步的演示教程。

虽然这些工具提供了生成.qm文件的方便途径,可任何能编写.qm文件的系统也都够用。你可以制做一个应用,以利用QTranslator::insert()把译文加入到QTranslator中,接着再利用QTranslator::save()写出一个.qm文件。 用这种办法可以从任何你选择的源文件中产生译文。

Qt本身包含有大约400个也需要翻译为目标语言的字符串。在$QTDIR/translations下,你会找到French和German的译文文件,也可以作为翻译为其他语言的模板。

典型地,你的应用的main()函数会类似这样:

    int main( int argc, char **argv )
    {
        QApplication app( argc, argv );

        // translation file for Qt
        QTranslator qt( 0 );
        qt.load( QString( "qt_" ) + QTextCodec::locale(), "." );
        app.installTranslator( &qt );

        // translation file for application strings
        QTranslator myapp( 0 );
        myapp.load( QString( "myapp_" ) + QTextCodec::locale(), "." );
        app.installTranslator( &myapp );

        ...

        return app.exec();
    }

编码支持

QTextCodec类和QTextStream中的工具,方便你支持很多对于用户数据的输入输出编码。当应用启动时,机器的地区(locale)决定了处理8-bit数据——例如对于字体选择、文本显示、8-bit文本I/O和字符输入——所用的8-bit编码。

应用有时会需要不同于缺省本地8-bit编码的编码。例如,一个Cyrillic KOI8-R locale(俄罗斯的事实标准locale)的应用 可能需要以ISO 8859-5编码输出Cyrillic。这样的代码可能会是:

    QString string = ...; // some Unicode text

    QTextCodec* codec = QTextCodec::codecForName( "ISO 8859-5" );
    QCString encoded_string = codec->fromUnicode( string );

    ...; // use encoded_string in 8-bit operations

对于把Unicode转换为本地8-bit编码,有一个快捷办法:QStringlocal8Bit()方法返回的就是这样的8-bit数据。另一个有用的快捷办法是utf8()方法,它以8-bit的UTF-8编码返回文本——有趣之处在于, 如果Unicode完全是US-ASCII的话,它可以完全保留Unicode信息,而看起来又是一般的US-ASCII。

至于其他方式的转换,有QString::fromUtf8()和QString::fromLocal8Bit()两个方便的函数,或者通用的代码,按下面从ISO 8859-5 Cyrillic到Unicode所演示:

    QCString encoded_string = ...; // Some ISO 8859-5 encoded text.

    QTextCodec* codec = QTextCodec::codecForName("ISO 8859-5");
    QString string = codec->toUnicode(encoded_string);

    ...; // Use string in all of Qt's QString operations.

理想状态下应该使用Unicode的I/O,因为这最大地提高了世界各地的用户之间的文档兼容性。可实际上,支持所有适当的你的用户为处理已存在的文档而会需要用到的编码,也是有用的。通常,Unicode(UTF16或者UTF8)最适于在任何人之间传递信息,然而在同一语言或者民族群体内部,一种本地标准一般更为合适。 要支持的最重要的编码是由QTextCodec::codecForLocale()所返回的那个,因为这是用户最可能需要用来与其他人和应用(这是local8Bit()所使用的编码器)通讯的一个。

考虑到大多数Unix系统对本地8-bit编码和Unicode之间的转换没有内建支持,可能有必要编写你自己的QTextCodec子类。视紧迫程度而定,可以联系Trolltech的技术支持或者询问qt-interest邮件列表以看看是否已经有别人已经致力于支持某种编码。 一个有用的过渡办法是,可以使用QTextCodec::loadCharmapFile()函数以构造一个数据驱动的编码器,虽然这种办法会有内存和速度上的恶化,尤其是带有动态装载库。关于编写自己的QTextCodec详情,参见QTextCodec主类的文档。

本地化

本地化是适应本地习惯比如日期时间表达方式的过程。 这样的本地化可以使用适当的tr()字符串完成,即使是"magic" words,像这个有些做作的例子所示意的:

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

图象本地化不推荐。请选择清晰的适用于任何地区的图标,而不要依赖于当地的双关语或者引申的暗喻。

系统支持

支持Unicode的操作系统和窗口系统还处于开发初期。底层系统所能得到的支持程度会影响Qt在该平台上所提供的支持,但是以Qt编写的应用一般不必太关心实际限制。

Unix/X11

  • 本地导向的(Locale-oriented)字体和输入法。Qt隐藏了这些,而代之以Unicode输入和输出。
  • 在一些Unix变种的文件系统中的约定,例如UTF-8,正在开发中。所有Qt的文件函数都认可Unicode,但把所有的文件名转换为本地8-bit编码,因为这是Unix的惯例(研究代用的编码,参见QFile::setEncodingFunction())。
  • 文件I/O缺省为本地8-bit编码,在QTextStream中带有Unicode选项。

Windows 95/98/NT

  • Qt提供完整的Unicode支持,包括输入法、字体、剪贴板、拖放和文件名。
  • 文件I/O缺省为Latin-1,在QTextStream中带有Unicode选项。注意,有的Windows程序并不能领会高位在前(big-endian)的Unicode文本文件,即使这是没有更高级的协议时Unicode标准所规定的次序。
  • 不像使用MFC或者一般winlib的程序,Qt程序可以在Windows 95/98和Windows NT之间移植。你不需要不同的二进制码以支持Unicode。

支持更多的输入法

Trolltech并没有那么多资源或者专门技术,可以在Qt里立即加入世界上所有语言的支持。所以我们很期待与有这些专门技术的人一起工作。在下几个次版本号中,我们希望能对你选择的语言加入支持,直到任何人都可以使用Qt和以Qt开发的所有程序,而不管他们使用什么语言。

单字节编码(European Latin-1和KOI8-R等)和多字节编码(East Asian EUC-JP等)的语言都已支持。 对于那些“复杂”的编码——需要从右至左输入或者复杂字符组成的编码(例如Arabic、Hebrew和Thai script)——已经实现,但是Indic script的范畴(Hindi、Devanagari、Bengali等)还在开发中。现在的情况是:

编码状态
Windows上的所有编码 本地的编码总是支持。
ISO标准编码 ISO 8859-1、 ISO 8859-2、 ISO 8859-3、 ISO 8859-4、 ISO 8859-5、 ISO 8859-7、 ISO 8859-9和 ISO 8859-15 完全支持。
KOI8-R 完全支持。
eucJP、JIS和ShiftJIS 完全支持。在X11上对于XIM协议使用eucJP,在Japanese Windows NT上使用IME Windows NT。Serika Kurusugawa和其他人正在介入这个工作。kinput2是X11上测试过的输入法。
eucKR 支持。Mizi Research正在介入这个工作。hanIM是测试过的输入法。
Big5 Qt包含着一个由Ming Che-Chuang开发的Big5编解码器。正在xcin (2.5.x) XIM server上展开测试。
eucTW 外部开发中。

关于Qt中对不同的writing system的支持,更多信息参见关于writing systems的文档

如果你有兴趣参与已有的成果,或是支持以上提到的之外的新编码,你的工作可以被考虑包括在Qt官方发布中,或者只是与你的应用打包在一起。

最后,我们希望能帮助Unix成为面向Unicode(Unicode-oriented),正如Windows正在做的那样。这就意味着,在字体服务器中使用新的进展而带来的更好的字体支持,例如xfsftxfstt和x-tt;同时还有UTF-8(一种Unicode编码)文件名,比如使用Solaris 7上的Unicode支持

关于X11上的Locale的注意事项

对于有的locale,很多Unix发布版只包括部分支持。例如,如果你有/usr/share/locale/ja_JP.EUC目录,这并不一定意味着你可以显示Japanese文本;你还需要JIS编码的字体(或者Unicode字体),以及/usr/share/locale/ja_JP.EUC目录,才能够达到目的。要达到最好的结果,使用你的系统提供商那里来的完全locale。

有关的Qt类

这些类同国际化Qt应用有关。

QEucJpCodec从和到EUC-JP字符集的转换
QEucKrCodec从和到EUC-KR字符集的转换
QGb18030Codec从和到Chinese GB18030/GBK/GB2312编码的转换
QGbkCodec从和到Chinese GBK编码的转换
QHebrewCodec从和到visually ordered Hebrew的转换
QJisCodec从和到JIS字符集的转换
QSjisCodec从和到Shift-JIS的转换
QTextCodec文本编码之间的转换
QTextDecoder基于状态(State-based)的解码器
QTextEncoder基于状态的编码器
QTranslator文本输出的国际化支持
QTranslatorMessageTranslator的信息及其属性
QTsciiCodec从和到Tamil TSCII编码的转换


Copyright © 2002 Trolltech Trademarks 译者:farfareast
Qt 3.0.5版