GNU gettext の自分用メモ
・最初のソースコード準備からpoファイル作成、moファイルへのコンパイルまで
・ソースコードが変更された場合のメンテナンス作業
GNU gettext HTMLドキュメント, "1.5 Overview of GNU gettext" よりファイル関連図を抜粋する(元の図から、"PO Compendium"という要素が分かりづらかったので取り除いてあります)(*1)。
Original C Sources ─> Preparation ─> Marked C Sources ──┐ │ ┌─<── GNU gettext Library │ ┌── make <──┤ │ │ └─<─────────────┬─────┘ │ │ │ ┌─<─ PACKAGE.pot <─ xgettext <──┘ │ │ │ │ │ └─────>─────┐ │ │ ├─> PO editor ─┐ │ ├─> msgmerge ──> LANG.po ─>─┘ │ │ │ │ │ └───<───<───┐ │ │ ├─── New LANG.po <────┘ │ ┌─ LANG.gmo <─ msgfmt <─┘ │ │ │ └─> install ──> /.../LANG/PACKAGE.mo ──┐ │ ├───> "Hello world!" └─> install ───> /.../bin/PROGRAM ─────┘
下記のような簡単なサンプルコードに対して、徐々にgettext対応を肉付けしていく。
test.c:
#include <stdio.h> char *msg = "GNU gettext excersize"; int main() { puts("Hello, World!"); puts(msg); return 0; }
$ gcc -Wall -o test test.c $ ./test Hello, World! GNU gettext excersize
例えば次のようなメッセージであれば、
puts("Hello, World!");
このようにするのが基本となる。
puts(gettext("Hello, World!"));
ただしこのままだとキータイプ数が多い上、test.cにおける
char *msg = "...";
にも対応できない。グローバル変数に対して下記のようなコードは書けない。
char *msg = gettext("...");
このため、GNU gettext HTMLドキュメントでは次のマクロを定義する事を奨めている。
まず、普通に "puts(...)" に埋め込めるマクロを定義する。
#define _(String) gettext (String)
次にグローバル変数の初期化などに使うマクロを定義する。
#define gettext_noop(String) (String) #define N_(String) gettext_noop (String)
このマクロを次のように埋め込む事で、グローバル変数の初期値に対してもマクロを適用できる。なぜなら、結局"N_"マクロは何も加工しないからである。
char *msg = N_("GNU gettext excersize"); ... puts(_(msg));
何も加工しない"N_"マクロをわざわざ定義したのは、続く"xgettext"でも検出できるようにする為である。
これで、test.cは次のようになる。
#include <stdio.h> #define _(String) gettext(String) #define N_(String) gettext_noop(String) #define gettext_noop(String) (String) char *msg = N_("GNU gettext excersize"); int main() { puts(_("Hello, World!")); puts(_(msg)); return 0; }
setlocale()では環境変数からロカール名を取得するようにする。bindtextdomain(), textdomain()でドメイン名とメッセージカタログディレクトリを設定する。ドメインとは、gettext()の翻訳するmsgidのセットのこと。
今回はドメイン名は"test", メッセージカタログディレクトリは"."起点とした。
#include <stdio.h> /* setlocale() */ #include <locale.h> /* bindtextdomain(), textdomain() */ #include <libintl.h> #define _(String) gettext(String) #define N_(String) gettext_noop(String) #define gettext_noop(String) (String) char *msg = N_("GNU gettext excersize"); int main() { /* ロカール名を環境変数から取得 */ setlocale(LC_ALL, ""); /* ドメイン名"test"のメッセージカタログディレクトリをカレントディレクトリに設定 */ bindtextdomain("test", "."); /* ドメイン名を"test"に設定 */ textdomain("test"); puts(_("Hello, World!")); puts(_(msg)); return 0; }
$ gcc -Wall -o test test.c $ ./test Hello, World! GNU gettext excersize
基本:
xgettext -kキーワード -o XXYY.pot 抽出対象ファイル群
キーワードに"_", "N_"の二種類を指定することで、両方のマクロが適用された翻訳対象文字列を抽出できる。
$ xgettext -k"_" -k"N_" -o test.pot test.c $ cp test.pot test.po
とりあえず"Content-Type"に文字コードを設定し、他、空になっているmsgstrに日本語翻訳文字列を埋め込む。
"Content-Type: text/plain; charset=CHARSET\n" → とりあえずUTF-8で埋め込むので、UTF-8にしておく。EUC-JPとかでもOK. "Content-Type: text/plain; charset=UTF-8\n" ... #: test.c:9 msgid "GNU gettext excersize" msgstr "GNU gettext 練習サンプル" #: test.c:16 msgid "Hello, World!" msgstr "こんにちは、世界!"
基本:
msgfmt -o XXYY.mo foo.po
今回は以下のようにコンパイルした。
$ msgfmt -o test.mo test.po
メッセージカタログディレクトリの起点は"."にしてあるので、ディレクトリを作成してmoファイルを配置する。
$ mkdir -p ja/LC_MESSAGES $ cp test.mo ja/LC_MESSAGES/test.mo
途中の*.poや*.moファイルは偶々ドメイン名と同じにして作業していたが、最終的にLC_MESSAGES/以下に配置される時にドメイン名.moになっていればよい。途中ファイルについては好きなファイル名にしても問題ない。
動作確認:
$ LANG=C ./test Hello, World! GNU gettext excersize $ LANG=ja_JP.UTF-8 ./test こんにちは、世界! GNU gettext 練習サンプル
次のようにtest.cを修正してみる。
#include <stdio.h> #include <locale.h> #include <libintl.h> #define _(String) gettext(String) #define N_(String) gettext_noop(String) #define gettext_noop(String) (String) int main() { setlocale(LC_ALL, ""); bindtextdomain("test", "."); textdomain("test"); puts(_("Hello, World!!")); puts(_("How are you?")); return 0; }
変更点:
$ gcc -Wall -o test test.c $ ./test Hello, World!! How are you?
基本:
msgmerge マージ先poファイル マージ元potファイル -o マージ結果出力poファイル
$ xgettext -k"_" -k"N_" -o test.pot test.c $ msgmerge test.po test.pot -o new_test.po .. 完了.
new_test.po:
... #: test.c:14 #, fuzzy msgid "Hello, World!!" msgstr "こんにちは、世界!" #: test.c:15 msgid "How are you?" msgstr "" #~ msgid "GNU gettext excersize" #~ msgstr "GNU gettext 練習サンプル"
#, fuzzy
→元のmsgidから少しだけ変更された場合に付く特殊なフラグコメント。"#, fuzzy"付のエントリはmsgfmtからは「翻訳済」とは見なされない。
#~ msgid ... #~ msgstr ...
→obsoleteなメッセージで、使われなくなったmsgid, msgstrを意味する。
とりあえず以下のように書き換えておく。
#: test.c:14 msgid "Hello, World!!" msgstr "こんにちは、世界!!" #: test.c:15 msgid "How are you?" msgstr "御機嫌如何?"
$ msgfmt new_test.po -o test.mo $ cp test.mo ja/LC_MESSAGES/ cp: `ja/LC_MESSAGES/test.mo' を上書きしてもよろしいですか(yes/no)? yes
$ gcc -Wall -o test test.c $ LANG=C ./test Hello, World!! How are you? $ LANG=ja_JP.UTF-8 ./test こんにちは、世界!! 御機嫌如何?
GNU gettext HTMLドキュメントの "13 The Maintainer's View" に詳細が載っている為、ここではポイントとなる手順だけを示す。
メンテナンス:
gettextの無効化:
以下、GNU Libtool HTMLドキュメントでの主な見出しをポイントしておく。(見出しの番号については2009/11時点のものなので、将来変更される可能性がある)
コメント