码迷,mamicode.com
首页 > 其他好文 > 详细

Qt 文字处理

时间:2015-08-20 12:56:52      阅读:1205      评论:0      收藏:0      [点我收藏+]

标签:

internationalization and localization

旨在使应用程序适用于不同的语言, 不同的区域文化, 不同目标市场的技术需求。

internationalization  是指程序可以在不改变设计的前提下适用于多种语言和地区; localization 是指让针对全球的程序通过添加特定的区域化组件(如日期、时间和数字格式)和翻译文字来应用于特定的地区。

    在一些案例中国际化可能是非常简单的, 比如把一个 美语版的程序翻译为澳大利亚版本, 可能只需要一些拼写上的改动即可。 但是如果把它翻译为日本版, 或者把一个韩文应用翻译为德文版,不仅仅需要让程序改变语言, 还需要使用不同的输入技术, 字符编码, 以及外观的改变。

    Qt 尽量减轻开发者的国际化工作。 几乎所有语言都提供了内建的输入组件和文字绘制方法。 内建的字体引擎可以在不同的书写系统中渲染出正确的、漂亮的文字。

Qt 支持的语言可以参考 Qt Mannual-Internationalization with Qt  所述。

    很多语言都有自己的特性。

  •     换行规则:一些亚洲文字的词素之间没有空格。换行可能发生于任意一个字符之后(特殊情况下) 如中文,日文, 韩文; 也有可能发生于一个逻辑词素界限之后, 如泰语。

  •     双向书写: 阿拉伯语和希伯来语自右至左书写,而文字中出现的英文、数字等却还是自左至右。

  •      标记:  非空格标记(词素结束)和额外的标记(部分欧洲语系中有音标等) 一些语系中广泛使用这些标记, 而且有时候一个词素中可能有多个标记以标明发音。

  •     连字: 在一些环境中, 几个字符会按照一定的规则连到一起。 比如 美语中的 fi  和  fl。

通常情况下, Qt 已经针对这些特性做了处理, 并不需要在使用 或者 继承一些 输入控件时自己处理。

对语言的支持对于开发者来说是透明的。 它被封装于 Qt Text Engine 中。也就是说在编写某种语系的应用程序时, 你没必要了解这门语言的规则。 当然, 也有例外。

  • QPainter::drawText(int x, int y, const QString &str)  从左往右绘制字符串, 这在阿拉伯语和希伯来语中是错误的。 可以使用 drawText 的 QRect版本, 指定文字方向。

  • 如果你需要编写自己的输入控件, 使用 QTextLayout。 在印度语系中, 字符图元的大小会随着上下文变化, 这在 QTextLayout 中是支持的。

QTextCodec

Qt 使用 Unicode 存储、绘制、操作字符串, QTextCodec 能够用于unicode 和其他编码转换。

能够支持用户输入/输出数据的编码。 当程序启动时, 系统的本地语言系统会决定如何为内部的8位用户数据编码。 而QTextCodec::codecForLocale()  能够返回一个可以使本地语言系统和unicode互相转换的类。而 codeForName 则根据指定编码生成一个和unicode 互相转换的类。  

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

QTextCodec *codec =QTextCodec::codecForName("ISO 8859-5");

QByteArray encodedString = codec->fromUnicode(string);

如果要操作不同编码的文件, 在 QTextSteam 中使用 setCodec 。

在处理大块文字的编码转换时, 需要小心。  比如网络传输时, 多字节字符可能会被拆成2块, 这在最好的情形下可能仅仅是文字丢失, 但是在最坏的情况下, 可能使整个转换工作的失败。 处理这个问题的办法是使用 QTextDecoder

QTextCodec*codec =QTextCodec::codecForName("Shift-JIS");

QTextDecoder*decoder = codec->makeDecoder();

QString string;

while (new_data_available()) {

    QByteArray chunk = get_new_data();

    string += decoder->toUnicode(chunk);

}

delete decoder;

QTextDecoder 能够记录当前块的状态,因此可以很好的解决这个问题。 

也可以通过继承 QTextCodec 定义新的编码。纯虚函数用于描述解码器和编码器。 它们可用于不同的文本文件格式(QTextSteam), x11系统, 和本地化的输入输出。Qt Mannual 中列举出了需要重新实现的几个方法。

