KEEP K.I.S.S.

tk's blog

编写国际化软件的一些提示

笔记,摘自 Internationalization Tips,基于国际化工具 GNU gettext

1. 不要分割整句

例如

String s = i18n("Press OK to delete ") + n + i18n(" files");

这样把一句话拆成几部分,在抽取字符串资源后,翻译人员在看到 " files" 这样的字符串时会莫名其妙,翻译到其他语言里很有可能意思不伦不类,表达不清。而且 像 " files" 这样短的词会整个程序中出现多次,并不能保证在每个语境下都是同一个 意思。

这种情况下应该使用占位符

printf(i18n("Press OK to delete %d files"), n);

这里还提到了一个C语言里的技巧:编译时于预处理器会将连续多行的字符串字面量 自动拼接,合并成一个,这样在写很长的字符串时,可以拆成多行来写。

2. 避免同时使用多个占位符

例如

printf("Press OK to %s %d file(s)", action, number);

像这样一条句子里使用了两个占位符,由于后面的参数位置写死在代码里,在翻译时,翻 译字符串中的占位符也必须依照参数的顺序,在某些语言习惯里,可能造成翻译困难或者 不够地道。比如

printf("You should say %s when %s comes.", hello, somebody);

当翻译成中文时

printf("当%s来的时候,你得说%s", somebody, hello);

后面的参数需要调整顺序。

由于C语言的 printf 族函数并不提供类似 C#、Python 那种 {0} 带序号的占位符用法,所以使用多个占位符不是明智之举。虽然标准库不支持,但是有一 些第三方库可以做到,例如 win32 下的 FormatMessage 和 java 的 MessageFormat ,还可以考虑 Qt。

其实个人觉得 Ruby 中的变量名占位符更好,翻译时候翻译人员可以根据有意义的变量名 来翻译,而不是数字。

当然,和作者再三强调的一样,最好的方法就是避免使用多个占位符。

3. 多复数处理

在某些语言中,单数和复数表达可能不一样。

例如

printf(i18n("You have won %d point(s)!"), n);

n == 1 时,输出 "1 points" 就欠妥当,最好修改成

if (n == 1)
    printf(i18n("You have won one point!"));
else
    printf(i18n("You have won %d points!"), n);

注意,这还没完,并不是所有语言就仅仅区分 n == 1n != 1 这两种情况, 有些语言中,当 n == 101 时,也应当使用单数。

用硬编码来处理所有这些情行则会把代码弄得臃肿不堪,gettext 则提供了解决方法:ngettext

4. 翻译歧义

在编码时要注意多义词的处理。比如英文单词 right ,表示方向时是 的意思, 在表示对错时是 的意思。这个时候就需要给字符串加上前缀来以示区别。

作者给了个菜单项的例子,使用 | 来作为前缀

Menu|File
Menu|Printer
Menu|File|Open
Menu|File|New
Menu|Printer|Select
Menu|Printer|Open

然后使用一个新函数来查找翻译字符串

char * sgettext (const char *msgid)
{
    char *msgval = gettext (msgid);
    if (msgval == msgid)
        msgval = strrchr (msgid, '|') + 1;
    return msgval;
}

这个方法有几个缺点

*   包含了 `|` 字符的字符串不能这样来处理
*   翻译人员必须清楚只有 `|` 字符之后的字符串才是翻译的内容
*   开发者也许只用 `sgettext` 函数,并没有注意到字符串是否包含有 `|`

从这点上看,个人认为 MSVS 那套字符串也是有优势的。

推荐一篇文章

Notes on Programming in C

文章讲述C编程里的一些注意点,涉及命名、风格、数据结构。作者是 Rob Pike

其中关于注释、复杂度、数据驱动以及函数指针这几小节特别值得一读。

简要笔记:

注释

作者倾向于少写注释,只写必要的

  1. 代码清晰,命名良好的代码应该是自解释的
  2. 编译器不会检查注释,不能保证注释是正确的,尤其是代码已经修改而注释没有更正
  3. 注释会打乱代码的排版
  4. 注释用于解释全局变量或类型的用法,介绍不寻常或者重要的子程序,将大量计算划分小节
  5. 避免花哨的注释
  6. 给数据写注释要比算法注释有用的多
  7. 如果代码需要注释来帮助理解,那么应该考虑重写以便于理解

