Appendix I. 本地化

本地化是Bash的一个未文档化的特征.

对于一个本地化的shell脚本来说, 它的输出都会使用本地系统所定义的语言. 对于一个德国柏林的Linux用户来说, 他的脚本会输出德文, 而对于他在马里兰的堂兄来说, 同样运行这个脚本, 输出就是英文.

为了创建一个本地化的脚本, 可以使用下面的模版来编写所有的用户消息(错误消息, 提示符, 等等.).

  1 #!/bin/bash
  2 # localized.sh
  3 #  此脚本由Stephane Chazelas编写, 
  4 #+ Bruno Haible进行了修改, Alfredo Pironti修正了bug. 
  5 
  6 . gettext.sh
  7 
  8 E_CDERROR=65
  9 
 10 error()
 11 {
 12   printf "$@" >&2
 13   exit $E_CDERROR
 14 }
 15 
 16 cd $var || error "`eval_gettext \"Can\'t cd to \\\$var.\"`"
 17 #  $var前面之所以需要三个反斜线(转义)
 18 #+ "因为在变量值还没被替换之前, 
 19 #+ eval_gettext需要一个字符串."
 20 #    -- per Bruno Haible
 21 read -p "`gettext \"Enter the value: \"`" var
 22 #  ...
 23 
 24 
 25 #  ------------------------------------------------------------------
 26 #  Alfredo Pironti注释: 
 27 
 28 #  这个脚本已经被修改, 
 29 #+ 使用"`gettext \"...\"`"语法形式替换了$"..."语法形式. 
 30 #  这么做没问题, 但是在新的localized.sh程序中, 
 31 #+ 命令"bash -D filename" and "bash --dump-po-string filename"
 32 #+ 将不会产生输出
 33 #+ (因为那些命令只会搜索$"..."字符串)!
 34 #  从新文件中提取字符串的唯一方法就是使用'xgettext'程序. 
 35 #  然而, xgettext程序存在许多bug. 
 36 
 37 # 注意'xgettext'还有一个bug. 
 38 #
 39 # shell片断: 
 40 #    gettext -s "I like Bash"
 41 # 将会被正确的提取, 但是 . . .
 42 #    xgettext -s "I like Bash"
 43 # . . . 失败!
 44 #  'xgettext'将会提取"-s"
 45 #+ 因为这个命令仅仅会提取
 46 #+ 'gettext'后边的第一个参数. 
 47 
 48 
 49 #  转义字符:
 50 #
 51 #  为了本地化一个句子, 就像
 52 #     echo -e "Hello\tworld!"
 53 #+ 你必须使用
 54 #     echo -e "`gettext \"Hello\\tworld\"`"
 55 #  `t'前边的"双转义字符"是必须的, 
 56 #+ 因为'gettext'将会搜索那些字符串(就像'Hello\tworld')
 57 #  这是因为gettext将会读取一个字符`\')
 58 #+ 并将输出一个字符串(就像"Bonjour\tmonde"), 
 59 #+ 所以'echo'命令将会正确的显示消息. 
 60 #
 61 #  你可能不想使用
 62 #     echo "`gettext -e \"Hello\tworld\"`"
 63 #+ 因为我们上面解释的xgettext的bug. 
 64 
 65 
 66 
 67 # 让我们本地化下面的shell片断:
 68 #     echo "-h display help and exit"
 69 #
 70 # 首先, 可以用:
 71 #     echo "`gettext \"-h display help and exit\"`"
 72 #  这样'xgettext'工作正常, 
 73 #+ 但是'gettext'程序将会把"-h"当作选项来读取! 
 74 #
 75 # 一个解决方法是
 76 #     echo "`gettext -- \"-h display help and exit\"`"
 77 #  这样'gettext'工作正常, 
 78 #+ 但是'xgettext'将会提取"--", 就像上边那样. 
 79 #
 80 # 为了获得这个本地化的字符串, 你可能使用的变通方法就是: 
 81 #     echo -e "`gettext \"\\0-h display help and exit\"`"
 82 #  我们已经在这句的开头添加了\0 (NULL). 
 83 #  这样'gettext'能够正确工作, 就像'xgettext'一样.
 84 #  此外, NULL字符将不会修改
 85 #+ 'echo'命令的行为. 
 86 #  ------------------------------------------------------------------

bash$ bash -D localized.sh
"Can't cd to %s."
 "Enter the value: "
这将列出所有的本地化文本. (-D选项将会列出以$为前缀, 并且使用双引号引用起来的字符串, 而不会执行这个脚本.)

bash$ bash --dump-po-strings localized.sh
#: a:6
 msgid "Can't cd to %s."
 msgstr ""
 #: a:7
 msgid "Enter the value: "
 msgstr ""
Bash的--dump-po-strings选项与-D选项很相似, 但使用gettext "po"格式.

Note

Bruno Haible指出:

以gettext-0.12.2开始, xgettext -o - localized.sh被推荐代替bash --dump-po-strings localized.sh, 因为xgettext . . .

1. 了解命令gettext和eval_gettext(而bash --dump-po-strings只认识它的$"..."语法)

2. 可以提取程序中的注释, 进而可以被翻译者读取.

这个脚本将不再被特定于Bash, 它与Bash 1.x和其他的/bin/sh实现, 都使用相同的方式工作.

现在, 为每种脚本需要被转换的语言都建立一个language.po文件, 指定msgstr. Alfredo Pironti给出了下面的例子:

fr.po:
  1 #: a:6
  2 msgid "Can't cd to $var."
  3 msgstr "Impossible de se positionner dans le repertoire $var."
  4 #: a:7
  5 msgid "Enter the value: "
  6 msgstr "Entrez la valeur : "
  7 
  8 #  这个字符串和变量名被打印, 没有%s语法, 
  9 #+ 与C程序很像. 
 10 #+ 如果程序员使用有意义的变量名, 
 11 #+ 那么这将会是一个非常酷的特点!

然后, 运行msgfmt.

msgfmt -o localized.sh.mo fr.po

将文件localized.sh.mo的结果放到/usr/local/share/locale/fr/LC_MESSAGES目录下, 并且在脚本的开头插入如下行:
  1 TEXTDOMAINDIR=/usr/local/share/locale
  2 TEXTDOMAIN=localized.sh

如果法文系统上的用户运行这个脚本, 那么她将得到法文消息.

Note

在老本的Bash或其他shell中, 本地化需要使用-s选项的命令gettext. 在这种情况下, 脚本为:

  1 #!/bin/bash
  2 # localized.sh
  3 
  4 E_CDERROR=65
  5 
  6 error() {
  7   local format=$1
  8   shift
  9   printf "$(gettext -s "$format")" "$@" >&2
 10   exit $E_CDERROR
 11 }
 12 cd $var || error "Can't cd to %s." "$var"
 13 read -p "$(gettext -s "Enter the value: ")" var
 14 # ...

变量TEXTDOMAINTEXTDOMAINDIR需要被设置, 并且需要export到环境变量中. 这应该在脚本中完成.

---

此附录由Stephane Chazelas编写, Alfredo Pironti, 和Bruno Haible给出了一些建议, 是GNUgettext的维护者.