Qt Linguist

  在国际化应用程序中, 许多在应用程序中出现的文字比如窗口标题, 菜单项, 工具提示, pushbutton,radiobutton checkbutton的文字, 无论是一个单词, 还是短语, 都需要被翻译。

  开发者需要在源程序中使用简单的声明来表示这些文字是需要被翻译的。 Qt tools 提供了每一条短语的信息来帮助开发者、翻译者完成翻译。

  Release Manager 能够根据源代码文件生成一系列翻译文件并传递给 Translator。 Translator 使用 Qt Linguist 打开这些文件, 编辑, 并保存, 然后返回给 Release Manager。 然后 Release Manager  生成能够被应用程序使用的较快的版本。

这个工具在程序更新时能够重复使用, 保留已有翻译并很容易添加新的翻译规则。Qt Linguist 提供了能够在不同程序之间保证翻译的一致性的语法书。

由于人类语言的微妙与复杂, 开发者和翻译者必须添加一系列的规则。

一个词素根据上下文不同可能会有多种翻译, 比如 open 可以翻译为 打开, 打开文件,打开网络连接,  open file等等。

快捷键可能会发生变化, 比如 &Quit 在挪威语中是 Avslutt , 它没有 Q; 定义快捷键时必须注意不要使用已经被使用了的字母, 除非你修改了多个快捷键。

短语中包含变量, 需要在运行时替换为数字时, 需要重新加载这个短语。 因为数字的顺序问题。

这些规则在 Qt Linguist 中能够很简便的得到解决。

Qt Linguist 能够导入和导出 XML Localization Interchange File Format (XLIFF) 文件。 Translator 中有描述。

Release Manager

Release Manager 里有两个工具:  lupdate, lrelease。

在 pro 中添加 TRANSLATION 变量, 然后使用 lupdate 生成 ts 文件, 在 Qt Linguist 中编辑以后, 再使用 lrelease 命令去生成qm 文件。

Translation 文件提供了所有用户可见的文字、Ctrl快捷键, 以及它们的译文。

这些基本近于设计者的工作范围, 并且可以在 Qt Mannual  - translator 一章中找到。

developer

使用Qt创建一个可以在运行时切换语言的应用是可能的, 但是需要开发者比较多的工作, 并会带来一定的性能代价。

Qt 为每个 Window 创建了一个一个语法书, 以降低翻译所带来的性能代价。翻译工作只会在窗口的初始化时占用系统资源。 因此对于那些在应用中只会创建一次的 MainWindow, 以及仅仅在需要时才会弹出的 Dialog, Qt Linguist 都是最好的选择。 但是对于那些会经常存在 创建和和销毁动作的类, 可能会存在性能问题。

一个典型的多语言应用的 pro 文件需要加上 TRANSLATIONS 选项。

TRANSLATIONS = arrowpad_fr.ts \

               arrowpad_nl.ts

在自定义的 ts 文件名中加上 _languagename 可以在运行期确定使用哪一个文件(有待验证)。

Lupdate 读取 pro 文件指定的 头文件和源文件, 以确定需要被翻译的文字。 这也意味着 只有在 SOURCES HEADERS 变量中包含的文件才能被翻译。但是这个导致的问题是, qml 和 js 无法被翻译。 解决办法是, 采用块语句

lupdate_only {

SOURCES += main.qml \

          MainPage.qml

}

虽然, Qt为国际化提供了很方便的翻译机制, 但是开发者还是要注意以下几项

  • 在应用程序中查找和加载合适的翻译文件

  • 把用户可见的文字,以及 Ctrl 快捷键标注为翻译对象

  • 为要翻译的文字提供上下文(context 属性)

  • 消除文字二义性

  • 使用 QString 的 %n 替代某些参数, 以实现运行时翻译

  • 注意数字、日期、时间和货币的国际化

  • Mark data text strings outside functions translatable.

翻译文件的内容会包括 类型名, 以及它在文件中出现的位置, 这样可以消除二义。除此之外, 开发者可能还需要添加一些其它信息。

Qt 提供了一些 qm 文件用于翻译 Qt 类(Qt 派生类中的 文字需要自己编写 qm文件), 在Qt4中,每一个 locale 对应一个 qm 文件;  在 Qt5中, qm文件根据 module 区分。 如 对于德国地区, qtscript 模块 有 qtscript_de.qm,  qtbase 有 qtbase_de.qm  , assistant  有 assistant_de.qm等等。 当然, 这只是 Qt 的一个愿景,因为在一个特定的 application中, 并不需要对所有模块的翻译。  不过在Qt5.50 中, 中文仍然是 qt_zh_CN.qm.

