GNU编程规范摘要(翻译)

4.2 编写鲁棒的程序

禁止对任意数据结构的长度和数量进行任何的限制,包括文件名,行,文件和符号。通过动态给数据结构分配空间可以避免这种情况的出现。在大多的Unix工具当中,“长行被无声的截断了”。这一点在GNU工具集当中是不能接受的。

读取文件的工具应该不能随意的丢弃NUL字符在内的其他非打印字符。程序在处理多字符编码(例如UTF-8)时候应该正常运行。你可以使用libiconv去处理多种编码。

检查每一个系统调用的error返回值,除非你知道你想要忽略错误。得到错误信息包括由错误调用得到的系统错误文本(strerror),和文件名称(如果有),工具名称。仅仅"cannot open foo.c"或"stat failed"是不够的。

检查所有的malloc或者realloc调用去看它是否返回NULL。即使你要通过realloc获取更小的block也要检查返回值。在将block的大小取整为2的幂的系统当中,在你索取更小block时realloc可以会得到一个不同的block。

你必须认为free会更改已经free的block。你对block的任何获取操作都需要在free之前进行。

如果malloc在一个非交互程序中失败,让它产生一个致命错误。如果一个交互程序(从用户读取数据)中malloc失败,最好中断当前命令并且回到命令读取循环当中。这样可以使用户去杀死其他进程来释放虚拟内存,然后重新输入命令。

使用getopt_long去解码参数,除非参数语法使之不合理。

当程序执行过程中静态储存区要被写入时候,使用显示的C代码初始化它。这样重启程序(不需要重新加载),或者程序的一部分时候会重新初始化这些变量。为不会更改的数据保留C初始化的声明。

尽力避免使用底层接口来掩盖Unix数据结构(例如文件目录,utmp,或者内核内存布局)。如果你需要找到文件夹当中的所有文件,使用readdir或者其他高层接口。这样的兼容性被GNU支持。

我们倾向于使用首选的信号处理工具是BSD信号变体和POSIX sigaction函数。USG的信号接口是低劣的设计

当前使用POSIX的signal函数是使得程序可移植的最简单方法。如果你使用signal,然后在GNU/Linux系统上使用GNU libc version1,那么你需要include bsd/signal.h而不是signal.h,这样可以获取BSD的signal行为。是否支持仅具有USG行为信号的系统,还是放弃这些信号,取决于您自己。

如果错误检测到一个“不可能”的情况,直接终止程序。打印任何信息都毫无意义。这表示代码存在BUG。无论谁想要DEBUG都会去读源码然后跑一个调试器来解决。所以请在注释当中说明这个问题。把相关的数据放在变量当中,这样更方便调试。

不要将错误的个数作为作为程序的退出状态(exit(status))。因为exit status限制在8比特。如果程序有256个错误信息,这样父进程就会发现exit status = 0.这样会误以为程序正确运行了。

如果你使用临时文件,检查TMPDIR环境变量,如果存在这个环境变量,使用这个变量的路径而不是/tmp

另外,把临时文件写在全局可写的目录下存在安全隐患。在C当中,你可以通过使用下面语句创建临时文件避免这个问题:fd=open(filename,O_WRONLY|O_CREAT|O_EXCL , 0600);,或者使用mkstemps函数

在bash当中,可以使用set -C来防止这个问题。另外,mktemp工具是在bash当中创建临时文件更加常用的方法。

4.3 c库编写准则

在编写库函数时候,尽量编写可重入函数。如果他需要动态内存分配,至少尽量去避免除了malloc本身的其他代码的不可重入性。

这里有一些对于库的命名建议去防止名称冲突

给库一个名称前缀,大于两个字符长。所有的对外函数和变量名称都应该包含这个前缀。另外,任何一个库成员都只有一个。这通常意味着将每一个单独放进一个源文件当中。

这里有一个例外,那就是两个外部符号(extern)通常一起使用。所以他们可以放进一个文件当中。

如果这个外部符号不是文档中对于用户的使用接口,他的名字应该以'_'开头。然后'_'后面跟着选定的前缀,这样防止与其他库的冲突。如果你喜欢,这个外部符号可以和其他的用户接口在一个文件当中。

静态函数和变量可以依照你的喜欢命名,不需要适配命令约定。

