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

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

Pythonでgettextを使用してNLS(Native Language Support)によるメッセージの国際化を行う(作業の流れと動作確認・前半)

gettextによるメッセージ国際化(NLS: Native Language Support)の概要」ではかなり大まかな内容しか扱っていないため、実際にコードを作成してメッセージを国際化することにする。
ここで扱う言語はPythonで、他の言語では扱いが若干異なる部分もある。
今回は、準備とgettextツール群を使用した作業の流れを扱う(前半)。

テスト用ディレクトリ構成の準備

まずは、作業ディレクトリ以下にこのようにディレクトリを作成する。*1

[work]-+-[bin]-(空)
       +-[po]-(空)
       +-[share]-[locale]-[ja]-[LC_MESSAGES]-(空)

プログラム

binディレクトリにコードを配置する。
ファイル名: bin/gettexttest.py

#! /usr/bin/python
# -*- encoding: utf-8 -*-

import gettext
import os

# ドメイン(パッケージ)名とmoファイルの場所を指定して、gettextを使用できるようにする
# gettext.install()だとngettext()がうまく動作しない?
# (探索パスが「/usr/share/locale/...」になる)
#gettext.install("gettexttest", os.path.join(os.path.dirname(os.path.dirname(os.path.realpath(__file__))), "share", "locale"), unicode=1)

# gettextを扱うための一連の処理(gettext.install()を使用しない場合)
from gettext import gettext as _
gettext.bindtextdomain("gettexttest", os.path.join(os.path.dirname(os.path.dirname(os.path.realpath(__file__))), "share", "locale"))
gettext.textdomain("gettexttest")

# 通常のメッセージ
# TRANSLATORS: comment string
print _("message")

# フォーマット
str = "test"
print _("string: %s") % str

# 数の表現
# ngettext()は単数用パターンと複数用パターンを扱える
items = 1  # 単数扱い
print gettext.ngettext("%d item", "%d items", items) % items
items = 2  # 複数扱い
print gettext.ngettext("%d item", "%d items", items) % items
items = 0  # 複数扱い
# dngettext()はドメイン指定ができる
print gettext.dngettext("gettexttest", "%d item", "%d items", items) % items

# 順番が逆転する例
# フォーマット文字列に複数の変数を使用するときには辞書を使用する
# 「%1$[フォーマット]」「%2$[フォーマット]」などは使用しない
aaa = "A"
bbb = "B"
#print _("%s by %s") % (aaa, bbb)      # これはダメ
#print _("%1$s by %2$s") % (aaa, bbb)  # これもダメ
# 順番入れ替えはPython独自のルールを使用?
print _("%(aaa)s by %(bbb)s") % {"aaa" : aaa, "bbb" :  bbb}

(2009/3/6)ディレクトリの指定でos.pathの関数を使用するように修正し、更にgettext.install()を使用しない場合のimport文の書き方も改善

xgettext: コードから翻訳可能文字列を抽出

コードを作成後、xgettextというコマンドにより、コードから翻訳可能文字列を取り出してメッセージカタログファイルの骨組みとなる.potファイルを生成する。
対象のファイルは拡張子によって種類が識別され、.gladeファイルのような形式にも対応している。-Lオプションを付けると、どのプログラミング言語として読み込むかを明示的に指定することもできる。
下の例では_()*2ngettext()*3から取り出すように指定している。また、「TRANSLATORS:」と書かれたコメント行を.potファイルに書き出すようにもしている。

[work]$ cd po/
[work/po]$ xgettext --add-comments=TRANSLATORS: --copyright-holder=myname --package-name=gettexttest --package-version=0.1 -k_ -kngettext -o gettexttest.pot ../bin/gettexttest.py

これを実行するとgettexttest.potというファイルができ、内容は下のようになる。
ファイル名: po/gettexttest.pot

# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR myname
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: gettexttest 0.1\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2008-07-08 00:00+0900\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=CHARSET\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"

#. TRANSLATORS: comment string
#: ../bin/gettexttest.py:19
msgid "message"
msgstr ""

#: ../bin/gettexttest.py:23
#, python-format
msgid "string: %s"
msgstr ""

#: ../bin/gettexttest.py:28 ../bin/gettexttest.py:30 ../bin/gettexttest.py:33
#, python-format
msgid "%d item"
msgid_plural "%d items"
msgstr[0] ""
msgstr[1] ""

#: ../bin/gettexttest.py:43
#, python-format
msgid "%(aaa)s by %(bbb)s"
msgstr ""

msginit: .potファイルから自然言語ごとの.poファイルを作成

次にmsginitで日本語メッセージカタログファイルja.poを生成する。他の自然言語の場合も同様に生成することになるが、これ以降、日本語のメッセージカタログの作成を行うものとする。

[work/po]$ msginit -l ja -i gettexttest.pot -o ja.po

この後、翻訳者はこのファイルを編集して、「msgid」に書かれた英語のメッセージに対応した翻訳済み文字列を「msgstr」に書いていく。
ここでは、下のようなメッセージカタログファイルを作成した。
ファイル: po/ja.po

# Japanese translations for PACKAGE package.
# Copyright (C) 2008 myname
# This file is distributed under the same license as the PACKAGE package.
#  <myname@domain.tld>, 2008.
#
msgid ""
msgstr ""
"Project-Id-Version: gettexttest 0.1\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2008-07-08 00:00+0900\n"
"PO-Revision-Date: 2008-07-08 00:00+0900\n"
"Last-Translator:  <myname@domain.tld>\n"
"Language-Team: Japanese\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=1; plural=0;\n"

#. TRANSLATORS: comment string
#: ../bin/gettexttest.py:19
msgid "message"
msgstr "メッセージ"

#: ../bin/gettexttest.py:23
#, python-format
msgid "string: %s"
msgstr "文字列: %s"

#: ../bin/gettexttest.py:28 ../bin/gettexttest.py:30 ../bin/gettexttest.py:33
#, python-format
msgid "%d item"
msgid_plural "%d items"
msgstr[0] "%d 個の項目"

#: ../bin/gettexttest.py:43
#, python-format
msgid "%(aaa)s by %(bbb)s"
msgstr "%(bbb)s による %(aaa)s"

既存の国際化されたソフトウェアで日本語に対応していないものに対しての日本語翻訳(対応)を行う場合は、まずこのmsginitを実行してからja.poの編集を行うことになる。バージョンが上がることによりメッセージが新しく追加された際には、翻訳者は(後半で扱う)msgmergeを使用後、未翻訳文字列を翻訳していく。

(「Pythonでgettextを使用してNLS(Native Language Support)によるメッセージの国際化を行う(作業の流れと動作確認・後半)」に続く)

関連記事:

参考URL:

使用したバージョン:

  • Python 2.4.4(2.4.4-r13)
  • gettext 0.17

*1:今回はパッケージビルドのディレクトリレイアウトとパッケージインストール後のレイアウトを混ぜた形にしている

*2:gettext()関数の簡略化した書き方

*3:gettext()関数に単数形と複数形を扱う機能を付けたもの