Author: 于明俭 <justiny@turbolinux.com.cn >
一 国际化、本地化和中文化
- 国际化、本地化和多语言化的概念
一般来说, "国际化"是指把原来为英文设计的计算机系统或应用软件改写为同时支持多种语言和文化习俗的过程.在软件创作的初期,一般的编程语言,编译,开发都是只支持英文的, 为了适应更广的语言和文化习俗,软件有必要在设计结构和机制上支持多语言的扩展特性, 这一过程称为国际化.国际化仅仅是在软件设计上提供了使用多语 言的可能.
"本地化"是指把计算机系统或者应用软件转变为使用并兼容某种 特定语言的过程.比如,把原来为英文设计软件制作为支持中文的软 件就是本地化的一种.它主要包括翻译文本信息,界面信息,重新设计图标等等.
语言和文化习俗因地域不同而差别很大.对某一特定的地域的 语言环境称为"locale".它不仅包括语言和货币单位,而且还包括数字标示格式, 日期和时间格式.国际化了的软件含有一个"locale" 的"参量",使用该"locale"参量便可以设置某一区域所用的语言环境.
在国际化部分中只处理语言的部分叫"多语言化".比如, 一个 "多语言化"的软件可以同时管理诸如英语,法语,中日韩文,阿拉伯语等.
在英文中, 国际化(Internationalization)被缩写为I18N, 即只 取首尾两个字母,中间字母为18个.同样地, 本地化(Localization) 缩写为L10N, 多语言化(Multilingualization)缩写为M17N.
在今天, Internet把世界各地的计算机联接了起来, 共享信息和 技术是必然的趋势和需要.因此各地的计算机系统可以互相交流变得越来越重要.在Linux系统向桌面普及的过程中, Linux软件也需要国 际化和本地化.
- 中文化
"中文化"是一个很模糊的概念.在Linux上的"中文化"它既包含使 软件或系统国际化,又包含使软件本地化.也就是说, "中文化"不仅仅 是只把软件本地化这么简单的事情, 更重要的是因为Linux直接支持中文的软件太少, 做"中文化"必须先做"国际化".
由于历史的原因, 现阶段使用的中文又有简体中文和繁体中文之 分.所使用的编码也不同.支持中文的软件应该同时支持简体中文和繁体中文, 这对软件的国际化提出了更高的要求.
1999年是中国Linux发展和普及过程中最重要的一年, 其中涌现了许多制作中文Linux发布版本的公司.中文Linux的技术都是采取了中文化的捷径----中文平台.尽管都是中文平台, 但是具体实现的技术特点各不相同.充分展示了中文平台在Linux中文化过程中的魅力.中文平台在短期内发挥了巨大的作用, 加速Linux的中文化过程并推动Linux在中国的普及.
中文平台的主要技术特点是不用修改西文应用软件, 便可以显示和 输入中文(有的情况下会失效).具体地说,就是利用自己的规范去修改 X系统的底层函数.从修改的层次上分为(1)修改函数库libX11.so,这种 方式是动态修改,又称外挂方式.外挂方式的实现可以是直接修改X11库 或使用LD_PRELOAD载入动态库修改.(2)修改X Server部分, 又称内嵌方式,它的实现也分为两种, 直接修改X Server部分和建立虚拟Display(X传输协议的部分代理).
- X11 国际化的历史和级别
早期的X11R4版本中, 仅仅含有支持单字节和双字节字体的函数, 所以它不能算是国际化的函数库.此后,一个叫做"mltalk"的X协会成立并着手研究X窗口系统的国际化问题.众多的X窗口系统供应商也参与了该组织.因为对国际化的研究刚刚开始, 所以mltalk提出的了一个基本问题:什么是X窗口系统的国际化? 对它的解释也各不相同. 实际上, 即使是现在,人们对国际化的定义仍然存在分歧, 分歧的焦点主要集中于对软件或系统怎样程度的国际化才算是真正的国际化.
按国际化的级别来分, 下列几种情况都属于国际化:
- 语言可以切换.在系统启动时可以设置某种语言
- 使用不同语言的软件可以同时使用, 在应用软件启动时可以 设置某种语言
- 使用不同语言的软件可以同时使用, 而且应用软件的语言可 以动态切换
- 使用不同语言的软件可以同时使用, 而且在应用软件中可以 同时使用不同语言
显然,第(4)种国际化方式是最完善的方式, 其次是第(3)种,第(2)种 和第(1)种.mltalk 最终决定使用第(3)种,原因是需要支持第(4)种的 X窗口系统供应商是少数的.从目前Linux上的国际化情况看,支持第(2), (3)种的国际化软件是最常见的,但是第(4)种软件比较少见,而且应用的 意义不是很大.
基于上述观点, X11R5 的目的是, 创建支持不用重新编译源代码 就可以适应于语言环境的应用软件开发平台.确切地说,就是国际化 的结构是基于标准C函数setlocale的.X11R5 确立了以下规范:
- 切换语言的机制
- 与语言无关的输出接口
- 与语言无关的输入接口
- 资源文件的国际化
- X工具(Xt)的国际化
此后, 以X11R5 为基础, OSF/Motif 完成了国际化改造, 并且成为被用户广泛接受的高层图形软件库,直到今天, 一些大型的软件仍然使用 Motif 作为基础库使用, 如Java, Netscape等.X11R5的规范在制定的同时,为了检测规范的实用性, 开发了两套样本应用, 即 Xsi 和 Ximp.两套应用在输入协议上和对locale的支持上都不同,从而为开发商带来了不便.
X11R6 解决了X11R5中存在的问题, 主要的变化有,
- 定义了标准的输入协议
- Locale数据格式定义
- 只采用了一种国际化工具的样本应用模块
在输出上, X11R6增加了从右到左的的书写方式, 以支持阿拉伯语和 希伯来语等,增加了从上到下的书写方式, 以支持中文和日文等的书写.
- 国际化标准组织
这里所说的国际化标准是国际化标准组织或一些相关组织制定的一些标准, 而且这些标准也会随时间不同而经常更新.国际化标准涉及到字符集,编码,字体处理,打印,文本绘制, 用户界面, 语言输入方法, 数据交换, 文化习俗,等方方面面.
下面列出一些制定国际化标准的组织:
- Li18nux(Linux I18n)
- ANSI(American National Standards Institute)
- POSIX(Portable Operating System Interface for Computer Environments)
- ISO(International Standards Organization)
- IEEE(Institute of Electrical and Electronics Engineers)
- Unicode Consortium
- Open Group(X Consortium and OSF)
- X/Open and XPG
其中, ANSI/ISO 制定了使用C编程语言编写国际化软件的通用接口.ISO 制定了字符集标准和其它影响locale名字的标准.IEEE提供了一些国际化的通用库函数和设置管理不同locale的用户命令. Open Group是Unix和X窗口 系统的国际化标准设立组织.Li18nux 是一个专门从事Linux上的软件国际化 规范制定的组织.
- 国际化的意义
国际化,特别是国际化中制定的标准,是当今开发国际化软件所必须 的.它也是软件开发的必然趋势.遵循国际化标准,可以更高效地开发和调试软件和移植软件, 降低软件的开发费用, 使用户更方便地使用软件.从国际环境来看, 新开发的基本的库函数都会支持国际化标准, 基于这些函数库所开发的应用软件理所当然地支持国际化标准,同时有大批的Linux 爱好者把以前不符合国际化标准的软件进行了改造,使它们在一定程度上符合国际化标准.使用国际化标准的软件, 淘汰非国际化标准的软件已成为 一种趋势.
从国际化的发展历史看, 其中许多标准都有日本的商业机构参与, 支持 日文的软件变得越来越多,而从日语软件移植为中文软件相对于直接移植西文软件相当容易, 有时甚至不用改动,这样就节省了许多不必要的劳动. 反过来, 符合国际化标准的中文软件又影响日语和韩语软件,成滚雪球之势向前发展.其次, 软件商的开发比较看好亚洲市场中的日本市场,在 Unix/Linux上的日语软件或操作系统一般是符合国际化标准的, 所以兼容这一标准是十分必要的.当然,目前的国际化标准也存在不足之处, 特别是对中文这一特殊语言(因为含有GB和Big5两种不能共存的编码)的处理上,应该由中国人在原来的基础上作相应的扩展.
对中文Linux来说, 遵循国际化也是必然的趋势.在以中文平台为基础 的中文Linux上,软件移植已成为必须解决的问题,这个问题的最终解决 方法就是遵循同一标准,就目前来说遵循国际化标准是唯一的方法.鉴于目前中文Linux上的中文平台的混乱状态,国际化标准是从无序到有序过渡 的必然途径.
软件的国际标准化也为最终用户带来极大的好处, 如同时支持简体中文 和繁体中文,中文操作为双字节操作, 中文输入能够在更大的程度上使用 标准输入接口带来的好处,如输入服务器的定位等交互式操作.
国际化的另一个特点是工作在应用软件级别, 所以国际化不会给X窗口 系统带来不稳定性.
- 参考资料:
- Linux I18N: http://www.li18nux.org/
二 Locale
- Locale 的概念
Locale 是ANSI C语言中最基本的支持国际化的标志, 对中文Linux来说, 如果它支持国际化,那么支持中文Locale是最基本的要求.
Locale 是软件在运行时的语言环境, 它包括语言(Language), 地域(Territory)和字符集(Codeset).其格式为: 语言[_地域[.字符集]]. 如对中文GBK字符集, locale的格式是:zh_CN.GBK. 目前Linux上的中文 Locale还不完善, glibc2.1.x中的许多涉及Locale的C函数还不正确.如果用户需要安装中文GBK Locale, 可以直接使用TLC6.0中的:
- glibc-2.1.2(含有GBK模块)
- localedata-zh-0.07
- /usr/X11R6/lib/X11/locale/zh_CN.GBK/XLC_LOCALE(X 下的 GBK Locale)
Locale 包含了以下分类:
- LC_COLLATE, 用于比较和排序.排序对中文来说也比较重要, 但是现在的glibc中的locale对中文支持有些问题.汉字排序的的方式有许多种, 按照发音(汉语拼音)或者汉字笔画来排序 是比较容易被接受的.
- LC_CTYPE, 用于字符分类
- LC_MONETORY, 用于货币单位
- LC_NUMERIC, 用于数字显示格式.下面是不同国家的在货币符号 和数字格式上的不同:
- 中国大陆: 1,234.56RMB
- 美国: $1,234.56
- 德国: 1.234,56DM
- LC_TIME, 用于时间和日期.时间可以用12小时或者24小时的 格式来计算.在小时和分钟之间可以用逗点或者冒号隔开.下面是一些Locale设置的时间和日期的格式:
- 中国: 14点20分 2000年三月十四号
- 英国: 02:20pm 14/03/2000
- 美国: 02:20pm 03/14/2000
- 芬兰: 14.20 14.03.2000
- LC_MESSAGES, 用于国际化信息, 主要是提示信息,错误信息, 状态信息, 标题,标签, 按钮和菜单等.
Locale 通过ANSI C 函数setlocale(分类, locale)来初始化locale 数据.当locale设置为空时, locale的值便从系统的环境变量中取得.为了 方便应用软件, 设置所有的分类,可以采用下述方式:
- setlocale(LC_ALL, "");
如果不成功, 该函数返回NULL.函数应该回落到setlocale(LC_ALL,"C").
- 在X中使用Locale
在X的客户程序中使用Locale的机制和在标准C函数中使用Locale的方式一样,除此之外, 在X库中还定义了另外两个函数来判断X的locale支持和设置locale 的修饰(XModifier),在X中使用Locale和libX11的基本步骤如下:
- setlocale(): 设置当前的locale
- XSupportLocale(): 用来判断X是否支持目前设置的locale.
- XSetLocaleModifier(): 它用来指定一系列的locale修正值. 它的参量的格式是@分类=赋值.目前唯一可用的是输入服务器的名称"im".如果参量为空, 则根据系统的环境 变量XMODIFIERS查找.比如在系统上设置了环境变量:
% setenv XMODIFIERS @im=Chinput (csh) 或
% export XMODIFIERS=im=Chinput (bash)
则客户程序将查找到输入服务器Chinput, "Chinput"是 输入服务器所设置的名称.
- 文化习俗的差别
下面是在国际化和本地化过程中常常遇到的并且应当注意的地方, 对国际化软件的开发, 应该充分注意到各个地域的文化和习惯, 开发出通用的软件, 对于本地化过程,则应选择与本地域相符的习惯.
- 姓名,地址等特殊信息
姓名中的"姓"和"名"的先后次序, 地址书写的先后次序 电话号码的长度等等
- 图标的通用性
图标是易于接受的用户界面, 设计时应考虑到地域习惯, 而且图标上不能有图形文字,否则需要重新设计本地图标, 并翻译图标上的文字.
- 声音使用
不适当的声音或提示可能会引起人的反感.另外, 声音 的性别对某些国家是敏感的.
- 颜色使用
颜色和色调与民俗有关, 比如红色在美国表示危险, 在中国 表示喜庆.
- 纸张尺寸
打印纸的尺寸因地域而不同, 在选择缺省尺寸时应注意.
- 键盘差别
在键盘上的键可能因国家而异, 键的个数也可能不一样.
- 政治因素
在产品设计上, 尽量不要有政治敏感性部分.
- 参考资料:
- Linux 上的Locale
http://www.ping.be/linux/locales/index.shtml
- GBK Locale
ftp://ftp.turbolinux.com.cn/pub/turbolinux/TurboLinuxC-6.0/SRPMS/SRPMS/localedata-zh-0.07-1.src.rpm
三 X 窗口系统的国际化
在 X 窗口系统上的国际化, 特别是中文化, 主要体现在显示,输入和打印三个方面.
- 显示的国际化
- 字符集和编码
在Linux上经常使用的字符集是ISO 8859系列的字符集.它包含了10个 多语言的单字节编码字符集.它们分别是,
字符集 |
涵盖语言 |
ISO 8859-1(Latin1) |
拉丁一字符集, 包含绝大多数的欧洲语言, 例如French(fr), Spanish (es), Catalan (ca), Basque (eu), Portuguese (pt), Italian (it), Albanian (sq), Rhaeto-Romanic (rm), Dutch (nl), German (de), Danish (da), Swedish (sv), Norwegian (no), Finnish (fi), Faroese (fo), Icelandic (is), Irish (ga), Scottish (gd), English (en), Afrikaans (af) 和 Swahili (sw).影响了美洲, 澳洲和非洲. |
ISO 8859-2(Latin2) |
拉丁二字符集, 包含了中欧和东欧的语言:Czech (cs), Hungarian (hu), Polish (pl), Romanian (ro), Croatian (hr), Slovak (sk), Slovenian (sl), Sorbian. |
ISO 8859-3(Latin3) |
拉丁三字符集, 包括: Esperanto (eo) and Maltese (mt) |
ISO 8859-4(Latin4) |
拉丁四字符集, 包括: Estonian (et), 巴尔地克 Latvian (lv) 和 Lithuanian (lt), Greenlandic (kl) , Lappish. |
ISO 8859-5(西里尔语) |
Bulgarian (bg), Byelorussian (be), Macedonian (mk), Russian (ru), Serbian (sr) |
ISO 8859-6(阿拉伯语) |
阿拉伯语(ar) |
ISO 8859-7(希腊语) |
希腊语(el) |
ISO 8859-8(希伯来语) |
Hebrew (iw) 和Yiddish (ji) |
ISO 8859-9(Latin5) |
重排了Latin1, 用土耳其语的几个字母做了替换 |
ISO 8859-9(Latin6) |
重排了Latin4, 去掉了某些符号, 增加了Inuit等 |
ISO 8859-11(泰国语) |
泰国语(th) |
ISO 8859-12 |
Celtic |
ISO 8859-13(Latin7) |
Baltic Rim 和 Lativian(lv) |
ISO 8859-14(Latin8) |
Gaelic 和 Welsh (cy) |
ISO 8859-15(Latin9) |
Latin1的变种, 修改了某些字母 |
双字节字符集主要包含中文,日文和韩文.它由前导字节(Lead Byte) 和尾部字节(Trail Byte)构成,由于一个字符采用了两个字节, 在软件的 国际化方面又增加了一些麻烦,比如在显示上, 光标的位置不能位于汉字 之间,删除和移动时必须是整字操作等,在输入上, 一般需要预编辑服务器 才能输入汉字. 下表列出了中日韩语言编码的有关信息:
语言 |
字符集 |
代码页 |
前导字节范围 |
尾部字节范围 |
简体中文 |
GB2312-1980 |
CP936 |
0xA1-0xF7 |
0xA1-0xFE |
GBK |
无 |
0x81-0xFE |
0x40-0x7E, 0x80-0xFE |
中文繁体 |
BIG-5 |
CP950 |
0x81-0xFE |
0x40-0x7E, 0xA1-0xFE |
日文 |
Shift-JIS |
CP932 |
0x81-0x9F, 0xE0-0xFC |
0x40-0xFC(0x7F除外) |
韩文 |
KSC-5601-1987 |
CP949 |
0x81-0xFE |
0x41-0x5A,0x61-0x7A,0x81-0xFE |
KSC-5601-1992 |
CP1361 |
0x84-0xD3 0xD8 0xD90-0xDE 0xE0-0xF9 0x41,0xFE |
0x41-0x7E 0x81-0xFE 0x31-0x7E |
最近, 信息产业部和国家质量技术监督局联合发布了两项新的中文信息处理基础性国家标准,为解决偏、生汉字的输入提供了方案。其中GB18030- 2000《信息技术和信息交换用汉字编码字符集、基本集的扩充》,为强制性国家标准.它收录了2.7万多个汉字,总编码空间超过150万个码位,为彻底解决邮政、户政、金融、 地理信息系统等迫切需要的人名、地名用字问题提供了解决方案,也为汉字研究、古籍整理等领域提供了统一的信息平台基础。这项标准还同时收录了藏文、蒙文、维吾尔文等主要的少数民族文字.字符集编码范围是:
字节数 |
编码空间 |
码位数目 |
单字节 |
0x00-0x80 |
129 |
双字节 |
第一字节:0x81-0xFE 第二字节:0x40-0x7E,0x80-0xFE |
23940 |
四字节 |
四字节范围分别是: 0x80-0xFE,0x30-0x39,0x81-0xFE,0x30-0x39 |
1587600 |
香港特别行政区也对Big5编码提出了"香港增补字符集", 其目的,是收纳香港特区政府及市民在中文电子通讯中有需要使用的字符,来补充目前大五码和ISO10646编码标准内并未包含的字符,以作为一个通用的中文界面,方便大家能准确地以中文进行电子通讯。香港增补字符集有两套编码方案,一套适用於大五码系统,另一套适用於ISO10646平台。香港增补字符集的大五码版本,实际上是政府通用字库的增订版。ISO10646国际编码标准目前并未包含香港增补字符集内的所有字符。目前尚未收纳在ISO10646内的香港增 补字符集字符,均已提交国际标准化组织管辖下的表意文字小组,以考虑是否纳入ISO10646日后的新增版本内.
上述标准和草案应该是以后的中文Linux所应该遵循的.
- 多字节字符(Multibyte)和宽字符(WideChar)的使用
我们平时见到的以文本方式存在的字符都是多字节字符, 它主要用于文件存储和网络上的以流(Stream)的方式传输.一个GB编码的汉字需要两个字节.多字节字符的缺点是在中文处理上不方便,比如汉字的删除和光标的 移动都会有半汉字问题.为了文本处理的方便,在内部操作上通常是把汉字 与英文的混和字符串先转换成等宽度的字符串,即宽字符,为软件的内部处理 提供方便.
glibc2.1.x中多字节字符串和宽字符串的转换有时有问题.在X下还可以 使用另外一种方式完成转换,即使用XmbTextListToTextProperty()和 XwcTextPropertyToTextList() 联合完成转换.
- Unicode
目前所使用的Unicode 是一种16位字宽的字符编码, 它由非赢利的计算机组织Unicode研讨会维护和改进.它起源于Xerox和Apple之间的合作研究.几个公司组成了一个非正式的论坛, 接着IBM, Microsoft等公司迅速加入. Unicode研讨会在1990年发表了Unicode标准版本1,同时国际标准化组织完成了一种类似的编码----ISO 10646.因为没有必要存在两套标准,所以Unicode 研讨会和国际标准化组织在1991到1992合二为一. 1994年, 中国和日本开始了基于ISO10646上的国家标准进行工作.现在, Unicode 开始用在许多产品中.
Unicode包含了当今计算机领域中广泛使用的所由字符, 如世界上大部分 的书面语言,印刷字符, 数字和技术符号,地理图形和标点符号.由于Unicode 的一致性,它在大多数情况下都可能简化软件的国际化过程.它取消了处理多种代码页的必要,并且由于是16位编码, 因此由双字节字符集所引起的额外 处理也不必要了.
但是, Unicode作为一种编码也有它的缺陷, 比如编码的位置与排序无关,所以使软件支持Unicode仅仅是国际化的第一步, 实际情况中还需要与语言相关的信息和规则.所以Unicode一般作为程序的内部处理编码, 必须提供与其它 编码的双向转换表.
最后需要说明的是, 虽然使用Unicode会使普通的英文文本大两倍, 但是使用Unicode的整个系统却不会增加太大,因为系统存放的文件大部分是二进制 文件格式, 同时,使用针对Unicode的压缩方式,可以把文件压缩成和使用对应 的8位正文一样大小.
- 字体(Font)和字体集(FontSet)
在X窗口系统下使用的字体都必须在X服务器中注册X逻辑字体描述(X Logical Font Description)名.它包括了字体的许多信息, 例如以下为西文字体和中文字体的两个例子.
- -adobe-times-medium-r-normal--14-140-75-75-p-74-iso8859-1
- -tlc-song-medium-r-normal--24-240-75-75-c-240-gbk-0
为了方便使用, 用户还可以给每一个字体加一个或多个别名, 别名文件 fonts.alias 放在字体目录下, 可以手工编辑.当字体目录变更或别名变更 后, 必须使用命令 "xset fp rehash"或重新启动X才起作用.
X 字体也可以通过字体服务器(Font Server)加载.这对于本地不放字体 的系统或X终端特别有用.加载的协议可以是TCP或DECNET.
X 窗口系统的字体在X Server中之存在一份, 当所由软件都不使用它时, 字体的内存自动施放.
字体中包含了制造商名, 字体类型, 权重, 字体大小, 字符集等信息.它们也可以缩写, 省去的部分用星号代替, 比如对上面的中文字体, 可以缩写为:
- -*-song-*-24-*-gbk-0
在实际应用中, 字符串往往是中文和英文的混和字符串, 所以必须使用两种字体来绘出该字符串, 这种指定两种或两种以上的字体的描述就是字体集.字体集一般的格式是把多种字体用逗号隔开, 比如, 指定下列字体集:
- "-adobe-helvetica-medium-r-normal--14-*-*-*-*-*-iso8859-*,\
- -tlc-song-medium-r-normal--14-*-*-*-*-*-gbk-0"
令人遗憾的是, 中文的GB编码和Big5编码有重叠区域, 不能区分开来, 所以字体集并不能同时指定GB和Big5的字体.
字体集的具体载入受到Locale的影响.
在许多已经国际化的软件和图形库中, 一般通过资源文件让用户指定字体集,比如gtk的简体中文资源文件为/etc/gtk/gtkrc.zh_CN, qt-1.44(国际化的)的资源文件是 ~/.qti18nrc 等等.
- 信息的国际化
信息(Message)国际化是软件国际化中比较重要的一环, 如果使软件可以 支持多种语言,在设计时就应当考虑到信息的国际化问题.现在的绝大多数 软件使用GNU的gettext作为基本工具.信息国际化的基本步骤是:
- 在软件初始化时设置使用setlocale()设置Locale
- 使用gettext宏定义, 使程序看上去比较方便:
- 指定信息的位置:
- 指定翻译信息: _("Some Strings");
- 在软件完成后,使用 xgettext 提取信息并翻译
- 使用msgfmt把信息文件转换为.mo文件, 安装到locale目录下
/* file this_app.c */
#include <locale.h>
#include <libintl.h>
#define _(String) gettext(String)
#define N_(String) gettext(String)
#define __(String) (String)
int main(){
//由环境变量决定locale
setlocale(LC_ALL, "");
//设置message的位置和文件名
bindtextdomain("this_app", "/usr/share/locale");
textdomain("this_app");
printf(_("Some String"));
}
至此, 本程序的国际化过程已完成.编译并联接成可执行文件this_app.
- gcc -o this_app this_app.c
下面是本地化的过程.
输入的国际化
在X窗口系统下输入主要有三种方式:
- 单次击键输入单字符
- 两个或多个组合键输入单字符
- 除键输入外, 还需要转换服务器
其中前两种用于输入西文字符, 比如对于欧洲语言的特殊字符的输入, 通常采用重映射键盘的方法.或者使用"加速键"的方法输入,加速键是键盘 上的特殊键, 按下后不会使光标向后移动.
在Linux下, 使用软件xkeycaps可以把键盘重新映射并且保存整个键盘 在映射后的对照表,使用命令xmodmap可以加载映射表.
对于中文输入, 主要使用第三种输入方式.针对各种语言的综合考虑, X 窗口系统在输入上定义了下列区域:
- 预编辑区域(Preedit Area), 用于显示输入的过程, 当用户输入 字符时, 应立即显示在该区域
- 状态区域(Status Area), 用于显示输入状态, 对中文来说, 用于显示输入方法,全角/半角状态, 中文/西文标点符号状态.
- 辅助区域(Auxiliary Area), 显示可供选择的列表, 又称选择 区域, 它由输入服务器控制.
根据预编辑区域和状态区域的不同组合, X 窗口系统共定义了四种输入 的风格(Input Style):
- Root风格: 预编辑区域和选择区域都在应用软件之外, 它们都是 由输入服务器完成的,输入服务器所显示的界面是根窗口的子 窗口.如类似"中文之星"的独立的输入条模式.
- OffTheSpot风格: 预编辑区域和选择区域在应用软件之内, 通常 是在窗口下方的某个固定区域内.如XEmacs的缺省输入模式.
- OverTheSpot风格: 预编辑区域在当前的输入位置, 状态区域 在应用程序的某一固定区域.它通常又称为光标跟随模式,类似 于Windows下的智能ABC输入方法
- OnTheSpot风格: 预编辑区域和选择区域都在应用软件之内, 内容是由输入服务器发送的,应用程序负责显示.
对中文输入来说, 最好的风格是(3),(4),(1).对大部分中文输入方法,必须弹出辅助区域, 供用户选择, 只有少数的中文输入方法, 如五笔字型,比较适合(4).对于状态区域,中文输入多数选用在Root风格的窗口的某个 位置或使用专用的控制条.在MS Windows下比较常用的光标跟随模式,可以 用(3),(4)来实现.鉴于Linux下有的用户把X Window设置成为虚屏模式,选择上述的任何一种模式都不尽满意.
对应用软件来说, 最简单的输入接口是Root风格, 它把显示部分交给输入服务器去做.编写软件时所用的代码量少,是对软件初步使用国际化 标准的最佳选择.从方便用户的角度来看, 应用软件,特别是高层的库函数应该同时支持四种输入风格.令人遗憾的是, 一般软件仅支持两到三种输入风格.所以在现在的输入服务器(IM Server)也很少支持四种风格,这似乎 成了鸡和蛋的问题.
下面列出几种常用软件和图形库的XIM支持情况:
Netscape |
Root,OffTheSpot,OverTheSpot |
Java |
Root,OnTheSpot |
Qt |
Root,OverTheSpot |
gtk+ |
Root,OverTheSpot |
rxvt |
Root,OffTheSpot,OverTheSpot |
中文输入需要客户软件和服务器软件的的密切配合, 它们之间是通过 XIM(X Input Method)协议来通讯的.输入服务器首先起动,在X Server里 注册自己,服务器的名字也被注册.当客户程序起动时, 到X Server里查寻有没有符合自己locale类型的输入服务器(如果用XMODIFIERS指定服务器名,则同时用locale和名字区分).找到后,根据输入服务器提供的风格种类 选择一个最适合自己的风格.然后客户程序为每一个需要输入的窗口都建立一个自己的标示IC(Input Context), 里面含有客户程序的信息, 以后的通讯则一直使用该标示.
下面是直接使用X Lib和服务器联接的过程, 在高层函数库中, 把这一 过程隐藏了起来:
XIM im;
XIC ic;
...
if( (im = XOpenIM(display, NULL, NULL, NULL)) == NULL ) {
printf("Error : XOpenIM !\n");
exit(0);
}
//指定预编辑的类型等...
if( (ic = XCreateIC(im,
XNInputStyle, XIMPreeditPosition | XIMStatusNothing,
XNClientWindow, window,
NULL)) == NULL ) {
printf("Error : XCreateIC() ! \n");
XCloseIM(im);
exit(0);
}
...
for(;;) {
XNextEvent(display, &event);
//如果输入服务器接收并处理...继续
if (XFilterEvent(&event, None) == True)
continue;
switch(event.type) {
case Expose:
XmbDrawString(...);
case KeyPress:
count = XmbLookupString(ic,
(XKeyPressedEvent *) &event,
string, len, &keysym, &status);
...
}
}
目前使用比较广泛的XIM输入服务器有Chinput(简体中文, 同时支持繁体), xcin(繁体中文), kinput2(日文) 和 hanIM/ami(韩文).
中文输入服务器Chinput 选择了OverTheSpot风格作为缺省的输入模式,它与标准的输入风格略有不同,即把预编辑区域偏离输入位置, 使输入区 域同时作为状态区域,在很大程度满足了用户的输入习惯.同时它还使用辅助工具条显示和改变输入状态.Chinput还解决了同时使用GB和Big5编码的问题,被动输入(Passive Input)问题等.对于普通用户, 除了使用键盘 输入外,还可以使用手写识别输入和语音识别输入方式.目前的输入架构基本能够满足它们的要求.笔者在手写识别输入方面做了一些尝试, 发现对绝大部分软件是能够适合被动输入的.
打印的国际化
在X窗口系统下的打印是一个很难解决的问题, 所以到目前为止没有形成 一个统一的打印标准.其原因之一就是X窗口系统在设计上把显示和打印完全分开了.
在Linux最常见的需要打印的文件格式是普通文本文件和PostScript文件.对于中文的普通文本文件的打印一般需要先转换为PostScript文件再打印.对于PostScript文件,如果应用软件在生成时含有中文字体信息, 则打印 比较容易实现,反之, 则很难实现甚至不可能打印.
目前中文文本文件常用的打印方法通常是,使用gb2ps/bg2ps/cnprint 等软件转换成PS文件打印,转换过程使用了中文的点阵字体.对已经形成的PS 文件的打印, 如果不包含中文字体,直接打印就会输出乱码,通常使用的方法 是将这一类PS文件过滤一下, 改为使用中文字体,然后再打印.如陈向阳先生的过滤软件ps2cps可以打印Netscape的存储文件.这种打印的缺点是有时输出的PS中汉字字符串和英文字符串对不齐.最好的方法是在 PostScript一级实现 中文打印,陈向阳先生对ghostscript进行了中文化, 可以直接使用TTF轻松打 印Netscape, Qt/KDE, lyx等软件输出的PS文件.这种从底层实现打印的方法 也是日文和韩文所采用的方法.
使用CID(adobe)字体打印的方法也在尝试之中.
总之, 目前的中文打印缺乏统一标准, 应用软件在输出打印PS文件时多数 不考虑双字节语言的问题,使打印变得更加复杂化, 所以当前的中文Linux发 布版本多数不支持中文打印,
客户程序间通讯的国际化
客户程序间通讯(Interclient Communications Conventions,简称ICCC)是客户程序之间共享资源的手段之一.最常见的应用是文本的拷贝和粘贴和与窗口管理器通讯.但是如果两个应用程序之间所使用的字符集不同,粘贴就会出现问题, 甚至粘贴的内容会丢失.所以客户程序之间必须国际化了的通讯协议.
应用程序和窗口管理器之间的通讯也属于客户程序间通讯.
如果客户程序之间使用的字符集相同, 但是编码不同, 则不会丢失数据, 这时应该使用复合文本(COMPOUND TEXT)传输.X内部定义了COMPOUND_TEXT 的原子(Atom)用于传输中英文混和的字符串.对7字节编码, ASCII或者其它 ISO8859-1字符集, 客户程序通讯可以不用转换而直接使用XA_STRING原子传输.
四 开发符合国际化标准的软件
在X窗口系统下开发软件, 应尽量符合国际化标准.它包括, 设置合适的locale(见前面讲述的在X下使用locale),注意选择字符集和字体集, 本地化文本的处理,输入方法等等.这里推荐用户尽量使用在国际化方面已经比较完善的高层图形库, 如Qt, gtk+, Java等, 这样可以避免考虑以上问题.选择Motif时需要考虑资源的国际化问题和FontList等.
- 开发国际化软件
使用已经支持国际化的高层图形库开发支持国际化的软件基本上可以不用考虑国际化问题.特别是输入问题,在标准的输入区内(单行输入和多行输入), 都可以自动输入汉字.在字体处理上,注意使用字体集.许多软件需要在资源文件中指定字体和字体集, 所以开发的软件应提供一个缺省支持字体集的资源文件.
下面所介绍的开发国际化的软件是基于libX11的开发方法.除了前面所说的 在软件初始化时调用一些Locale的函数外,在实际编程时, 还应注意以下问题:
- 字体载入: 在处理字符串时, 使用FontSet, 而不是Font:
XCreateFontSet() - 建立字体集
XFreeFontSet() - 释放字体集内存
XFontsOfFontSet() - 返回XFontStruct和字体名
XBaseFontNameListOfFontSet() - 返回字体集的名称
XLocaleOfFontSet() - 返回XFontSet的locale名
XExtentsOfFontSet() - 获得FontSet的最大Extents
- 计算字符串的屏幕尺寸并画字符串:
Xmb/XwcDrawString() - 只画字型(glyphs)的前景
Xmb/XwcDrawImageString() - 画前景和背景
Xmb/XwcDrawText() - 复杂的间隔和字体集
Xmb/XwcTextEscapement() - X 方向像素
Xmb/XwcTextExtents() - 字符串轮廓
- 客户程序间通讯:
Xmb/wcTextListToTextProperty() - 根据locale的文本转换
Xmb/wcTextPropertyToTextList() - 根据locale的文本转换
XFreeStringList()
Xmb/wcFreeStringList() - 释放StringList
XSetWMProperties() - 设置窗口管理器属性
XSetWMName() - 设置窗口窗口名
XSetWMIconName() - 设置窗口图标名
- 输入:
XOpenIM()/XCloseIM() - 打开/关闭输入服务器
XDisplayOfIM()/XLocaleOfIM()
XSetIMValues()/XGetIMValues() - 设置/获取输入服务器属性
XCreateIC()/XDestroyIC() - 建立/释放IC
XIMOfIC()
XSetICValues()/XGetICValues() - 设置/获取IC的值
XSetICFocus()/XUnsetICFocus() - 聚焦/取消聚焦
XmbResetIC()/XwcResetIC() - 重设IC
XFilterEvent() - 过滤事件
Xmb/wcLookupString() - 查找字符串
XRegister/UnregisterIMInstantiateCallback() - 注册/取消回调
- 使非国际化软件国际化
修改已经存在的非国际化软件, 应根据具体情况采用不同的补丁.需要 注意的是修改后的软件应与原来的软件兼容,不会对软件以前在西文和其它 语言的支持造成影响.Locale应该是软件的语言切换中心点.下面是笔者在修改软件的过程中一些经验, 仅供参考.
- 在软件初始化时设置Locale.
- 定义gettext的宏, 并且把它与信息文件绑定.
- 对所有静态信息使用gettext
- 对文本绘制使用字体集代替字体
- 绘制函数使用X下的多字节或宽字符函数
- 初始化和XIM服务器的联接
- 在事件循环中用XFilterEvent()过滤事件到XIM服务器
- 使用Xmb/wcLookupString()查找字符串
五 目前中文化中存在的问题
现有的国际化标准中存在许多问题, 问题的原因主要出自目前的 国际化架构.对于中文化来说,这些问题显得更加突出.
- 编码动态切换的问题
对中文软件来说, 同时支持多内码(GB和Big5)是比较完善的中文软件, 但是动态切换内码,特别是切换软件界面(如菜单项)的内码,是受到信息 (Message)国际化中 gettext 的限制的.一般来说, 一旦软件载入, 所有 文本信息便被初始化,而且在整个过程中不会再重新装载信息.退一步说,即使重新装载了信息, 由于所装载信息的长度发生了变化, 软件界面调整 布局也是十分困难的.
所以现有软件的动态编码切换仅仅是在部分区域实现, 例如Netscape. 遗憾的是, Netscape的编码切换并不彻底,它切换的仅仅是显示部分, 输入 部分仍然有问题.比如在zh_CN.GBK的环境下启动Netscape,当切换到有 输入条的繁体中文页面时,如果采用输入软件自动识别Input Context的编码的方式, 仍然会认为Netscape是GB编码, 输入结果不正确.如果输入 Big5编码,必须缩定输出的编码为Big5.Chinput在这方面做了一些尝试, 结论是可以输入Big5编码,但是在输入条中的显示不正确.
一般来说, 使用中文平台来动态切换编码更容易实现.在中文Linux 的发布版本中,有几个是可以使用中文平台来实现动态切换编码的, 其原理十分简单, 只要在应用程序或X服务器把某个窗口的编码状态记住就行了,以后的文本显示和输入都以此编码为标准.这种方法的缺点是, 应用程序初始界面上的中文由于转化了编码变成了乱码.
- 中文编码自动识别问题
在文本浏览,网页浏览或网页翻译时, 通常需要自动识别汉字的内码, 但是中文的GB编码和Big5编码有重叠区域,所以很难区分开.目前公开 源代码的识别软件很少, 识别结果不能令人满意,远没有达到目前商业软件 的识别水平.
- Linux上的中文平台到国际化的过渡
但是从长远的角度看, 因为中文在对中文显示和输入上与国际化标准 存在很大差异,所以亟需一种从中文平台到国际化标准的过渡性方案.在 过渡的过程中, 中文平台可能会和国际化标准共同存在一段时间.
以CLE和TurboLinux为例, 它们在早期的版本中都采用了中文平台来 支持中文的显示和输入,随着支持国际化标准的软件的增多,逐步采用了 中文平台和国际化标准共同存在的版本作为过渡性版本.到目前为止,已经在缺省情况下放弃中文平台的使用.中文平台只是作为残留物包含在发布 版本中.
- Linux 文档中文化
Linux文档, 主要是指Linux上的一些命令帮助文档(man文件), 软件 手册和说明,软件本身的Message文件(po).目前在这方面的工作还缺乏 统一的管理和广大Linux爱好者的参与.
参考资料
- Unicode: http://www.unicode.org/
- 香港增补字符集: http://www.digital21.gov.hk/chi/hkscs/introduction.html
- CJK 有关信息: ftp://ftp.ora.com/pub/examples/nutshell/ujip/doc/cjk.inf
- Linux国际化资料: http://i18n.linux.org.tw/
- Linux国际化标准: http://www.li18nux.org/
- MicroSoft 国际化: http://www.microsoft.com/globaldev/
六 附录
- 宽字符处理函数函数与普通函数对照表
字符分类:
宽字符函数 |
普通C函数 |
描述 |
iswalnum() |
isalnum() |
测试字符是否为数字或字母 |
iswalpha() |
isalpha() |
测试字符是否是字母 |
iswcntrl() |
iscntrl() |
测试字符是否是控制符 |
iswdigit() |
isdigit() |
测试字符是否为数字 |
iswgraph() |
isgraph() |
测试字符是否是可见字符 |
iswlower() |
islower() |
测试字符是否是小写字符 |
iswprint() |
isprint() |
测试字符是否是可打印字符 |
iswpunct() |
ispunct() |
测试字符是否是标点符号 |
iswspace() |
isspace() |
测试字符是否是空白符号 |
iswupper() |
isupper() |
测试字符是否是大写字符 |
iswxdigit() |
isxdigit() |
测试字符是否是十六进制的数字 |
大小写转换:
宽字符函数 |
普通C函数 |
描述 |
towlower() |
tolower() |
把字符转换为小写 |
towupper() |
toupper() |
把字符转换为大写 |
字符比较:
宽字符函数 |
普通C函数 |
描述 |
wcscoll() |
strcoll() |
比较字符串 |
日期和时间转换:
宽字符函数 |
描述 |
strftime() |
根据指定的字符串格式和locale设置格式化日期和时间 |
wcsftime() |
根据指定的字符串格式和locale设置格式化日期和时间, 并返回宽字符串 |
strptime() |
根据指定格式把字符串转换为时间值, 是strftime的反过程 |
打印和扫描字符串:
宽字符函数 |
描述 |
fprintf()/fwprintf() |
使用vararg参量的格式化输出 |
fscanf()/fwscanf() |
格式化读入 |
printf() |
使用vararg参量的格式化输出到标准输出 |
scanf() |
从标准输入的格式化读入 |
sprintf()/swprintf() |
根据vararg参量表格式化成字符串 |
sscanf() |
以字符串作格式化读入 |
vfprintf()/vfwprintf() |
使用stdarg参量表格式化输出到文件 |
vprintf() |
使用stdarg参量表格式化输出到标准输出 |
vsprintf()/vswprintf() |
格式化stdarg参量表并写到字符串 |
数字转换:
宽字符函数 |
普通C函数 |
描述 |
wcstod() |
strtod() |
把宽字符的初始部分转换为双精度浮点数 |
wcstol() |
strtol() |
把宽字符的初始部分转换为长整数 |
wcstoul() |
strtoul() |
把宽字符的初始部分转换为无符号长整数 |
多字节字符和宽字符转换及操作:
宽字符函数 |
描述 |
mblen() |
根据locale的设置确定字符的字节数 |
mbstowcs() |
把多字节字符串转换为宽字符串 |
mbtowc()/btowc() |
把多字节字符转换为宽字符 |
wcstombs() |
把宽字符串转换为多字节字符串 |
wctomb()/wctob() |
把宽字符转换为多字节字符 |
输入和输出:
宽字符函数 |
普通C函数 |
描述 |
fgetwc() |
fgetc() |
从流中读入一个字符并转换为宽字符 |
fgetws() |
fgets() |
从流中读入一个字符串并转换为宽字符串 |
fputwc() |
fputc() |
把宽字符转换为多字节字符并且输出到标准输出 |
fputws() |
fputs() |
把宽字符串转换为多字节字符并且输出到标准输出串 |
getwc() |
getc() |
从标准输入中读取字符, 并且转换为宽字符 |
getwchar() |
getchar() |
从标准输入中读取字符, 并且转换为宽字符 |
None |
gets() |
使用fgetws() |
putwc() |
putc() |
把宽字符转换成多字节字符并且写到标准输出 |
putwchar() |
getchar() |
把宽字符转换成多字节字符并且写到标准输出 |
None |
puts() |
使用fputws() |
ungetwc() |
ungetc() |
把一个宽字符放回到输入流中 |
字符串操作:
宽字符函数 |
普通C函数 |
描述 |
wcscat() |
strcat() |
把一个字符串接到另一个字符串的尾部 |
wcsncat() |
strncat() |
类似于wcscat(), 而且指定粘接字符串的粘接长度. |
wcschr() |
strchr() |
查找子字符串的第一个位置 |
wcsrchr() |
strrchr() |
从尾部开始查找子字符串出现的第一个位置 |
wcspbrk() |
strpbrk() |
从一字符字符串中查找另一字符串中任何一个字符第一次出现的位置 |
wcswcs()/wcsstr() |
strchr() |
在一字符串中查找另一字符串第一次出现的位置 |
wcscspn() |
strcspn() |
返回不包含第二个字符串的的初始数目 |
wcsspn() |
strspn() |
返回包含第二个字符串的初始数目 |
wcscpy() |
strcpy() |
拷贝字符串 |
wcsncpy() |
strncpy() |
类似于wcscpy(), 同时指定拷贝的数目 |
wcscmp() |
strcmp() |
比较两个宽字符串 |
wcsncmp() |
strncmp() |
类似于wcscmp(), 还要指定比较字符字符串的数目 |
wcslen() |
strlen() |
获得宽字符串的数目 |
wcstok() |
strtok() |
根据标示符把宽字符串分解成一系列字符串 |
wcswidth() |
None |
获得宽字符串的宽度 |
wcwidth() |
None |
获得宽字符的宽度 |
另外还有对应于memory操作的 wmemcpy(), wmemchr(), wmemcmp(), wmemmove(), wmemset().
- X 窗口系统下支持中文的函数
支持西文的函数 |
支持中文的函数 |
描述 |
XLoadFont |
XCreateFontSet |
载入字体集 |
XTextExtents(16) |
Xmb/wcTextExtents Xmb/wcTextPerCharExtents |
返回文本的限制框 |
XDrawString |
Xmb/wcDrawString |
在窗口中画字符串, 背景填充 |
XDrawImageString |
Xmb/wcDrawImageString |
在窗口中画字符串 |
XDrawText |
Xmb/wcDrawText |
在窗口中画字符串 |
XLookupString |
Xmb/wcLookupString |
查找字符串 |
- 支持国际化的高层库
- OSF/Motif
- Qt/kdelib
- gtk+/gnome-lib
- Perl
- Java
- 支持多语言的典型软件
- 浏览器 Netscape
- 编辑器 XEmacs
- 编辑器 Mule
- 编辑器 vim
- 终端 rxvt
- 排版软件 LaTeX/lyx
- PostScript/PDF: gs/acroread
- 图像处理: gimp
- 幻灯片制作 mgp
- 即将完成: StarOffice, KOffice
- 支持Unicode的软件
- 高级图形库函数 Qt 2.x
- Java 语言开发工具 JDK
- 编辑器 yudit
- 专用的支持Unicode的 X 终端
- 基于GTK+的文本处理器 GScript