4.4 错误信息格式化

来自编译器的错误信息应该长这样:

sourcefile:lineno: message

如果你想增加列数,使用这种格式:

sourcefile:lineno:column: message
sourcefile:lineno.column: message

行数应该从文件头从1开始,列数也应该从每一行的行首从1开始(这两个约定都是为了解决兼容性)。列数的计算假定所有的空格和ASCII定义的可打印字符都是相等宽度,并且嘉定tab占8列。对于非ASCII字符,Unicode字宽在UTF-8环境中应该被使用。GNU libc和GNU gnulib提供了wcwidth函数。

错误信息可以给出错误字段的开始和结束位置。这里有好几种样式所以你可以避免行数的冗余信息,例如如下:

sourcefile:line1.colunm1-lin2.colunm2: message
sourcefile:line1.column1-colunm2: message
sourcefile:line1-line2: message

如果错误包含多个文件,可以使用如下格式:

file1:line1.column1-file2:line2.column2: message

从非交互程序产生的错误而且有对应的源码时应该格式如下:

program:sourcefile:lineno: message

当没有源码时:

program: message

如果你想提及列数的话:

program:sourcefile:lineno:column: message

在交互式程序(从终端读取命令)当中,最好不要在错误信息中包含程序名称。可以通过提示或者屏幕布局去说明运行了哪个程序(如果程序从非终端以外的源读取操作,最好视为非交互程序)

句子的开头如果是程序名称或者文件名称时候不应该首字母大写,当然也不应该由一个句号结尾。

交互式程序中的错误信息和其他信息(例如使用说明),应该由大写字母开头,但是不应该由句号结尾。

4.5 一般接口标准

请不要程序调用的名称决定程序的行为。讲程序链接到一个不同的名称通常是有用的,而且这不应该影响程序的行为。

相反,使用一个运行选项或者编译选项或者这两者去决定程序行为。你可以构建一个程序的两个版本,使用不同的名称和不同的默认行为。

同样,不要让程序输入输出设备的类型决定命令行程序的行为。设备的独立性是系统设计的重要原则。不要因为想要避免某人不时的输入某种选项而妥协。

如果你认为一个行为在输出是一个终端设备时是十分有用的,而另一个行为在输出是文件或者管道时是十分有用的,这时候你最好使得默认行为是前者,然后给出一个选择后者的选项。你也可以构建两个名称不同的不同版本的程序。

有一个例外,那就是如果程序输出的是二进制数据,那么把输出指向终端是没有用途的。如果这个程序经常发送输出到stdout,那他应该检测,当stdout对应是一个terminal时候报出一个错误信息。-f选项可以做到无视这个例外,因此保持输出为terminal。

兼容性要求一些程序的行为取决于输出设备的类型。如果lssh没有按照所有用户期望那样做将会是灾难性的。在这种情况下,我们提供了这个程序的另一种选项。例如,我们提供了dir程序很像ls但是他的默认输出格式是多列的格式。

4.6 标准图形接口

如果你的程序提供了图形化用户接口,那么请让它使用X窗口系统,使用GTK+工具集或者GNUstep工具集,除非功能明确需要其他选择(例如在控制台模式显示jpeg)

另外,请提供一个命令行接口来控制功能性。(很多场合下,图形化接口也是一个与命令行程序分开,但是调用命令行程序的程序。)这样就可以做和脚本同样的工作了。

请考虑提供一个D-bus接口来从其他运行的程序得到帮助,例如GNOME。另外,考虑提供一个函数库接口或者一个键盘驱动的命令行接口(为命令行模式用户使用)。

