bash スクリプト内メッセージの国際(カタログ)化

はじめに

bash-2.0 から $"..." という記法により ... をロケール依存変換することが可能になりました(NEWS より)。 この文書では、この bash-2.0 以降の $"..." 記法による bash script のメッセージの国際化(カタログ化)について 簡単に説明します。

必要なソフトウェア。

スクリプトのメッセージカタログ化

これから次のシェルスクリプトを例に使っていきます。

#!/usr/bin/env bash
# hello.sh -- say hello script
echo hello world

実行例:

$ ./hello.sh
hello world
$

メッセージのカタログ化

カタログ化したい文字列をダブルクオート(")で括り、 $ をその先頭につけてください。

#!/usr/bin/env bash
# hello.sh -- say hello script
echo $"hello world"

実行例の結果は前と変わりません。

メッセージの抽出

スクリプトを --dump-po-strings オプションを付けた bash で処理します。 処理結果はファイルに保存してください。 通例としてファイル名は .po で終わります。 この --dump-po-strings を利用するには bash 2.02 以降が必要です。 bash 2.02 より前のバージョンでは -D (--dump-strings) を使います。 ただ、-D オプションではカタログ化された文字列を抜き出すことしか できないので、手で po 形式に編集する必要があります。

実行例:

$ bash --dump-po-strings hello.sh >hello.po
$ cat hello.po
#: hello.sh:3
msgid "hello world"
msgstr ""
$

メッセージカタログの作成

次にロケール毎のメッセージカタログファイルを作成します。 ここでは日本語(ja)用のメッセージカタログだけ作成します。 まず先に作っておいた hello.po を ja.po にコピーします。 それから ja.po を編集しましょう。 日本語ロケールでは "hello world" のかわりに "こんにちは 世界" を 使うことにします。 このためには ja.po にある msgstr の引数を "" から "こんにちは 世界" に 変更します。

実行例:

$ cp hello.po ja.po
$ vi ja.po
$ cat ja.po
#: hello.sh:3
msgid "hello world"
msgstr "こんにちは 世界"

注意: この po ファイルは不完全です。 本来は Content-Type で charset を指定すべきです。 詳しくは参考文献にあげた GNU getttext manual を参照してください。

po ファイルのコンパイル

ja.po を bash がメッセージカタログとして直接取り扱える形式に コンパイルする必要があります。 コンパイルには msgfmt を使います。 ファイル名は .mo で終わる必要があります。 通例 ja.po にたいして ja.mo を作成します。

実行例:

$ ls
hello.po  hello.sh  ja.po
$ msgfmt -o ja.mo ja.po
$ ls
hello.po  hello.sh  ja.mo  ja.po
$

mo ファイルの配置

コンパイル結果の ja.mo は メッセージカタログ用のディレクトリに置く必要があります。 glibc2 を採用した GNU/Linux システムでは通常 /usr/share/locale 以下に置く必要があるのですが、 これは環境変数 TEXTDOMAINDIR で変更することができます。 ここではカレントディレクトリ以下に直接置くことにします(TEXTDOMAINDIR=.)。

メッセージカタログ(moファイル)は メッセージカタログ用ディレクトリ以下の 言語を表わすディレクトリ(日本語では ja)の LC_MESSAGES というディレクトリに置く必要があります。 つまり ja.moファイルは ./ja/LC_MESSAGES ディレクトリに置くことになるのですが、 ファイル名は ja.mo ではなくプログラムやパッケージをあらわすものに します。 これは後で TEXTDOMAIN という環境変数で指定することになるのですが、 ここでは hello.mo というファイル名にします。

実行例:

$ mkdir -p ja/LC_MESSAGES
$ cp ja.mo ja/LC_MESSAGES/hello.mo

スクリプトにメッセージカタログを参照させる

ここまで hello.sh -> hello.po -> ja.po -> ja.mo -> ja/LC_MESSAGES/hello.mo という順でメッセージカタログを作成・配置してきました。 hello.sh がこのメッセージカタログを参照するには TEXTDOMAINDIR と TEXTDOMAIN という 二つの環境変数を設定する必要があります。 TEXTDOMAINDIR はメッセージカタログ用ディレクトリを指定します。 この文書の例では TEXTDOMAINDIR=. としてきました。 TEXTDOMAIN は通常プログラム名、 あるいは gnome-core のようなパッケージ名を指定します。 この文書の例では TEXTDOMAIN=hello としました。 つまり ./ja/LC_MESSAGES/hello.mo というファイル名は ${TEXTDOMAINDIR}/言語/LC_MESSAGES/${TEXTDOMAIN}.mo という構成をしているのです。

実行例:

$ LANG=C TEXTDOMAINDIR=. TEXTDOMAIN=hello ./hello.sh
hello world
$ LANG=ja_JP.eucJP TEXTDOMAINDIR=. TEXTDOMAIN=hello ./hello.sh
こんにちは 世界
$ 

スクリプトの改良

以上で bash スクリプトのメッセージをカタログ化することができました。 しかし TEXTDOMAINDIR と TEXTDOMAIN をスクリプトの外で指定しているのは いかにも不細工です。 次にこれをどうにかします。

単純にスクリプト内で環境変数 TEXTDOMAINDIR と TEXTDOMAIN を 指定しても効果がありません。 このため少々トリッキーなことを行う必要があります。 スクリプトの先頭で環境変数 TEXTDOMAIN をチェックし、 スクリプトの TEXTDOMAIN(hello) ではなかった場合、 環境変数 TEXTDOMAINDIR と TEXTDOMAIN を設定し、 自分自身($0) を exec します。 これで TEXTDOMAINDIR と TEXTDOMAIN をスクリプト内に納めておくことが できます。

編集例:

$ vi hello.sh
$ cat hello.sh
#!/usr/bin/env bash
# hello.sh -- say hello script
[ x"$TEXTDOMAIN" = x"hello" ] || TEXTDOMAINDIR=. TEXTDOMAIN=hello exec "$0"
echo $"hello world"

実行例:

$ LANG=C ./hello.sh
hello world
$ LANG=ja_JP.eucJP ./hello.sh
こんにちは 世界
$ 

その他

glibc2 システムでは TEXTDOMAIN=libc とすることで libc のメッセージカタログを参照することができます。

実行例:

$ LANG=ja_JP.eucJP TEXTDOMAIN=libc bash -c 'echo $"No such file or directory"'
そのようなファイルやディレクトリはありません

おわりに

この文書での po/mo ファイルの取り扱いはかなり大雑把です。 詳しくは GNU gettext manual を参照してください。

gettext コマンドがあるので 実のところ bash 組み込みのメッセージカタログ機能を 利用する必要はそれほどないと思いますが、 外部コマンドを使わないという点でメリットがあるでしょう。

参考文献


間違いの指摘などは

ysjj@unixuser.org
まで。


変更履歴

Last modified: Sun Jan 13 22:50:15 JST 2002