复杂度

作者认为大部分程序写的过于复杂源于不好的设计,但是没有讲设计而是给了6条规则

  1. 避免过早优化
  2. 在没有测定程序运行效率前,不要进行优化
  3. 高级算法在数据规模小的时候会变慢,除非确信数据规模很大,不要使用高级算法。就算 数据规模很大,首先考虑规则2
  4. 高级算法比简单算法更难实现,更容易滋生bug,使用简单算法和简单的数据结构
  5. 数据主导。使用了正确的数据结构,那么算法就很清晰明了
  6. 没有规则 6

总结起来2点:杜绝过早优化,数据主导

包含文件

包含文件不应包含已经被包含的文件

避免编译依赖和物理耦合

PS:这里介绍两个技巧

  1. 前向声明,用于避免引用整个头文件
  2. 不透明指针,在C++中常称作 Pimpl idiom,可以完全接偶接口(头文件)和 实现(源文件),修改实现可不必重新编译其他引用了模块的部分,可以用来保证二进制 兼容性(在制作动态库时很有用)。

碎碎

昨晚捣鼓下了 Debian 7.2 Xfce 的 CD,结果被坑了,安装选了中文进入系统是不带中文字体的, 都是方框和HEX数字。后来换了163的源安装字体后好了,大概用了用还是感觉没有 XUbuntu 开箱 即用来的方便。


今晚一直在 XUbuntu 下捣鼓代码,才意识到一个长久以来的问题:我总是很艰难地在 Bash 的输出 里找到从哪里开始是当前命令的输出结果。我想要是能把输入的命令或者提示符修改一个颜色,就容易 分辨了。

在我谷歌一番 change bash prompt color 后,果然找到了一篇文章讲方法。对着方法去修改 .bashrc 文件中的 PS1 环境变量时,发现里面已经有这个功能了:

# uncomment for a colored prompt, if the terminal has the capability; turned
# off by default to not distract the user: the focus in a terminal window
# should be on the output of commands, not on the prompt
#force_color_prompt=yes

取消注释,启用即可...


_(:з」∠)_

秋冷

长久没在线了,这次出差了一个半月,再加上十一在家的时间,基本上算是离线快两个月了。

这次出差不算累,但是压力感觉比较多,神经性皮炎有萌出的迹象。包括现在回来,依旧觉 得自己身体机能上,不是太健康。我自己不是个很能抗压的人,压力之下的思维和心境会很 影响创作和效率。

这么长的离线时间,我觉得对自己是个很好的心境历练,在出差的时候,呆在广州的那种烦躁 感少了很多。也有相当充裕的时间来思考一些问题。


近期列的计划

  1. 继续看《代码大全》
  2. 重读《Progamming in Lua》

但愿能抽得出空...

Windows 下修改 Inkscape 和 Gimp 字体

基于Gtk+的程序运行在Windows下默认字体是很简陋的,修改方法基本是一致的,可以参照这篇文章GTK+ 程序字体的设置(被墙)。

以 Gimp 2.8.6 为例,找到程序安装目录,然后进入 ..\etc\gtk-2.0\ 目录,打开gtkrc.zh_CN(根据系统语言环境后缀名可能不同,比如繁体对应 gtkrc.zh_TW )文件,如果不存在则新建,修改内容为:

style "user-font" { 
font_name = "Microsoft YaHei 10" 
}

widget_class "*" style "user-font"

gtk-font-name="Microsoft YaHei 10"

这样就会使程序的字体变为微软雅黑。

如果不行则说明程序默认没有使用Native Window Syle,这时,打开目录下的 gtkrc 文件,将其内容修改为:

gtk-theme-name = "MS-Windows"

保存后重开程序即可。

PS: 在默认安装下,Gimp 只需前一步即可修改字体,而 Inkscape 则需要第二步才能完成字体修改。