请确保你的程序可以通过接入技术共同操作(例如屏幕读取程序)(https://www.gnu.org/accessibility/accessibility.html)。如果你使用GTK+,这将是自动提供的。

4.7 标准命令行程序接口

非常赞成依照POSIX的命令行选项指导书来编写命令行的选项。最简单的方法就是使用getopt来解析他们。GNU的getopt函数通常可以保证解析命令行选项除非特殊参数--出现。这不是POSIX指定的参数,这是GNU的扩展。

请定义和单一字符的Unix风格选项相对应的长名称选项(--开头),我们希望让GNU在这个方面更加用户友好。这可以通过GNU的函数getopt_long很容易实现。

长名称选项的一个好处就是可以在不同程序当中保持一致性。例如--verbose选项在不同的GNU程序当中都有相同的含义

通常情况下将Input文件名称作为一个单独的选项,但是通过-o选项或者--output指定输出文件。即使你为了兼容性使得输出文件为一个单独的选项,也尽量提供一个选项作为另一个指定他的方式。这样可以为GNU工具提供更多一致性,也更方便用户记忆。

所有的程序都应该提供两个基本的选项,--version--help。CGI程序也应该支持这种命令行参数,并且如果他作为PATH_INFO,也要支持。例如,浏览器访问http://example.org/c.cgi/--help应该打印和p.cgi --help相同的内容。

4.7.1 --version

标准--version选项应该直接是程序打印他的名称,版本,出身和法律地位,然后成功退出。其他的选项和参数应该直接忽略,而且程序不应该执行他的通常行为。

忽略一部分原文

4.7.2 --help

标准--help选项应该输出如何调用程序的简短文档在标准output上,然后成功退出。其他选项在这时应该被忽略,程序也不应该运行他的默认行为。

--help选项的最后一行,请给出email来报告BUG,格式如下:

    Report bugs to: mailing-address
    pkg home page:<https://www.gnu.org/software/pkg>
    General help using GNU software: <https://www.gnu.org/gethelp>

也可以提及其他合适的邮箱和网站。

4.8 动态插件接口标准

另外一个保持自由软件免费的方面是鼓励开发免费插件,并且不鼓励开发专利插件。很多GNU软件没有插件,但是那些有插件的应该遵循这些做法。

首先,一般的插件体系结构设计应将插件与原始代码紧密联系,以使插件和基础程序成为一个扩展程序的一部分。 例如,对于GCC,插件可以接收和修改GCC的内部数据结构,因此显然可以与基础GCC组成扩展程序。

其次,你应该让插件开发者确认插件是在合适的协议下发行的。这应该包含一个简单的程序自动检查过程。对于GCC来说,插件必须定义一个全局符号"plugin_is_GPL_compatible",这样断言插件是在GPL兼容的协议下发行的。

通过将此检查添加到您的程序中,您并不会创建新的法律要求。GPL本身要求插件必须是免费软件,并且必须获得兼容许可。 只要您遵循上述第一条规则以使插件与原始程序保持紧密联系,GPL和AGPL就已经要求这些插件在兼容许可证下发布。插件中的符号定义-或任何其他内容 等效程序在您的程序中效果最好-使得可能分发专有插件以进行法律辩护的任何人都更加困难。 如果有关此事的案件告上法庭,我们可以将其指向该符号,以表明插件开发人员理解许可证具有此要求。

4.9 长选项的表

这里是一个GNU程序使用的长选项的表。他肯定是不全面的,但是我们目的是列出一个新程序想要兼容的所有的长选项的列表。如果你使用名称不在这个表中,请发送你的列表到bug-standards@gnu.org。这样我们可以更新这个表。

忽略一部分原文

4.10 OLD分配

忽略一部分原文

4.11 内存使用

如果程序通常只使用几兆的内存,请不要费力减少内存使用量。 例如,如果出于其他原因对文件长度超过几兆的文件进行操作不切实际,则将整个输入文件读入内存以对其进行操作是合理的

然后,对于cat和tail这种经常在超大文件操作的程序,避免使用会限制文件长度的读取技术之分重要。如果一个程序按行工作,并且可以处理任意用户输入,它应该只在内存中保留一行。因为这样不是很困难,而且用户可能想要处理的总数据量无法全部储存在内存当中。

如果你程序创建复杂的结构体,那么把它放在内存中,如果malloc返回NULL,爆出一个致命错误。

内存分析工具例如valgrind是非常有用的,但是不要仅仅为了避免错误警报而使得程序复杂化。例如,如果一个内存在程序退出前都一直使用,不要仅仅为了使得这个工具不报警告而去free他。

4.12 文件使用

程序应该做好在/usr和/etc都是只读文件的准备。因此,如果一个程序管理log files,lock files,backup files,score files或者其他因为程序内部原因修改的文件时候,不应该储存在/usr和/etc里面。

有两个例外,/etc用来储存系统配置信息。如果程序任务是更新系统配置信息时候是有理由修改/etc目录文件的。另外,如果用户明确要求修改目录中的一个文件,则该程序将其他文件存储在同一目录中是合理的

5 充分利用C

这一章阐述了如何在编写GNU软件时充分利用C语言

5.1 使你的程序格式化

请把源文件的行长度保持在79个字符之内,这是在最宽的环境下最大的可读的长度。

重要的是,将用于启动C函数主体的开括号放在第一列中,以便它们可以启动defun。 有几种工具在第一栏中寻找大括号,以查找C函数的开始。 这些工具不适用于未格式化的代码

当它们在函数中时,请避免在第一列中放置大括号,以免它们启动defun。 如果您发现将定义视为defun有用,则开始结构体的开放式括号可以进入第一列

对于函数定义,在第一列中开始函数的名称也很重要。 这可以帮助人们搜索功能定义,也可以帮助某些工具识别它们。 因此,使用标准C语法,格式为:

static char *
concat (char *s1,char *s2)
{
    ...
}

如果是传统的c语法

static char *concat (s1, s2)        /* Name starts in column one here */
    char *s1, *s2;
{                     /* Open brace in column one here */
    ...
}

标准C中,如果参数太长了不适应一行,这样分开:

int
lots_of_args (int an_integer, long a_long, short a_short,
              double a_double, float a_float)

对于结构体或者枚举,也同样把大括号放在第一列,除非他可以只有一行。

struct foo
{
    int a,b;
}

或者

struct foo { int a, b; }

忽略一部分原文

对于函数的主体,我们建议风格如下:

if (x < foo (y, z))
    haha = bar[4] + 5;
else
    {
        while (z)
            {
                haha += foo (z, z);
                z--;
            }
        return ++x + bar();
    }

我们发现当大括号前面有空格时候更方便阅读程序,尤其是逗号后面。

当你把一个表达式分开成多行时候,在操作符前分开他,而不是操作符后面:

if (foo_this_is_long && bar > win (x, y, z)
    && remaining_condition)

避免使得两个优先级不同的运算在同一个缩进等级上,例如,不要这样写:

mode = (inmode[j] == VIODmode
        || GET_MODE_SIZE (outmode[j])) > GET_MODE_SIZE (inmode[j])
        ? outmode[j] : inmode[j]);