可以使用 lconvert 命令, 根据多个模块的翻译文件生成一个。

例如 

lconvert -o installation_folder/qt_de.qm qtbase_de.qm qtdeclarative_de.qm

一些 例子

Hello tr()        Arrow Pad      Troll Print      Internationalization

Text Id

在一个应用程序中只能使用 纯文本, 或者 text id 函数。这个机制可以保证一个单词在不同上下文中被正确翻译成不同的意思。可以参看  Qt  Mannual  - qstr  qstrid

 

QLocale

QLocale 支持 default locale 的概念。 默认的Locale 是在应用程序启动时刻加载的系统Locale信息。Static 成员函数 setDefault 可以改变默认的 locale。

改变default locale 有以下几个影响:

  • 如果 QLocale 实例使用 默认构造函数生成, 它会使用 default locale

  • QString::toInit,   QString::toDouble 使用default locale 翻译文字, 如果失败, 他返回 “C” locale 的翻译结果。

  • QString::arg , 如果它的位置标识符里有 ‘L’, 例如 ‘%L1’时, 它使用 default locale 格式化数字。

QLocale 的构造函数中包括一个 language/country 对, 每当一个language/country 对确定时, Qt 会进行如下操作

  • 如果 它 能在数据库中被找到, 则使用它;

  • 如果这个language没有找到,但是 country 找到了, 或者country 是 AnyCountry, 则language 会使用现有的、与这个country 相关度最大的语言。

  • 如果language 和 country 都没有被找到, 则系统会使用 default locale。

可使用 language()  country()函数查看实际的语言 国家信息。

一个更简单的方法是使用 locale-name 来定义 QLocale

富文本处理

所有的类围绕 QTextDocument 展开, 它保证了用户可以创建和修改 富文本块, 而无须定义中间语言。

一个 QTextDocument 可以通过两个接口操作, 一个是用于编辑的 Cusor based 接口, 一个是只读的文档结构树。

Rich Text Document Structure

QTextDocument 是一个文档的抽象, 它包括了文档的内部结构, 并且能够通过跟踪变化以提供 undo / redo 操作。

Qt通过 block  frame table 以及其他类的树状结构表示文档。 Frame 和 table 用于组织其它结构, 而 block 包含实际内容。

 

  QDocument 可以从 QTextEdit 中获取, 也可以是用默认构造。 这样可以保证 控件和 document的轻耦合。 一个空的文档包括了一个 root frame , 它包括了一个空的block 。 table 是 frame 的一个 子类, 它的行和列都可以单独包含 block。

  Block 包含 fragment, 每一个fragment 都包含了 文字 和 字符的格式信息。文字格式信息包含于 block 和 character 两个层面。 Character 包含了字体、文字颜色、字体加粗等信息, block 包含了 文字方向、排版以及背景色等信息。

QTextObject 的子类可以归并文档元素:  QTextBlockGroup 可以归并 block; QTextFrame 可以归并 block 以及其它 元素。

一个frame 结构可以包括 多个 或者0个 frame,至少1个block;  它的 format 决定了frame在 页面上如何被渲染和定位。使用 iterator 可以遍历frame 结构,但是要注意 iterator 的数据类型是不确定的。 

QTextList 是 QTextBlockGroup 的一个子类, 它并不是 把 list item 当作它的子元素, 而是提供了一系列用于操作list item的函数。 可以如下使用

    QTextFrame::iterator it;

    for (it = frame->begin(); !(it.atEnd()); ++it) {

        QTextBlock block = it.currentBlock();

        if (block.isValid()) {

            QTextList *list = block.textList();

            if (list) {

                int index = list->itemNumber(block);

                processListItem(list, index);

            }

        }

    }

Image 使用 fragment 表示。

The QTextCursor Interface

Cursor 可以通过一个构造函数创建, 也可以在 edit widget 中获取。

cursor 可以以 block 为单位, 保存选区信息。

一个 document 里面可以有多个 cursor, 但是只有一个是可见的; paste 和 copy操作就是这样实现的。 通过简单的继承, 可以实现很多复杂的操作。

Cursor 能够追踪它在 document 中的位置, 以及所在结构。

