本节介绍如何在 PostgreSQL 发行版所含的程序 或库中实现本地语言支持。目前它只适用于 C 程序。
为程序添加 NLS 支持
将以下代码插入程序的启动序列中:
#ifdef ENABLE_NLS
#include <locale.h>
#endif
...
#ifdef ENABLE_NLS
setlocale(LC_ALL, "");
bindtextdomain("progname", LOCALEDIR);
textdomain("progname");
#endif
(progname 实际上可以自由选择。)
在发现任何候选翻译消息的地方,都需要插入对
gettext() 的调用。例如:
fprintf(stderr, "panic level %d\n", lvl);
应改为:
fprintf(stderr, gettext("panic level %d\n"), lvl);
(如果未配置 NLS 支持,gettext 被定义为空操作。)
这往往会造成大量杂乱。一个常见的捷径是使用:
#define _(x) gettext(x)
如果程序的大部分通信都通过一个或少数几个函数进行,例如后端的
ereport(),那么还有另一种可行方案。此时让该
函数在内部对所有输入字符串调用 gettext 即可。
在程序源代码所在目录中添加一个文件 nls.mk。
此文件将作为 makefile 读取。需要在这里进行以下变量赋值:
CATALOG_NAME
程序名,即 textdomain() 调用中提供的
名称。
GETTEXT_FILES
包含可翻译字符串的文件列表,即那些用 gettext
或替代方案标记过的文件。最终这将包括程序的几乎所有源文件。如果
这个列表过长,您可以把第一个“文件”设为一个
+,第二个词设为一个每行包含一个文件名的
文件。
GETTEXT_TRIGGERS
为译者生成待处理消息目录的工具需要知道哪些函数调用包含可翻译
字符串。默认只识别 gettext() 调用。如果您
使用了 _ 或其他标识符,需要在这里列出它们。
如果可翻译字符串不是第一个参数,条目需要采用
func:2(对应第二个参数)的形式。
如果您有一个支持复数消息的函数,条目应形如
func:1,2(标识单数和复数消息参数)。
添加一个文件 po/LINGUAS,其中包含已提供翻译的
列表 — 起初为空。
构建系统会自动负责构建和安装消息目录。
以下是一些编写易于翻译的消息的指南。
不要在运行时构造句子,例如:
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 还是不等于 1 的区别,但其他
语言可能有许多不同的复数形式。译者会把这两个英语形式作为一组看待,
并有机会提供多个替换字符串,运行时会根据 n
的值选择合适的一个。
如果您需要为一条不直接进入 errmsg 或
errdetail 报告的消息做复数处理,就必须使用
底层函数 ngettext。参见 gettext 文档。
如果您想向译者传达某些信息,例如关于某条消息预期如何与其他输出
对齐,请在字符串出现的位置之前放置一条以 translator
开头的注释,例如:
/* translator: This message is not what it seems to be. */
这些注释会被复制到消息目录文件中,以便译者看到。