使用额外的括号去改进:

mode = ((inmode[j] == VIODmode
        || (GET_MODE_SIZE (outmode[j])) > GET_MODE_SIZE (inmode[j])))
        ? outmode[j] : inmode[j]);

忽略一部分原文

do-while格式如下:

do
    {
        a = foo (a);
    }
while (a > 0);

请使用换页符(ctrl-L)把程序按照逻辑分成几页。这样因为它不需要去试配一个打印页,所以不论一页有多长。换页附应该单独占一行。

5.2 给你的工作添加注释

忽略一部分原文

5.3 干净的使用C结构体

请显式的声明所有的对象。例如,你可以显式的声明函数的所有参数,而且你应该声明函数返回int而不是忽略int

有一个程序员喜欢使用GCC-Wall选项,并且在出现warning时修改代码。如果你想这么做,那就做。有一些程序员倾向于不使用-Wall因为他不想修改那些报出warning但是有效合法的代码。如果你想这么做,那就做。编译器应该是你的服务员,而不是主人。

不要为了使用静态代码分析工具例如lint,clang和GCC外加-Wconversion和-Wundef而让代码变丑。这些工具可以帮助找到bug和不明确的代码。但是他们也会生成很多很多错误警告,如果使用不必要的强制类型转换,包装或者其他复杂手段来去掉那些警告,会使得代码变得难看。例如,请不要加入强制转换到void的语句,或者使用空函数仅仅为了满足lint

external函数的声明和在源文件稍后出现的函数的声明应该在头文件开始的某个地方(在第一个函数定义之前),或者在头文件当中。不要在函数中添加外部声明external

