試験運用中なLinux備忘録・旧記事

はてなダイアリーで公開していた2007年5月-2015年3月の記事を保存しています。

Pythonでgettextを使用してNLS(Native Language Support)によるメッセージの国際化を行う(Pythonのコードについて)

Pythonでgettextを使用してNLS(Native Language Support)によるメッセージの国際化を行う(作業の流れと動作確認・前半)」で扱ったPythonのテストコードの内容に関する覚え書き。

準備

以下、Pythonでgettextによりメッセージを国際化するための一連の処理について。

色々な指定

(2009/3/6)ディレクトリ指定の部分でos.pathの関数を使用するように変更したのに合わせて記述を修正
ここでいう「ドメイン」は、1つのメッセージカタログがカバーする範囲のことで、bindtextdomain()textdomain()dgettext()などの関数からメッセージカタログのファイル名(拡張子の「.mo」を除いた部分)で扱われる。

gettext.install()とgettext.ngettext()

(2011/2/22)gettext.install()を用いてngettext()を動かすための追加メモを「Pythonでgettextによるメッセージ国際化を用いる上でのgettext.install()に関する追加メモ(前半)」「Pythonでgettextによるメッセージ国際化を用いる上でのgettext.install()に関する追加メモ(後半)」で扱っている。以下は古い内容となる。

  • gettext.install()を使用すると、それ以外の初期化作業が必要なくなるように見えるが、実行すると
メッセージ
文字列: test
1 item
2 items
0 items
B による A
新しいメッセージ

となってしまい、gettext.ngettext()を使用しているところだけ日本語にならなかった。
そこで、どのように動作しているのかを細かく見るため、straceを使用してみた。

[work]$ strace -o stace.log bin/gettexttest.py

このログファイルstrace.logから一部を抜き出すと

write(1, "\346\226\207\345\255\227\345\210\227: test\n", 16) = 16
stat("/usr/share/locale/ja_JP.utf/LC_MESSAGES/messages.mo", 0x7fff23c49c50) = -1 ENOENT (No such file or directory)
stat("/usr/share/locale/ja_JP/LC_MESSAGES/messages.mo", 0x7fff23c49c50) = -1 ENOENT (No such file or directory)
stat("/usr/share/locale/ja.utf/LC_MESSAGES/messages.mo", 0x7fff23c49c50) = -1 ENOENT (No such file or directory)
stat("/usr/share/locale/ja/LC_MESSAGES/messages.mo", 0x7fff23c49c50) = -1 ENOENT (No such file or directory)
write(1, "1 item\n", 7)                 = 7
stat("/usr/share/locale/ja_JP.utf/LC_MESSAGES/messages.mo", 0x7fff23c49c50) = -1 ENOENT (No such file or directory)
stat("/usr/share/locale/ja_JP/LC_MESSAGES/messages.mo", 0x7fff23c49c50) = -1 ENOENT (No such file or directory)
stat("/usr/share/locale/ja.utf/LC_MESSAGES/messages.mo", 0x7fff23c49c50) = -1 ENOENT (No such file or directory)
stat("/usr/share/locale/ja/LC_MESSAGES/messages.mo", 0x7fff23c49c50) = -1 ENOENT (No such file or directory)
write(1, "2 items\n", 8)                = 8
stat("/usr/share/locale/ja_JP.utf/LC_MESSAGES/gettexttest.mo", 0x7fff23c49e80) = -1 ENOENT (No such file or directory)
stat("/usr/share/locale/ja_JP/LC_MESSAGES/gettexttest.mo", 0x7fff23c49e80) = -1 ENOENT (No such file or directory)
stat("/usr/share/locale/ja.utf/LC_MESSAGES/gettexttest.mo", 0x7fff23c49e80) = -1 ENOENT (No such file or directory)
stat("/usr/share/locale/ja/LC_MESSAGES/gettexttest.mo", 0x7fff23c49e80) = -1 ENOENT (No such file or directory)
write(1, "0 items\n", 8)                = 8
write(1, "B \343\201\253\343\202\210\343\202\213 A\n", 14) = 14

となっている。これが示すのは

  • gettext.ngettext()を使用しているところでは/usr/share/locale以下からmessages.mo*1を読み込もうとしている(が失敗)
  • gettext.dngettext()を使用しているところでは(「gettexttest」というドメインを指定しているため)/usr/share/locale*2以下からgettexttest.moを読み込もうとしている(が失敗)
  • gettext.ngettext()gettext.dngettext()を使用しているところでは、出力されるメッセージは全て英語のまま・それ以外のところは日本語が表示されている

ということで、(gettext.install()の代わりに)gettext.bindtextdomain()gettext.textdomain()を呼んだ場合とは挙動が異なる(うまく動作しない)。

フォーマットの順番を入れ替える際の注意点

print _("%s by %s") % (aaa, bbb)

という書き方をしたところ、xgettext

../bin/gettexttest.py:[行番号]: 警告: 名無し引数のある 'msgid' フォーマット文字列を適切に特定することができません:
                                翻訳者は引数を整理し直すことができません.
                                名前付き引数のあるフォーマット文字列を使うこと, 且つ引数に対して組 (tuple)
                                の代わりにマッピングを使うことを検討してください

という警告を出した。
Pythonprintf()系関数のようなフォーマット付けを行うときには、上のような形式以外に、フォーマット文字列に

%(名前)[フォーマット文字列(「s」や「d」など)]

の形で名前を付けて

{[名前1] : [名前1に対応する変数], [名前2] : [名前2に対応する変数], ...}

という形の辞書に名前と変数の対応を書くという方法があるのだが、複数の変数をフォーマット付けするところのメッセージを国際化したいときには、こちらの方式を使用することになる。

print _("%(aaa)s by %(bbb)s") % {"aaa" : aaa, "bbb" :  bbb}

関連記事:

参考URL:

使用したバージョン:

  • Python 2.4.4(2.4.4-r13)
  • gettext 0.17

*1:msgfmtコマンドで-oオプションで出力ファイル名を指定しないときにこの名前になるのだが、これと同じように、どこかでドメインの指定がうまくいっていない状態ではないかと思われる

*2:今回の例では、この場所は意図しない場所