4.1 国际化概述

关于国际化的动机,大家应该都比较清楚。世界上很多国家或地区都有自己的语言和文字。一个程序要与普通用户进行交互,就应该提供目标用户所能理解的语言文本。同样,每个国家或地区也有自己惯用的日期、时间、数字以及货币的格式,程序应该使用用户惯用的格式来显示这些内容。

每种语言都是由一系列的字符按照某种规则组合而成的。这些字符是语言在发展过程中形成的。这些字符之间可能有内在的排列顺序关系,如英文的26个字母的顺序;也可能只是一个无序的集合,如中文的汉字。在计算机程序中处理输入数据和输出结果的时候,总是免不了与这些字符打交道。如何在计算机程序中表示这些字符,就成了一个需要解决的问题。由于计算机内部对数据的表示都是0和1的序列,表示这些字符的问题实际上就等价于如何在字符与0和1的序列之间建立一个映射关系。

通常的做法是先对由语言中所有字符组成的字符集(character set)按照一定的规则进行编码。最直接的编码方式是为每个字符在给定的范围之内分配一个整数序号。这个整数序号被称为该字符的代码点(code point)。比如一个字符集中包含1000个字符,就可以把这1000个字符分别编号为从1到1000。对于一个字符集来说,其中每个字符的代码点的分配方式是固定的,一般由相关的标准化组织来制定。在经过这种编码之后,之前的字符集就变成了编码字符集(coded character set)。目前存在非常多的编码字符集,开发人员熟悉的包括ASCII、Unicode、GBK和GB2312等。以ASCII为例,英文字母、数字以及常见的符号都在ASCII中有相应的编码,如字母“A”的编码是65,而“a”的编码则是97。当计算机程序处理以ASCII来编码的文本的时候,如果识别出来的编码是65,则认为这是一个字母“A”。在进行输出的时候,用户就会看到相应形状的字符出现。

有了编码字符集之后,下一步要做的就是把字符的代码点映射到计算机中。最直接的映射方式是一一映射,即把字符的代码点直接映射到对应的数字。比如ASCII中的字母“A”就用整数65来表示。这种映射方式直接而易懂,实现起来也很简单,但是并没有考虑字符集中的字符总数和字符的使用频率。不同编码字符集中所包含的字符个数是大不相同的,如ASCII就只有128个字符,而Unicode中可使用的字符数多达1 114 112个。如果使用一一映射的方式,对于ASCII来说,只需要7位就足够了;而对于Unicode来说,则最少需要21位来表示。对于包含字符较多的编码字符集,一一映射的方式带来的问题是造成存储空间上的浪费。以Unicode为例,Unicode字符集包含ASCII字符集中的所有字符。如果使用一一映射,每个Unicode字符都需要使用21位来表示。如果一段Unicode文本只包含ASCII字符,使用ASCII编码就可以完全表示;而使用一一映射方式的Unicode编码所需的空间是使用ASCII编码的3倍。在实际的使用中,ASCII字符的使用频率要远高于其他Unicode字符。所以这种一一映射的方式对于Unicode这样庞大的编码字符集来说并不可取。

从节省空间和提高使用效率的角度出发,对于Unicode这样的编码字符集,更好的办法是采用多个代码单元(code unit)来表示一个字符。一个代码单元是编码时使用的最小单元。每个字符所使用的代码单元个数是不确定的。如果采用8位作为代码单元,那么Unicode中的ASCII字符就可以直接用8位来表示;其他语言中的常用字符可以用16位来表示;另外一些比较少见的字符,可以使用更多的位来表示。通过这种划分方式,既可以保证常用字符所占用的存储空间尽可能少,又不会对表达能力产生影响。