对于一个函数中的不同值,一遍又一遍地使用相同的局部变量(如temp等名称)是一种常见的做法。与其这样做,不如为每个不同的目的声明一个单独的局部变量,并为其赋予一个有意义的名称,这不仅使程序更易于理解,而且还有助于良好的编译器进行优化。与其这样做,不如为每个不同的目的声明一个单独的局部变量,并为其赋予一个有意义的名称,这不仅使程序更易于理解,而且还有助于良好的编译器进行优化。这样可以使得程序更加干净。

不要使用局部参数来屏蔽全局标识符(例如使用局部变量屏蔽全局变量)。GCC的-Wshadow选项可以检测到这个问题。

不要使用这样声明变量:

int foo,
    bar;

使用这种方法:

int foo, bar;

或者

int foo;
int bar;

(如果他们是全局变量,应该有一个注释来解释他)
如果你使用if-else语句在另外一个if-else里面,不要忽略外层大括号,不应该这样:

if (foo)
    if (bar)
        win();
    else
        lose();

应该这样

if (foo)
    {
        if (bar)
            win();
        else
            lose();
    }

不要把else和if放在一行,不应该这样:

if (foo)
    ...
else if (bar)
    ...

应该这样:

if (foo)
    ...
else
    {
        if (bar)
            ...
    }

不要在同一声明中同时声明结构标记和变量或typedef。相反,请分别声明结构标记,然后使用它来声明变量或typedef

尽量避免在if条件内分配(while条件内的分配是可以的)。 例如,不要这样写:

不要这样写:

if ((foo = (char *) malloc (sizeof *foo)) == NULL)
    fatal("virtual memory exhausted");

这样写:

foo = (char *) malloc (sizeof *foo);
if (foo == NULL)
    fatal("virtual memory exhausted");

5.4 命名变量,函数和文件

全局变量,函数在程序中的名字充当了部分注释的作用。不要使用简洁的命名 - 相反,使用有帮助的名称。

局部变量名称可以更短,因为它们仅在一个上下文中使用,(在注释中)注释可以解释其用途。

尝试限制在符号名称中使用缩写。 可以缩写一些,解释它们的意思,然后经常使用,但不要使用太多晦涩的缩写

请使用下划线将名称中的单词分开,以便Emacs单词命令可以在其中使用。 坚持小写; 为宏和枚举常量以及遵循统一约定的名称前缀保留大写。

例如,你应该使用ignore_space_change_flags,不要使用iCantReadThis。

那些用来只是一个命令行参数是否被指定的标识参数应该在参数的意义(注释)后面定义,而不是option-letter后面,例如:

/* Ignore changes in horizontal whitespace (-b).  */
int ignore_space_change_flag;

当你想要使用deine一个名称到一个整数值时,使用enum而不是#define。GDB知道枚举的常量。

你可能想要确保没有一个文件名称在载入MS-DOS文件系统中不起冲突(83格式),你可以使用doschk去测试。

某些GNU程序旨在将其自身限制为14个字符或更少的文件名,以避免在将文件读入较早的System V系统中时出现文件名冲突的情况。 请在具有此功能的现有GNU程序中保留此功能,但是在新的GNU程序中无需这样做。doschk还报告文件名超过14个字符。

5.5 系统类型的可移植性

在Unix的术语当中,“移植性”代表移植到不同的Unix版本。对于GNU程序,这种可移植性是可取的,但不是最重要的。

GNU软件的主要目的是在各种类型的CPU上运行GNU C编译器编译的GNU内核。 因此,绝对必要的可移植性种类非常有限。 但是,支持基于Linux的GNU系统非常重要,因为它们是流行的GNU形式。

除此之外,最好还支持其他免费操作系统(* BSD),并且如果愿意的话,不支持其他类似Unix的系统。支持各种类Unix系统是理想的,虽然不是最重要的。 通常不会太难,所以您也可以这样做。 但是,即使事实证明这很困难,您也不必将其视为一项义务

实现对大多数类Unix系统的可移植性的最简单方法是使用Autoconf。 很有可能您的程序需要比Autoconf所能提供的更多有关主机平台的信息,这仅仅是因为大多数需要这种知识的程序已经编写好了。

当这里有一个更高级的可选项时(readdir)。避免使用半内部的数据格式(例如directories)。

忽略一部分原文

在编译C文件时定义“功能测试宏” _GNU_SOURCE是一个好主意。当你在GNU或者GNU/Linux上面编译时候,这会使能GNU扩展函数(如果你想让程序更具有扩展性,你可以不必使用这些函数)

