🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
# 50.2\. 寄语程序员 ## 50.2.1\. 机理 本节描述如何在属于 PostgreSQL 版本的程序或者库里面支持本地语言。 目前它只适用于 C 语言。 **向程序中增加 NLS 支持** 1. 把下面的代码插入到程序的开头: ``` #ifdef ENABLE_NLS #include <locale.h> #endif ... #ifdef ENABLE_NLS setlocale(LC_ALL, ""); bindtextdomain("_progname_", LOCALEDIR); textdomain("_progname_"); #endif ``` (这里的 `_progname_` 实际上可以自由选择。) 2. 如果发现一条需要翻译的消息,那么就需要插入一个对 `gettext()` 的调用。比如 ``` fprintf(stderr, "panic level %d\n", lvl); ``` 会改成 ``` fprintf(stderr, gettext("panic level %d\n"), lvl); ``` (如果没有配置 NLS ,那么 `gettext` 会定义成空操作。) 这么干会出现一堆东西。一种常用的缩写是 ``` #define _(x) gettext(x) ``` 如果程序只通过一个或者少数几个函数做大部分的消息传递,比如后端里的 `ereport()` , 那么也可以用另外一个方法。在这些函数的内部对所有输入字符串调用 `gettext` 。 3. 在程序源代码所在的目录里加一个文件 `nls.mk` 。 这个文件将被当做 makefile 读取。在这里需要做下面一些变量的赋值: `CATALOG_NAME` 那些在 `textdomain()` 调用里提供的程序的名字。 `AVAIL_LANGUAGES` 提供的翻译的语言列表,开始的时候是空的。 `GETTEXT_FILES` 一列包含可翻译字符串的文件,也就是那些用 `gettext` 或者其它相应手段标记了的文件。 最终,这里会包括几乎所有的程序源文件。如果列表太长,你可以把第一个"文件"写成一个 `+` 和第二个词组成,第二个词是一个文件,在这个文件里每行包含一个文件名。 `GETTEXT_TRIGGERS` 生成给翻译者使用的消息表的工具,以便知道哪些函数调用包含可翻译字符串。 缺省时只知道 `gettext()` 调用。 如果你使用了 `_` 或其它标识符,那么你需要把它们列在这里。 如果可翻译字符串不是第一个参数,那么该项需要是这样的形式:`func:2` (用于第二给参数)。 如果函数支持多个消息,那么该项看起来就像这样:`func:1,2`(识别单个和多个消息参数)。 编译系统将自动制作和安装消息表。 ## 50.2.2\. 消息书写指导 这里是一些关于如何书写易于翻译的消息的指导: * 不要在运行时构造语句,比如像 ``` printf("Files were %s.\n", flag ? "copied" : "removed"); ``` 语句里这样的单词顺序会让其它语言很难翻译。 而且,即使你记得在每个片断上调用 `gettext()`,这些片断也不一定能很好地独立翻译。 最好复制一些代码,好让每条消息可以当作有机的整体进行翻译。 只有数字,文件名和类似的运行时变量才应该在运行时插入消息文本。 * 出于类似的原因,下面的东西不能用: ``` printf("copied %d file%s", n, n!=1 ? "s" : ""); ``` 因为它假设了如何形成复数形。如果你看到这样的东西,你可以用下面方法解决 ``` if (n==1) printf("copied 1 file"); else printf("copied %d files", n): ``` 不过还是有让人失望的时候,有些语言在某些特殊规则上有超过两种形式。 通常最好是通过消息的设计避免这些东西,比如你可以这样写: ``` printf("number of copied files: %d", n); ``` 如果你真的要构造一条恰当的复数形消息,也是支持的,但形式上有一点笨拙。 比如当通过`ereport()`生成一条概要或者详细消息时,你可以这样写: ``` errmsg_plural("copied %d file", "copied %d files", n, n) ``` 第一个参数是对应英文单数形的格式字符串,第二个参数是对应英文复数形的格式字符串,第三个参数是决定是否是复数形的一个整数值。 随后的参数和通常一样对应与格式字符串中的参数值。(通常,控制复数形的值也是格式字符串中的其中一个参数。) 英语中只关心`_n_`是不是1,但其它语言中可能有多个复数形。 针对英文中作为一组的2个格式字符串,翻译者能够为根据`_n_`的运行值选中的恰当的那个提供多个替代字符串。 如果你需要一条不直接调用`errmsg`或`errdetail`的复数形消息, 必须使用下层的`ngettext`函数。具体参考gettext的文档。 * 如果你想和翻译者进行交流,比如说一条消息是如何与其它输出对齐的,那么在该字符串出现之前,放上一条以 `translator` 开头的注释,比如 ``` /* translator: This message is not what it seems to be. */ ``` 这些注释都拷贝到消息表文件里,这样翻译者就可以看见它们了。