cursor一系列的 edit可以被打包, 这样能够在 redo 和 undo 中把它作为一个操作看待。一般使用 beginEditBlock 开始, endEditBlock 结束。

 Cursor 的insert 有两个级别。 分别是 block 级别, 和 document级别。 Block级别可以插入 block 、 fragment 、 image 、text等; document 级别 插入 frame 、 table、 list, 这个级别的插入会在当前 frame 内、 当前block 以后。

可以在当前块中插入 当前文字格式 的字符串, 也可以插入自定义字符格式的字符串; 一旦在使用cursor 插入时使用了一个格式,那么这个格式会成为以后的 cursor 插入的默认格式,除非使用一个新的格式(替代了它)。

   cursor.insertText(tr("Character formats"),

                      headingFormat);

    cursor.insertBlock();

    cursor.insertText(tr("Text can be displayed in a variety of "

                                  "different character formats. "), plainFormat);

    cursor.insertText(tr("We can emphasize text by "));

    cursor.insertText(tr("making it italic"), emphasisFormat);

 

 

Document Layouts

Document Layout 仅仅在文档的展示、需要展示的变化发生时有意义。

QAbstractTextDocumentLayout 是文档中管理所有 text layout 的抽象类。

富文本处理过程概述

    常规的文本操作包括, 使用 QTextEdit 和 QTextBrowser 编辑文档, 使用QTextDocument 创建文档, 使用 QTextCursor 编辑文档, 以及打印等。

QTextBrowser 提供对有超链接文本的展示。 如果在文末使用 #anchor, 在文本滚动显示时会自动加载 anchor 所指内容。

QTextDocument 的查找是基于 QCursor 的。 如下:

    QTextCursor newCursor(document);

    while (!newCursor.isNull() &&!newCursor.atEnd()) {

        newCursor = document->find(searchString, newCursor);

        if (!newCursor.isNull()) {

            newCursor.movePosition(QTextCursor::WordRight,

                                   QTextCursor::KeepAnchor);

            newCursor.mergeCharFormat(colorFormat);

        }

    }

 

    Printing

QTextEdit 用于展示大文件在屏幕上可读的部分, 使用和 web 浏览器相同的方法去渲染。 它没有分页的功能。

QTextDocument 提供了 print 函数, 以下代码展示了如何使用 QPrinter 打印。   

QTextDocument*document = editor->document();

    QPrinter printer;

    QPrintDialog*dlg =newQPrintDialog(&printer,this);

    if (dlg->exec() !=QDialog::Accepted)

        return;

    document->print(&printer);

 

Advanced Rich Text Processing 

Qt  在处理文字时没有对文件大小做出限制,因此这有可能导致你的应用在加载文件时失去响应。 也有一些其它问题也会导致程序的性能降低。 因此有以下几个规则。

  • 把大的段落分割成较小的段落。

  • 把 文件中 block 的数目限制在  maximumBlockCount()  以内。

  • 以块为单位把文档加载到 text edit, 如下。 因为很多时候text edit 都没有必要加载一整个文档。

textEdit.show();

textCursor.beginEditBlock();

for (int i =0; i <1000; ++i) {

    textCursor.insertBlock();

    textCursor.insertText(paragraphText.at(i));

}

textCursor.endEditBlock();

Supported HTML Subset 

    Qt 的 text widgets支持html4的语法子集, 具体可以参考 Qt Mannual - Supported HTML Subset 。

 

在代码中处理翻译工作

  Qt Quick

  1.    在 QML 代码中, 用qsTr(),qsTranslate(),   qsTrId(),

  QT_TR_NOOP(),  QT_TRANSLATE_NOOP(),   和  QT_TRID_NOOP()  标记的字符串可以被翻译。

  1.  添加自定义上下文信息:

Text {

    id: txt1;

    // This user interface string is only used here

    //: The back of the object, not the front

    //~ Context Not related to back-stepping

    text: qsTr("Back");

}

在上文中, //:  以后的文字用于翻译。 //~ 以后的文字是可选项; 注意第一个词语是一个标识符(未验证)。

  1. 消除歧义: qstr 的第二个参数作为一个 id, 可以保证相同的源码在不同的语境中能被正确的翻译为不同的意思。