5.6 CPU之间的移植性

即使是GNU系统也会在不同的CPU类型之间产生差异 - 例如,不同的字节顺序和对齐要求。处理这些差异十分重要。但是不需要担心int会小于32bits,我们不支持16位系统。

你不需要担心long会小于指针长度或者size_t。我们知道64位Microsoft Windows是一个例外,他们使用8字节指针和4字节long。但是是否去支持Mingw64,或者说Windows,使你的选择,我们GNU项目没有责任这样做。我们任务是去取代所有权系统,包括Windows,而不是去加强他们。如果有人迫使你去运行你的程序在Windows上面,而且你对此没有兴趣,你可以这样回复:“Switch to GNU/Linux – your freedom depends on it”

预定义的用来描述文件大小的类型off_t是一个例外,他们总是在很多平台上面比long长,所以。一个去打印off_t类型的方法就是一个一个数字的print他。

不要去假设int的地址是它最低有效字节的地址。这在大端平台上面是错误的,所以,不要这样出现下面这种错误:

int c;
...
while ((c=getchar ()) != EOF )
    write (file_descriptor, &c ,1);

相反,使用unsigned char(unsigned是因为对于某些不寻常的系统,那些系统的char是有符号的而且还有整数overflow检查)

int c;
while ((c = getchar()) != EOF)
    {
        unsigned char u = c;
        write (file_descriptor, &u ,1);
    }

如果你可以,避免将指针强制转换为整数。这样的转换很大的降低了移植性,而且大多程序中这很容易避免。在一些讲指针转换为整数很重要的场合(例如:Lisp的解释器在一个word里面储存了类型和地址两个信息。)您必须做出明确的规定来处理不同的word size。你还需要做好在一些系统上malloc会得到一个很大地址值的准备。

5.7 调用系统函数

从历史上看,C的实现有很大的不同,许多系统缺少ANSI / ISO C89的完整实现。 但是,如今,所有实用系统都具有C89编译器,并且GNU C支持几乎所有的C99和某些C11。 同样,大多数系统都实现POSIX.1-2001库和工具,并且许多系统都具有POSIX.1-2008。

因此,几乎没有理由支持旧的C或非POSIX系统,并且您可能想利用标准C和POSIX编写更清晰,更可移植或更快的代码。 您应该尽可能使用标准接口。 但是如果GNU扩展使您的程序更具可维护性,功能强大或更好,那么请不要犹豫使用它们。 在任何情况下,请勿自行声明系统功能。

尽管有标准,在一些系统上几乎所有的库函数都有一些可移植性的问题。这里有一些例子。

open 带有/尾巴的名称在很多平台上面处理不当。
printf long double可能没有实现。浮点数的NaN和无限大经常被错误处理。高精度的输出可能不对。
readlink 可能返回int而不是size_t
scanf 在Windows上面,errno没有在错误时候设定。

Gnulib是一个在这个方面的巨大帮助。Gnulib在许多缺少标准接口的系统上提供了标准接口的实现,包括增强的GNU接口的可移植实现,从而使其易于使用,以及POSIX-1.2008接口,其中有些甚至在最新的GNU系统上也缺少。

Gnulib还提供了许多有用的非标准接口。 例如,标准数据结构(哈希表,二进制树)的C实现,用于内存分配函数的错误检查类型安全包装器(xmalloc,xrealloc)以及错误消息的输出

Gnulib与GNU Autoconf和Automake集成在一起,从而减轻了程序员编写可移植代码的负担:Gnulib使您的配置脚本自动确定缺少的功能并使用Gnulib代码提供缺少的部分.Gnulib和Autoconf手册内容广泛 有关可移植性的部分:Gnulib中的“简介”部分,以及Autoconf中的“可移植C和C ++”部分。 请咨询他们以获取更多详细信息

5.8 国际化

gnu有一个叫做gettext的GNU库函数可以用来简化将信息转化为不同语言的负担。你可以在所有的程序当中使用这个函数。在程序当中使用英文,然后使gettext将文字转化为用户的语言。

使用GNU的gettext需要在每一个需要转化的字符串外面调用一个名称叫做gettext的宏,就像这样子一样:

