为什么注释和文档应该使用英文?

我一直推荐使用英文来写代码,无论是变量命名、注释还是写文档,甚至是书面的项目沟通。为什么呢?

原因主要是两方面的,包括英语本身的通用性,和当前软件系统对英文的良好兼容。

英语在世界范围内的通用性

英文作为世界范围内较为通用的语言,有其传播上的便利性。使用英文写的注释、文档和项目交流,即使跟全球各地的工程师协作,也可以 不需要过多的事先准备,而直接得以交流。

相比之下,如果一直使用的中文,那么当有不懂得中文的同事、客户加入进来时,我们往往需要做大量的翻译工作,许多甚至是无法翻译的。

变量命名

部分程序语言允许使用中文对变量进行命名,但是所幸使用者寥寥。

class Main {
public static void main(String[] args) {
int 今年 = 2018;
System.out.println(今年);
}
}

如果我们将中文变量名称全部修改成英文,可能需要使用较为智能的 IDE 来协助完成,令其搜索出当前变量所有出现过、使用到的地方。全局替换 的方式非常容易出错,而 手动替换 的方式则显得极为烦琐。

此外,替换完成后需要重新进行全面测试,尤其是一些特性丰富的程序语言,很容易在边边角角有所遗漏。

注释与文档

注释在一个良好的 IDE 或 Text Editor 中是非常容易辨别的,但是翻译的过程可能比较繁琐。由于 注释夹杂于代码之中,而且与代码上下文往往有所关联,因此翻译注释的过程可能也几乎阅读了一遍代码。

相比之下,文档可能稍微轻松一些,大量相同的关键词可能可以使用全局替换的方式直接完成,机器翻译也能帮上不少忙,剩下的内容则取决于原始文档的可读性。无论如何,翻译文档依然是一项非常花时间并且比较无聊的事情。

邮件与论坛交流

当充满收件人的抄送列表中忽然出现了一个英语使用者,整个邮件来往内容 可能一下子变得毫无意义。这时能想到的最好办法,大概是由专门的一两个人,对前面的所有邮件进行翻译,并整理成文档,以附件的形式抄送给收件人列表中的大家。

论坛也是类似。虽然现在机器翻译已经非常发达,但是在完全不懂中文的情况下,将中文直接翻译成英文,尤其是许多 口语化和专业性的语句,往往容易得到与原意截然相反的结果。此时,无论是类似 git 系统的 issue 列表,还是 board 类型的团队进度管理,最好的方式可能依然是由一两个人出来翻译成文档,并放在合适的地方供大家查看。

毕竟,代码可以重写,而邮件总不能依次重新发一遍,论坛也无法重新让所有人依次回复。

代码提交

相比之下,当一个不懂得中文的同事加入到项目中后,最大的困难可能来自于版本控制系统中代码提交历史的消息内容。为了审查以往的任何一次提交,他都需要了解当时提交者所填写的 message 内容。

(上图所示 log 来自于这个公开仓库)

而这,几乎是无法翻译的。即使翻译成文档,也并不利于执行 git 或 svn 的历史相关命令。

当前软件系统对英文的良好兼容

基于种种历史原因与现实便利性,英文在当前的软件系统中其实是非常有优势的。总的来说,就是英文字母全部包含于 ASCII 码表 中,而这避免了大量繁复的字符编码问题。

薛定谔的字符串

如果让你判断下述代码的输出,你认为会是多少呢?

#include <stdio.h>

int main()
{
char ch_zh[] = "撒肯";
char ch_en[] = "SanKo";
printf("%s\nsize_zh=%d\n", ch_zh, sizeof(ch_zh));
printf("%s\nsize_en=%d\n", ch_en, sizeof(ch_en));
return 0;
}

事实上,当这段代码使用相同的编译器、相同的操作系统中时,它的输出也是不确定的。

Linux 与 gcc

在 Linux 下使用 gcc 编译后执行,当文件以 GB2312 保存时,输出为:

ɶ¿
size_zh=5
SanKo
size_en=6

而当文件格式修改为 UTF-8 后,输出为:

撒肯
size_zh=7
SanKo
size_en=6

编码成 Windows 1252 后,字符被截断:

??
size_zh=3
SanKo
size_en=6

若是改成 UTF-16,则直接报了 error:

p.c:1:1: error: stray ‘\377’ in program
ÿþ#
^
p.c:1:1: error: stray ‘\376’ in program
p.c:1:3: error: stray ‘#’ in program
ÿþ#
^
p.c:1:4: warning: null character(s) ignored [enabled by default]
ÿþ#
^
p.c:1:6: warning: null character(s) ignored [enabled by default]
p.c:1:5: error: unknown type name ‘i’
p.c:1:8: warning: null character(s) ignored [enabled by default]
p.c:1:9: error: expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘__attribute__’ before ‘c’
p.c:1:10: warning: null character(s) ignored [enabled by default]
p.c:1:9: error: unknown type name ‘c’
p.c:1:12: warning: null character(s) ignored [enabled by default]
...

Windows 与 Visual Studio

类似的,在 Windows 下使用 Visual Studio 编译后执行,字符编码默认为 UTF-16 (准确来说是 UCS-2LE Unicode 小端序):

撒肯
size_zh=5
SanKo
size_en=6

使用 GB2312 自然没有什么问题:

撒肯
size_zh=5
SanKo
size_en=6

Windows 1252 编码执行后,字符无法显示:

??
size_zh=3
SanKo
size_en=6

修改编码为 UTF-8 后,出现了所谓的「中文乱码」:

鎾掕偗
size_zh=7
SanKo
size_en=6

上述仅是随意举几个例子作为说明,具体的编码格式、操作系统版本、编译器版本不作具体陈述,但对比其输出结果也已可见一斑。

使用中文时,字符长度不确定、显示效果不确定,对开发者和最终用户来说都不够友好。而使用英文时,稳定输出 SanKo size_en=6,非常可靠。

配置文件读写

许多时候我们会通过读写文件的形式来加载、保存配置。简单举例,我们有一个配置文件,来决定是否需要继续执行程序:

继续=是

这个一个非常标准的 key=value 式配置,如果 value 配置成「否」,那么程序将停止;如果配置成「是」,那么程序会继续执行下去。

读取配置的代码如下,在 Linux 下使用 UTF-8 编码编译:

#include <stdio.h>

int main()
{
FILE *fp = NULL;
char buff[255];
fp = fopen("yes.config", "r");
fscanf(fp, "%s", buff);
fclose(fp);
if (strcmp(buff, "继续=是") == 0)
{
printf("continue\n");
}
else
{
printf("stop\n");
}
}

如果配置文件以 UTF-8 编码保存,那么程序输出为:

continue

而当配置文件不小心被用户修改成了其他编码形式,比如用户在 Windows 下编辑后,一定概率会被转换成 GB2312 编码,此时输出结果为:

stop

显然,使用中文配置时,所作的配置项不一定会完全如预期。

解决方案很简单,改成英文即可:

continue=yes

这样,无论配置文件采用何种编码方式,结果都会是 continue

代码提交与差异对比

以 git 为例,首先创建一个文件,采用 UTF-8 编码,内容如下:

撒肯,你好

创建一条提交,之后假定用户将文件编码改成了 GB2312,执行:

git diff

会得到:

--- a/i.c
+++ b/i.c
@@ -1 +1 @@
-撒肯,你好
+<C8><F6><BF>ϣ<AC><C4><E3><BA><C3>

提交后,再次修改该文件,仍然保持 GB2312 编码不变:

撒肯,你好!

这时,diff 的结果是:

--- a/i.c
+++ b/i.c
@@ -1 +1 @@
-<C8><F6><BF>ϣ<AC><C4><E3><BA><C3>
+<C8><F6><BF>ϣ<AC><C4><E3><BA>ã<A1>

也就是说,当使用 GB2312 编码时,我们对这个文件的修改将几乎无法通过 Git 系统来进行版本控制。

如果文字改成英文,那么 diff 的结果将变得非常具有可读性,而无论文件编码是否为 UTF-8

--- a/i.c
+++ b/i.c
@@ -1 +1 @@
-Hello, SanKo
+Hello, SanKo!

由此可见,单字节的英文字符理应是编码过程中一切文件的首选方案。

文件名的窘境

或多或少,我们总能看到这样的文件名:

尤其当我们从网上下载一个文件后,文件名经常出现类似的不知所云。示例中的第一个文件属于 URL 编码,第二个文件是字符编码的异常转换造成的。而如果把文件名换成英文,则完全没有这样的烦恼。

此外,当我们使用命令行的时候,经常会用 tab 来自动补全当前路径下的文件名。这时,如果文件名是以中文开头的,我们就不得不唤出中文输入法,按下多个按键、打出第一个或前几个中文字后,才可以自动补全,这对 tab 提供的 操作连续性 造成了很大的破坏,体验非常差。