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_的运行值选中的恰当的那个提供多个替代字符串。

    如果你需要一条不直接调用errmsgerrdetail的复数形消息, 必须使用下层的ngettext函数。具体参考gettext的文档。

  • 如果你想和翻译者进行交流,比如说一条消息是如何与其它输出对齐的,那么在该字符串出现之前,放上一条以 translator 开头的注释,比如

    /* translator: This message is not what it seems to be. */
    

    这些注释都拷贝到消息表文件里,这样翻译者就可以看见它们了。