printf (gettext ("Processing file '%s' ..."), file);

这样子允许GNU的gettext把字符串替换称合社的语言。

一旦程序使用了gettext,请在添加需要翻译的新字符串时对gettext进行调用。

在package中使用gettext需要指定一个text domain name给package。这个text domain name用来把这个package的翻译和其他package的翻译分开。一般情况下,text domain name应该和package的名称相同,例如,coreutils是GNU核心工具集的text domain name

为了使gettext正常工作,请避免编写假设单词或句子结构的代码。 当您希望句子的精确文本根据数据而变化时,请使用两个或多个替代字符串常量,每个常量都包含不完整的句子,而不是将条件化的单词或短语插入单句框架中

这是我们不应该做的事情:

printf ("%s is full", capacity > 50000 ? "disk" : "floppy disk");

如果这样修改句子

printf (gettext ("%s is full“),
        capacity > 50000 ? gettext ("disk") : gettext ("floppy disk"));

译者几乎不知道“disk”和“floppy disk”将被替换到另一个字符串。 更糟糕的是,在某些语言(例如法语)中,这种构造无法正常工作:“ full”一词的翻译取决于句子第一部分的性别; 它可能与“disk”和“floppy disk”不同

这样子句子将会没有问题:

printf (capacity > 50000 ? gettext ("disk is full")
        : gettext ("floppy disk is full"));

一个类似的问题如下:

printf ("#  Implicit rule search has%s been done.\n",
        f->tried_implicit ? "" : " not");

把gettext加入到这个代码当中将不会得到正确结果,因为在很多语言当中,否定都不会值加入一个单词在句子当中。所以这样会导致翻译的问题。所以我们建议:

printf (f->tried_implicit
        ? "#  Implicit rule search has been done.\n",
        : "#  Implicit rule search has not been done.\n");

还有一个类似的问题是:

printf ("%d file%s processed", nfiles,nfiles != 1 ? "s" : "");

如果你这样修改它:

printf ((nfiles != 1 ? gettext ("%d files processed"): gettext ("%d file processed")),nfiles);

在大多数情况下是可以的。但是对于Polish却不是很正确:一种格式对应nfile == 1,一种格式对应nfile == 2,3,4,22,23,24 … ,还有一个对应剩下的所有。GNU的ngettext函数可以用来解决这个问题。

printf (ngettext ("%d files processed", "%d file processed", nfiles),nfiles);

5.9 字符集

使用ASCII(7-bits字符,明文)是GNU倾向在代码注释,文档和其他文档中使用的,除非有理由去因为应用程序的使用域而去使用一些其他的。例如,如果源代码处理的是法国革命日历,则其文字字符串包含月份名称(例如“Floŕeal”)中的重音字符就可以了。 同样,可以(但不是必需)使用非ASCII字符来表示更改日志中贡献者的正确名称。

如果你想要使用non-ASCII字符集,你应该使用一种编码格式,一定是在单个文件当中。UTF-8是最佳的选择。

5.10 引号字符

在C语言环境中,对于给用户的消息中的引号字符,GNU程序的输出应坚持纯ASCII:最好用0x22(’“’)或0x27(’’’)来表示打开和关闭的引号。尽管GNU程序传统上使用0x60(’`’)来打开引号,而使用0x27(’’’)来结束引号,但是如今引号’‘like this’通常是不对称呈现的,因此引用’“ like this”'或’like this ''通常看起来更好

GNU程序可以在非C语言环境中生成特定于语言环境的引号,但这不是必需的。 例如:

printf (gettext ("Processing file '%s' ... "), file);

如果是翻译到法文的话就会变成"Traitement de fichier < %s >…",产生更适合法国语言的翻译。

有时,一个程序可能需要直接使用左引号和右引号。 通过约定,gettext将字符串““`””转换为开头的引号,并将字符串““’””转换为结尾的引号,程序可以使用这些翻译。 通常,尽管如此,最好在较长的字符串的上下文中翻译引号字符。

如果程序的输出很可能会被另一个程序解析,则最好提供一个使该解析可靠的选项。 例如,您可以使用C语言或Bourne Shell中的约定对特殊字符进行转义。 例如,参见GNUls的–quoting-style选项。

忽略下面的内容

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值