字母汤:Linux 的国际化,第 1 部分
什么是 Linux?既然您正在阅读《Linux Journal》,您可能已经知道了。尽管如此,仍然有必要强调 Linux 是 UNIX 的开源软件实现。它是由分布式开发过程创建的,主要应用是通过网络与其他独立实现和管理的系统进行交互。在这种环境中,符合公共标准至关重要。不幸的是,国际化是信息处理领域,当前的标准和可用方法远非令人满意。人们常常很想放弃遵守(国际)标准,转而支持准确高效地实施本地标准和习俗。
什么是国际化?它不仅仅是 Linux 安装在多少个国家/地区的问题,尽管这当然表明了 Linux 的灵活性。直到最近,尽管他们的母语差异很大,但大多数 Linux 用户都精通某些常见的非自然语言,例如 C、sh 和 Perl。他们使用 Linux 的主要目的是将其作为软件开发和提供网络服务的廉价、灵活和可靠的平台。当然,大多数人也使用 Linux 进行母语的文本处理和文档传播,但这只是一个相对次要的目的。强大的计算机技能和黑客精神使得人们可以接受绕过各种问题。
今天,许多新用户正在转向 Linux,寻求一个可靠、灵活的平台,用于桌面出版和万维网内容提供等活动。即使是黑客也会厌倦绕过软件缺陷,因此现在迫切需要软件来简化和可靠地进行英语以外语言的文本处理,并允许根据每个用户的母语和习俗格式化文本。
使系统适应新文化的过程称为本地化(缩写为 L10N)。显然,这需要提供字符编码、显示字体和输入法,用于输入和显示用户的母语,但它也涉及对默认时间系统(12 小时制或 24 小时制)和日历(数字日期是像美国那样使用 MM/DD/YY,还是像国际标准那样使用 YY/MM/DD,还是 DD/MM/YY?)、货币表示和字典排序顺序等设施进行更细微的调整。POSIX 已将自动处理这些问题的 API 标准化,但许多其他问题仍然存在,例如换行和连字符约定。因此,本地化不仅仅是为语言显示提供合适的脚本,实际上,它不仅仅是支持一种语言。美国人和英国人都使用相同的语言(就计算机而言),但他们的货币符号不同。
真正的国际化有助于本地化,但也可以通过修补或移植任何系统 临时 来完成。为了了解其中的区别,请考虑一个希望在 Microsoft Windows 环境中处理日语的中国人有两种选择:双启动日文版 Windows 和中文版 Windows,或使用相当不令人满意且应用程序通常不支持的 Unicode 环境。这是一种本地化;将应用程序从日文版 Windows 移植到中文版 Windows 并非易事,因为不能使用相同的二进制文件。在国际化的设置中,人们只需要更改字体、输入法和翻译消息;这些将被实现为可加载模块(或单独的进程)。关于应用程序,Linux 中的情况充其量稍好一些(特别是从亚洲用户的角度来看)。但是,未来看起来非常光明,因为许多团体正在积极推动国际化并为 GNU/Linux 环境开发国际化系统。
国际化(缩写为 I18N)是调整系统的数据结构和算法的过程,以便将系统本地化到新文化只是翻译数据库的问题,而不需要修补源代码。当然,我们希望二进制文件同样灵活,但出于效率或向后兼容性的原因,本地化版本可能会实现不同的数据结构和算法。虽然国际化比本地化更困难,但一旦完成,将国际化软件本地化到新环境的过程就会变得例行化。此外,本地化本质上不是标准化的有力候选者,因为每个要本地化到特定环境的新系统都会带来其自身的新问题。另一方面,国际化根据定义是独立于不同文化环境的标准。一个明显的扩展是共同标准化许多系统通用的那些设施。
国际化可以与多语言化进行对比。多语言化(缩写为 M17N)是使系统适应同时使用多种语言的过程。多语言化显然比本地化甚至国际化更困难,它要求系统不仅要处理不同的语言,还要为当前数据集的特定部分维护不同的上下文。
请注意,操作系统可以本地化、国际化或多语言化,而某些或所有应用程序则不然,反之亦然。在某种意义上,Linux 是一个多语言操作系统;内核对不同语言的使用几乎没有限制。但是,由于字体和输入法的可用性以及它们自身的内部结构和消息数据库,大多数实用程序和应用程序都仅限于英语。即使是内核崩溃也以英语显示。另一方面,GNU Emacs 20,无论是 FSF 版本还是 XEmacs 变体,都集成了 Mule(多语言扩展 Emacs)功能(参见本期中的“Polyglot Emacs”)。借助字体的可用性以及在必要时国际化的终端仿真器,Emacs 可以同时处理世界上大多数语言。许多 GNU 实用程序使用 GNU gettext 函数(参见本期中的“Linux 程序中的国际化消息”),该函数为每种语言支持不同的程序消息目录。
基础是操作系统内核。对内核功能施加的主要条件是它必须将自然语言文本数据视为二进制数据。进程之间传递的数据不应以任何方式随意更改。对于许多内核功能(例如,调度和内存管理),这已经是大多数系统都遵守的要求。
从历史上看,计算机文本通信使用 7 位字节和 ASCII 字符集和编码。通常,通信软件会将高位用作“奇偶校验位”,这有助于错误检测和纠正。一些终端驱动程序会将其用作“bucky 位”或指示面部变化,例如下划线或反向视频。虽然这些用法本身都不是坏事,但应将其限制在明确定义和充分记录的接口中,并提供 8 位干净的替代方案。
不幸的是,7 个有效位假设泄露到许多软件中,特别是电子邮件的实现,现在已载入 Internet 标准,例如用于简单邮件传输协议 (SMTP) 的 RFC-821 和描述文本消息 Internet 消息头格式的 RFC-822。透明地传递所有数据且不执行未经请求的转换的软件称为“8 位干净”。请注意,endianness 现在是文本处理中的一个问题,因为中文、日文、韩文和 Unicode 字符集使用至少两个字节的整数来表示某些或所有字符。因此,任何提及 8 位清洁度都应理解为也暗示正确处理 endianness 问题。
人们可能还希望内核日志和错误消息被本地化。虽然错误消息相当程式化且易于学习,但也很容易翻译。这样做的主要反对意见是效率。将所有已知语言编译到内核中会使其变得庞大;本地化内核意味着为每种语言维护单独的构建。一个更微妙的问题是,人们可能会倾向于避免需要翻译更多消息的新功能,或者进行仓促、不准确的翻译。最后,单独的消息目录需要在某个时候加载,从而引入更多错误的可能性。更糟糕的是,在加载目录之前系统崩溃意味着根本没有错误消息。
下一层是文件系统,通常被认为是内核的一部分。在像 Linux 的 /proc 文件系统这样的情况下,它们与内核密不可分。文件系统是一种特殊情况,因为它们包含的对象本质上需要呈现给用户并由用户输入。因此,仅仅实现文件系统的程序是 8 位干净的还不够,因为目录分隔符 / 对操作系统具有特殊含义。这意味着尝试使用根据 JIS-X-0208 标准编码的日文文件名可能不起作用,因为其中一些双字节字符的第二个字节包含 ASCII 值 0x2F。如果文件系统将路径名简单地视为字节字符串,则当在函数参数中传递到文件系统接口时,此类字符将被损坏。
人们可以想象各种解决方案来解决这个问题,例如重新设计文件系统调用以识别各种编码,使用通用编码(如 Unicode),或通过将路径数据类型定义为字符串列表来消除操作系统对特殊字符的依赖。但是,这些解决方案将排除向后兼容性和与外部文件系统的兼容性,并且对程序员来说将是一种负担。幸运的是,一种相当令人满意的解决方案是使用转换编码,该编码在非 ASCII 字符上设置第八位。主要的亚洲语言都有这样的编码 (EUC),Unicode (UTF-FSS,正式名称为 UTF-8) 也是如此。根据定义,ISO-8859 系列编码(所有编码都包含美国 ASCII 作为子集)满足此约束,而无需转换。使用转换格式很少给用户带来负担,因为他们通常不需要知道他们的语言正在使用哪种编码格式。但是,当挂载外部文件系统时,这种困难仍然适用,尤其是 MS-DOS 和 VFAT 文件系统,其中 Microsoft 为其本地化的 DOS 和 MS Windows 扩展实现了特殊的编码,例如 Shift-JIS。图 1 显示了作为 MS-DOS 文件系统挂载在我的 Linux 系统上的日文版 Windows 95 文件系统的部分目录列表,即 Mule 中的 Dired 缓冲区。请注意缓冲区顶部的错误消息。ls 无法找到它刚从文件系统接收到的文件名。当制作目录列表时,一次传递会生成文件名列表,其中包括 Shift-JIS 编码的日文名称。当列表传递回文件系统以获取文件属性时,日文字符的一些八位字节被解释为文件系统分隔符。因此,找不到日文字符,并导致错误消息。VFAT 文件系统在本例中没有表现出这个问题;我不确定为什么。大多数字符都完好无损地传递了,正如您所看到的。
图 1. Linux 上的日文版 Windows 95 文件系统
此原则普遍适用于其他系统进程(init、网络守护进程、记录器等)。只要实现它们的程序是 8 位干净的,在配置文件等的注释和字符串中使用非 ASCII 字符应该是相当透明的,只要使用标准编码的文件系统安全转换格式即可。这是因为关键字和语法上重要的字符在历史上是从美国 ASCII 字符集中提取的,特别是美国英语。这种情况不太可能改变,因为美国在计算机系统制造和分销方面的历史主导地位意味着大多数编程和脚本语言都是基于英语的,并且使用 ASCII 字符集。一个有趣的例外是 APL;由于其 IBM 血统,它基于 EBCDIC,其中包含许多 ASCII 中不存在的符号。
编程语言、操作系统 (POSIX)、用户界面 (X) 和系统间通信 (RFC 1123、MIME 和 ISO 2022) 的主要标准指定了可移植字符集,这些字符集是 ASCII 的子集。协议关键字被定义为来自可移植字符集的字符串,并且在指定默认初始编码的情况下,它是美国 ASCII 或其超集 ISO-8859-1。请注意,在大多数情况下,指定了字符集,例如,在 C 和 X 中。IBM 大型机必须支持可移植字符集,该字符集是 ASCII 的子集,但这些字符将以 EBCDIC 编码。
唯一的例外是 ISO 2022,它既没有指定可移植字符集,也没有指定 ASCII 字符集作为默认值;但是,即使在那里,ASCII 的影响也极其强大。256 个可能的字节被分为“左”(第八位为零)和“右”(第八位为一)两半,每半 128 个码位,并且在每半中,前 32 个码位(0x00 到 0x1F)保留用于控制字符,而其余 96 个码位可能是可打印字符。此外,位置 0x20 和 0x7F 分别保留了解释为空格和删除字符,可能不用于图形字符,而 0xA0 和 0xFF 通常保持未使用状态。
下一层是用户界面,例如 Linux 控制台和 X。在这里,强烈的偏好是原始形式的多语言化,允许显示任意字体,并通过键盘的可配置映射以任意编码输入文本。Linux 控制台和 X 都提供了这些功能,尽管 Linux 控制台不直接支持无法在一个字节中编码字符的语言。它们不需要更复杂的机制,因为用户很少直接与它们打交道;应用程序开发人员将在这些工具包之上构建用户友好的界面。另一方面,它们应该尽可能通用,以便本地化尽可能灵活。
下一层是应用程序,包括系统实用程序。在这里,事情变得更加复杂。不仅希望它们以用户的母语发出消息和接受输入,而且还必须处理非平凡的文本操作,例如排序。当然,它们的全部目的可能是文本操作,例如,文本编辑器 Emacs 或文本格式化程序 TeX。
这种复杂性的一个例子是,即使语言有共同的字符,排序顺序通常也不同。例如,西班牙语和英语共享罗马字母的大部分,并且都可以用 ISO-8859-1 编码进行编码。但是,在英语中,Canada、China 和 Czech Republic 的名称按该顺序排序,但在西班牙语中,它们的排序顺序为 Canada、Czech Republic 和 China,因为西班牙语将“ch”视为一个实体,在“c”之后但在“d”之前排序。尽管中文、日文、韩文和在某种程度上越南文共享起源于中国的表意文字,但它们对如何对这些字符进行排序有非常不同的想法。
最外层,除了用户到系统的界面之外,是系统间通信。这一层具有前面已经提到的所有问题,外加一个问题。在单个系统内,可以隐式地指定如何处理每种语言;当识别出一种语言时,某个子系统的相应版本会处理它。但是,当与另一个系统通信时,必须存在一种指定格式的机制。在这里,MIME(多用途 Internet 多媒体扩展)格式至关重要。在可能的情况下,应提供一种协商通信的适当格式的方法,如 HTTP(超文本传输协议),它是万维网的基础。
最基本的功能是文本显示。仅仅讨论文本显示就需要三个概念:字符集、编码和字体。语言的字符集是用于构成单词、短语和句子的那些字符。字符是语言的语义单位,而“字符”的概念非常抽象。计算机无法直接处理字符;它们必须编码为位串。这些位串通常为 8 位宽;8 位字符串在标准中称为 八位字节。“字节”不使用,因为它是一个面向机器的概念;八位字节可能指的是通过串行线传输的对象,并且两端的主机都不需要具有直接处理 8 位字节的功能。大多数 Linux 用户都熟悉十六进制编号系统和 ASCII 表,因此我将使用八位字节的两位十六进制数字表示。例如,“拉丁大写字母 A”将被编码为 0x41。
人类读者通常没有用于电子输入位串的串行接口;相反,他们更喜欢阅读视觉表示。字体是 字形(字符形状或图像)的索引集(不一定是数组,因为合法索引序列中可能存在间隙),这些字形可以显示在印刷页或视频监视器上。字体中的字形不需要与字符一一对应,并且不一定在母语中具有语义含义。例如,考虑单词“fine”。在内存中表示时,它将由字节字符串“Ox66 Ox69 Ox6E Ox65”组成。如果表示为 C 字符数组,它将是“fine”,但如果由 TeX 在 PostScript Times-Roman 字体中格式化后显示,它将由 三个 字形组成,“fi”、“n”和“e”,如图 2 所示。
相反,在西班牙语小写字母 enye (ñ) 的某些表示形式中,基本字符和波浪号是单独编码的。如果使用 ISO-8859-1 或 Unicode,则对于西班牙语来说这是不必要的,但在 Unicode 中提供了该功能。它在数学中经常很有用,在数学中,任意含义可以分配给印刷重音符号。图 3 显示了一个字体示例,该字体在任何人类的母语中都没有语义含义,即标准 X 光标字体。
编码是从每个抽象字符或字形到一个或多个八位字节的映射。对于西方语言的字符集和字体的常见编码,仅使用一个八位字节。但是,亚洲语言有数千个表意文字的库;通常,每个字符使用两个八位字节,每个字形使用两个八位字节。两种格式用于这种大型编码。第一种是 宽字符 格式,其中每个字符由相同数量的八位字节表示。示例包括纯日文 JIS 编码和 Unicode(每个字符使用两个八位字节)以及 ISO-10646 UCS-4 编码(Unicode 的计划超集)(每个字符使用四个八位字节)。此编码是字符集或字体的索引映射。
另一种格式是 多字节字符,其中不同的字符可以用不同数量的八位字节表示。一个示例是用于日语的打包扩展 UNIX 代码 (8 位 EUC-JP),其中 ASCII 字符用一个没有设置第八位的八位字节表示,而日文字符用两个八位字节表示。这些八位字节与普通 JIS 编码中的八位字节相同,只是设置了第八位(在伪 C 代码中,euc = jis | 0x8080)。通过使用此编码,任何为 ASCII 设计的 8 位干净编译器都可以用于编译在注释和字符串中使用日语的程序。宽字符格式无法使用此选项。如果程序是用纯 JIS 编写的,则必须重写编译器以接受 JIS ASCII 字符。ASCII 字符集是 JIS 字符集的子集,但字母和数字不是分配范围 0x00 到 0x7F,而是分配由 ASCII 值 + 0x2300 给出的值,并且标点符号分散,没有如此简单的转换。遇到多字节字符的另一个常见地方是在转换格式中,特别是 Unicode 的文件系统安全转换 UTF-8。与 EUC-JP 一样,UTF-8 将 ASCII 字符编码为标准位置的单字节。
多字节格式不会干扰程序不关心内容的文本处理(例如,连接、文件 I/O 和逐字符显示操作),但在内容重要且需要寻址特定字符位置的情况下(例如,字符串比较,排序和搜索的基础),它们的性能会很差或效率低下。对于向后兼容为 ASCII 的约束设计和实现的系统(例如,编译器),它们可能特别有用。如果单字节字符在文本中相对频繁,它们可能会更节省空间。
宽字符格式最适合寻址特定字符位置很重要的情况。它们不能向后兼容为单字节编码设计的系统,尽管通过选择合适的编码(例如,Unicode),除了通过将字符类型扩展到宽字符大小来重新编译之外,可能不需要太多工作。不幸的是,C 等现有语言标准未指定宽字符的大小,仅指定它至少为一个字节。但是,最新设计的语言通常将 Unicode 指定为字符的内部编码,并且大多数系统库指定一个两个字节的宽字符类型,这等效于两个八位字节。
在某些方面,文本输入是文本显示的逆过程。但是,由于计算机在显示图形方面比读取图形方面做得好得多,因此它提出了自身的问题。
文本输入最简单的方法可能是语音。但是,语音输入技术仍处于起步阶段,并且有时直接的文本表示更可取,例如对于数学和计算机编程。可以创建一个高度自适应的系统,但在一段时间内,键入键盘输入将更快、更准确。同样,尽管光学字符识别 (OCR) 和手写识别正在迅速改进,但对于大量文本,键盘输入也将保持更有效率。OCR 和手写识别之间的区别在于,OCR 处理静态二维数据,而手写识别具有动态优势;这在识别东方语言的手写表意文字时尤其重要。但是,这两者都非常接近文本显示的精确逆过程;系统将从物理输入直接映射到内部编码,而无需用户干预。语音和光学技术在 Linux 系统上不可用的事实使这一点在目前变得无关紧要。
出于实际目的,大多数 Linux 系统仅限于键盘输入。与国际化键盘输入相关的几个问题。首先是大多数计算机键盘最多为一种语言设计良好。美国计算机键盘不太适合生成使用重音符号的语言中的字符。但是,明显的解决方案(即添加用于重音字符的键)对于那些有许多重音字符(斯堪的纳维亚语)或上下文相关形式(阿拉伯语)的语言来说效率低下。对于使用表意文字符集(如汉字)或复杂音节表(如韩文)的语言来说,这是不可能的。对于希腊语和俄语等具有自己的字母脚本的语言,可以方便地映射到键盘上,但不能用于编程计算机,应该怎么办?
解决方案是创建输入法,将击键转换为编码文本。例如,在带有 Mule 的 GNU Emacs 中,可以通过击键 "u 在美国键盘上输入字符“带有分音符的拉丁小写字母 u”。但是,这带来了文本 upper 无法直接输入每个字符一个击键的问题。此外,分音符不适合辅音的重音符号;即使对于元音,语言在哪些元音可以用分音符重音符号方面也各不相同。因此,击键 " 的这种用法必须在输入流中与上下文相关,并且必须受语言环境的制约。
对于表意文字的东方语言,该过程甚至更复杂。当然,可以简单地记住编码并直接输入码位,例如,以十六进制形式。对于 ISO-2022 兼容的编码,将双字节表示形式记忆为一对 ASCII 字符更有效率。尽管这种输入方法非常有效率,但需要付出巨大的努力和大量时间才能记住一组有用的字符。受过教育的日本成年人知道大约 10,000 个字符;Unicode 有 20,902 个字符。此外,如果您需要一个您没有记住的罕见字符,则字典查找非常昂贵,因为它必须手动完成。对于这些语言,最流行的方法涉及输入语音转录,然后输入法在内部字典中查找该转录。接受击键、生成编码语音转录并查询字典的函数通常称为 前端处理器,而字典查找通常作为单独的服务器进程实现,称为 后端、字典服务器 或 翻译服务器。
字典服务器通常定义用于优化搜索的复杂协议。在日语中,一些表意文字有数十种发音,一些音节对应 100 多个不同的字符。输入法必须根据上下文(根据字典单词中并列的字符)并通过使用句法线索来筛选候选词。即便如此,相当复杂的输入法也会为给定的音节字符串生成数十个候选词,这并不少见。日语有很多同音异义词,通常具有句法上相同的用法;有时,即使在上下文的帮助下,读者也必须相信作者选择了正确的字符。最近在一个教堂公告中发生了一个有趣的例子,日语单词“megumi”,意思是“(上帝的)恩典”,被转录成一对字符,很容易被解释为后缀,意思是“一群恶棍”。语法用法与名词“megumi”不同,但碰巧的是,在当期文章的上下文中它是可以接受的。只有教堂公告的更广泛背景才使印刷错误显而易见。
显然,大量的用户交互是必要的。大多数日语输入法都涉及向用户呈现一个选项菜单;但是,交互不仅限于此。输入法将为用户提供在字典中注册新单词的方法,并且通常提供一种指定候选列表优先级的方法。此外,字典会根据常用用法预先排序,但复杂的输入法会跟踪每个用户自己的风格,在菜单中尽早显示最常用的候选词。
即使对于欧洲语言中带有重音字符的相对简单的情况,用户通常也对输入法有偏好,因此每个用户都希望自己做出选择。此外,当前没有输入法对两种或三种以上的语言有用。Wnn 是一种最初为日语开发的字典服务器,也使用相同的算法处理中文和韩文,尽管每种语言都由单独的可执行文件提供服务。对于国际化而言,这意味着应用程序和输入法之间通信的协议将非常有用,以便用户可以选择自己喜欢的协议,甚至可以在语言环境更改时动态更改方法。在 X11R6 中,此协议由 X 输入法 (XIM) 标准提供;但是,当前控制台没有可用的此类协议。
虽然本文的重点不是详细讨论输入法本身,但我将描述最常见的输入法用户界面方法。首先,对于非拉丁字母文字,键盘将被简单地重新映射以生成相应的编码字符。X Window 系统和 Linux 控制台都提供了直接的方法来实现这一点。对于新手用户,还需要重新标记键帽;而盲打者甚至不需要这样做。
对于带音标的文字,除非带音标的字符数量非常少,否则不可能将每个字符都分配到自己的键位上。处理音标的一种方法是组合键,这是一个特殊的键,它本身不产生编码字符,而是引导一个按键序列,这些按键序列被解释为一个带音标的字符。组合键方法通常不需要用户调用或关闭;它们始终处于活动状态。由于使用了特殊的键,因此它们不会干扰键盘的母语。音标可以有自己的键,但通常使用一些助记标点符号,例如,撇号被映射为尖音符。
组合键的替代方法是死键方法。某些键被称为死键,因为它们不产生编码字符;相反,它们通过在相邻字符上放置音标来修改该字符。死键方法可以是前缀方法或后缀方法,具体取决于修饰符是在基本字符之前还是之后输入。显然,这些方法会干扰其他语言的输入;因此需要一种打开和关闭它们的方式。
组合键方法类似于使用 Shift 键来大写单个字母;死键方法就像使用大写锁定键。哪种方法更好取决于用户的偏好和任务。键盘重新映射,结合组合键或死键方法,足以处理 ISO 8859 字符集系列的所有字符集。
处理文本是一个极其复杂和多样化的应用领域。目前,本地化的大多数方面以及因此国际化都必须由每个应用程序根据自身的需求来处理。希望其应用程序具有最广泛实用性的程序员应注意国际化问题,尽可能使用诸如 gettext 之类的标准技术。他们应避免优化,例如在字节中使用高位或在编码中使用未使用的代码点来表示非字符信息,这可能会与扩展到新的字符集相冲突。在标准尚未发展完善的领域,国际化要求程序员为需要本地化的应用程序特定功能设计自己的协议。在可能的情况下,应将文本上的复杂操作本地化到少数几个函数,这些函数可以推广到其他语言,或者在新标准被采纳时变得符合标准。
已经有许多领域实现了标准化:数字格式、日期格式、货币格式和排序。是/否答案也已标准化,但这已被 GNU gettext 取代。
这些函数中的每一个都有一个或多个由 POSIX 标准为 C 标准库提供的函数。Linux 的 libc 历史上没有提供它们,但它们或多或少都在 GNU C 库的第 2 版中得到了充分提供。这些函数由区域设置控制,区域设置是一种环境参数,用于编码文本处理的各种文化方面。
区域设置在程序中使用 setlocale 函数显式设置。可以使用相同的 setlocale 函数检索当前区域设置。在内部,每个区域设置都分为几个部分,可以分别控制。用户通常使用一个或多个环境变量(LANG、LC_ALL、LC_COLLATE、LC_CTYPE、LC_MONETARY、LC_NUMERIC、LC_TIME、LC_MESSAGES)告知程序他们的区域设置首选项。
首先,区域设置的命名约定指示了语言、地区子变体和使用的编码。“可移植区域设置”是一个例外;它有两个名称,C 和 POSIX。它们具有相同的含义。语言(ISO 639)和国家/地区(ISO 3166)的两个字母缩写已标准化。因此,例如,美国英语可以指定为 en_US.iso646-irv。(ISO 646-IRV 是 ISO 发布的美国 ASCII 标准版本。)这与 en_US.iso8859-1 略有不同,后者指定使用 ISO 8859-1 编码,如果必要的字体可用,这将允许直接包含来自德语或法语的带音标字母。前者则不允许。这些差异如何处理将取决于具体实现,即使在 Linux 中,控制台驱动程序对此的处理方式也与 X Window 系统不同。
英国英语将指定为 en_GB.iso8859-1。(ISO 646-IRV 在这里是不合适的,因为它不包含英镑货币符号。)美国和英国的区域设置在拼写等方面没有差异。理论上,ispell 可以从 LANG 变量中获取提示,但据我所知,它并没有这样做。主要差异将是货币格式,当然还有日期,即美国使用 MM/DD/YY,而英国使用 DD/MM/YY。此外,Linux 为丹麦提供了英语语言区域设置(反映了 ISO WG15 区域设置库协调员 Keld Simonsen 的国籍);此区域设置使用丹麦克朗作为货币单位,并使用 ISO 8601 标准 YY/MM/DD 作为日期。
另一个例子是几个日语区域设置。虽然日语仅在日本广泛使用,因此所有日语区域设置都以 ja_JP 开头,但有几种常用的编码。日语 Linux 系统上通常使用的区域设置是 ja_JP.eucJP(使用 EUC-JP 编码),但在日语 MS Windows 系统上运行的国际化软件可能会使用 ja_JP.sjis 区域设置(使用 Microsoft 的 Shift-JIS 编码)。出现差异的原因是 UNIX 文件系统与以 EUC-JP 编码的文件名兼容,而 MS Windows 文件系统将使用以略有不同的 Shift-JIS 编码的文件名。这可能会导致在 Linux 系统上挂载的 MS-DOS 和 VFAT 文件系统中出现日语文件名问题,因为 POSIX 区域设置系统不允许同时激活多个区域设置。一些不符合 8 位洁净标准的老式日语系统可能会使用 ja_JP.jis 区域设置,使用基本的七位 JIS 编码。
系统的以下方面受区域设置的影响。前五个方面根据 POSIX 标准在 libc 中实现。其他方面在 X Window 系统中或由应用程序软件 临时 实现。由 libc 实现的那些方面由 setlocale 调用显式控制,这通常会默认为环境变量 LANG 的内容。其余方面作为编码在 LANG 环境变量或其他环境变量中的“建议”来实现。
数字格式
日期格式
货币格式
排序(整理)
是/否消息
显示字体
文件系统编码
文本文件编码
输入法
显示字体通常由应用程序根据区域设置的编码部分(句点之后)设置。俄语、日语和繁体中文都有多种编码。但是,大多数字体仅以一种编码提供,因此应用程序必须在内部重新映射其他编码。如果此重新映射未正确完成,则显示将是无法理解的“乱码”,发音为 MOH-JEE-BAH-KAY,这是一个日语单词,字面意思是“改变的字符”,但更形象地翻译为“怪物字符”。比较图 4 中以 EUC-JP 编码的日文文本与图 5 中明确告知 kterm 将文本解释为 Shift JIS 时产生的标点符号和滑稽字符的混合。这对于从 DOS 移植的程序的用户来说很熟悉,这些程序使用基于 PC 划线字符的文本窗口。由于大多数 Linux 字体都基于 ISO-8859-1 字符集,因此您会得到以花哨的带音标字符而不是线条作为边框的“法式窗口”。
文件系统编码也从 LANG 变量的编码部分中的建议中检索。在这里,至关重要的是应用程序要进行非常防御性的编程。草率地接受建议可能会导致文件名在插入文件系统时损坏或无法访问。
文本文件编码也类似地默认为 LANG 变量的编码部分中的建议。但是,在网络环境中,通常会导入外部文件,例如使用 FTP,并且没有理由假设这些文件将具有与当前区域设置相同的编码。应用程序应提供一种指定所用文件编码的方法;在多种编码可用的区域设置中(今天,日语、俄语和繁体中文,但随着 Unicode 和 UTF-8 的普及,所有区域设置都将如此),应提供用于在兼容编码之间进行转换的实用程序。
最后,通常可以从 LANG 变量中猜测默认输入法。在当前系统中,它通常会与键盘映射或控制台驱动程序捆绑在一起。X Window 系统提供了一个更灵活的系统,其中会查询 XMODIFIERS 环境变量以了解用户为每个区域设置首选的输入法。
下个月,我将研究为解决这些问题而制定的庞大国际化标准体系。