Text {

    id: txt1;

    // This user interface string is used only here

    //: The back of the object, not the front

    //~ Context Not related to back-stepping

    text: qsTr("Back","not front");

}

  1. 使用使用 %n 在string中插入参数: 因为不同语言对于词素的放置顺序不同, 所以把字符串硬编码可能不正确。

  2. 使用 %Ln 在string中插入数字:  对于数字而言, 不同的书写系统可能有不同的展示。 因此把它作为参数传入 qstr 中, 可以实现本地化的展示。

Text {

    text: qsTr("%L1").arg(total)

}

  1. 货币、日期、时间的本地化:  没有像上述的简单方法转换货币等符号。需要使用系统的 Locale 。

Text {

    text: qsTr("Date %1").arg(Date().toLocaleString(Qt.locale()))

}

  1. 使用QT_TR_NOOP() 翻译字符串文本数据:如果系统的 locale 信息发生了变化, 但是没有重启, 那么 array、list、model中的string数据可能不会自动更新。 QT_TR_NOOP() 使每次使用这些数据填充展示类(widgets等)时,都要显式的去查找文本数据的翻译文字。例如

ListModel {

    id: myListModel;

    ListElement {

        //: Capital city of Finland

        name: QT_TR_NOOP("Helsinki");

        }

    }

...

Text {

    text: qsTr(myListModel.get(0).name); // get the translation of the name property in element 0

    }

  1.  使用 Locale 信息扩展应用:  如果需要指定不同 Locale 下的特性, 可以如下使用 Qt.Locale()

Component.onCompleted: {

    switch (Qt.locale().name.substring(0,2)) {

        case"en":   // show the English-language icon

            languageIcon ="../images/language-icon_en.png";

            break;

        case"fi":   // show the Finnish language icon

            languageIcon ="../images/language-icon_fi.png";

            break;

        default:     // show a default language icon

            languageIcon ="../images/language-icon_default.png";

    }

}

  1. Qt 和 QML 使用相同的Locale 信息, 使用相同的翻译工具(lupdate, lrelease, linguist等)。 可以在一个应用中同时在c++ 和 QML 中使用翻译机制, lupdate 能够在同一个 ts 文件中生成翻译信息。

  2. Lupdate 会遍历 SOURCES 和 HEADERS 变量中的所有文件。 因此如果有需要翻译的 QML 或者 js 文件, 需要使用 lupdate_only 做特殊处理, 这样可以通知 qmake 忽略它们。如下

lupdate_only{

SOURCES = main.qml \

          MainPage.qml

}

    也可以使用通配符声明, 但是注意这里的查找不是递归的, 因此需要指明所有的目录。

lupdate_only{

SOURCES =*.qml \

          *.js \

          content/*.qml \

          content/*.js

}

Qt

  1. 使用 QString:  QString 使用 Unicode 编码。因此所有语言都可以使用QString; 少数情况下必须使用 char× 或者 QByteArray类型, 比如 QObject name, 以及 file format 信息;如果在程序中需要使用 其它编码, 可以使用 QTextCodec 做相应转换。

  2.  使用 tr : 如果程序中有需要展示给客户的文字, 应该使用 QCoreApplication::translate 函数。 它的简化版是 tr(在QObject 的子类中使用)。 是否使用tr 也可以把程序中是否需要翻译的文字区分开。

  3. 对于自定义类, 如果是 QObject 的子类, 使用 Q_OBJECT 宏可以使能翻译; 如果不是 QObject 的子类, 可以使用 Q_DECLARE_TR_FUNCTIONS(MyClass) 宏, 使这个可以使用tr 翻译文字。

  4. 对于作为文本数据提供的字符串, 可以使用QT_TR_NOOP 标记, 然后使用 tr 引用; 或者使用 QT_TRANSLATE_NOOP 标记, 然后使用 tr  或者 使用QCoreApplication::translate 函数引用

Qt Localization

 

 

 

 

附录

  1. Qt Linguist 相关类

QCollator

根据比较算法比较字符串

QCollatorSortKey

用于提高字符串比较速度

QLocale

用于转换数字、日期等的字符串格式。

QTextCodec

不同编码间转换

QTextDecoder

字符串状态相关的编码转换-解码

QTextEncoder

字符串状态相关的编码转换-编码

QTranslator

字符串输出的国际化的支持

Qt 文字处理

标签:

原文地址:http://www.cnblogs.com/aslistener/p/4744635.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!