LoginSignup
15
16

More than 5 years have passed since last update.

python translationstringでlocalizationに対応したメッセージを作る

Last updated at Posted at 2014-06-12

python translationstringでlocalizationに対応したメッセージを作る

localeによってエラーメッセージなど出し分けたい場合がある。
そのような時にどうすれば良いのか分かっていなかったので調べた。

translationstring

translationstring を使う。
これはpylons project関連のpackageでlocalizationのために使われている模様。
一般的な翻訳の際によく使われているgettextの仕組みを利用しているらしい。

python側コード

日本語localeでは「失敗」。それ以外では「failure」と表示する文字列を考える。

表記する場所にはtranslationstring.TranslationStringFactoryで生成した関数を使えば良いらしい。
値を変換するにはTranslator Objectで包む。

以下のコードの他に所定の位置に.moファイルが必要。

translationstring_example.py

# -*- coding:utf-8 -*-
import gettext
import translationstring

# .poファイルの生成ツールと上手く連携させるために_という名前にするのが良い
_ = translationstring.TranslationStringFactory('sample')

lang = gettext.translation("sample", "./locale", languages=["ja"], codeset="utf8")
translator = translationstring.Translator(lang)

print(translator(_("failure")))

.moファイルの生成

.moファイルはgettextで使われるフォーマット。.poファイルからmsgfmtを使ってコマンドを使って生成する。
.moファイルの前に.poファイルを生成する。これにはpot-createを利用する。

linguaのinstall

linguaというパッケージが.poファイルを生成してくれる。
ligua自身はbabelを必要としないがpot-createを実行しようとした際にbabelが要求された。

pip install lingua babel

.poファイルの生成

linguaをインストールするとpot-createが使えるようになる。所定の位置に.poファイルを生成する

mkdir -p locale/ja/LC_MESSAGES/
pot-create translation_example.py -o locale/ja/LC_MESSAGES/sample.po

生成したsample.poを書き換えるファイル名はTranslationStringFactoryに渡したドメイン名と同じにするのが良い。

以下の様に書き換える。本来は翻訳者の名前などの部分も書き換えた方が良い。今回は末尾の部分を書き換えただけ

# 
# SOME DESCRIPTIVE TITLE
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2014.
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE 1.0\n"
"POT-Creation-Date: 2014-06-12 15:37+09:00\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=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: \n"

#: ./translation_example.py:11
#, c-format
msgid "failure"
msgstr "失敗値"

差分は以下のとおり

diff --git a/locale/ja/LC_MESSAGES/sample.po b/locale/ja/LC_MESSAGES/sample.po
index 2559ffb..db040c4 100644
--- a/locale/ja/LC_MESSAGES/sample.po
+++ b/locale/ja/LC_MESSAGES/sample.po
@@ -18,4 +18,4 @@ msgstr ""
 #: ./translation_example.py:11
 #, c-format
 msgid "failure"
-msgstr ""
+msgstr "失敗値"

.moファイルの作成

.moファイルの作成にはmsgfmtを使う。

msgfmt locale/ja/LC_MESSAGES/sample.po -o locale/ja/LC_MESSAGES/sample.mo

$ tree
.
├── locale
│   └── ja
│       └── LC_MESSAGES
│           ├── sample.mo
│           └── sample.po
├── requirements.txt
└── translation_example.py

3 directories, 4 files

これで"failure"が"失敗値"に翻訳されて表示される。

$ python translation_example.py
失敗値

ja/LC_MESSAGES/sample.moの場所 について

gettextはlocales情報を見て、翻訳する際に利用するマッピングを求めている。
bash
LANG="ja_JP.UTF-8"
LC_COLLATE="ja_JP.UTF-8"
LC_CTYPE="ja_JP.UTF-8"
LC_MESSAGES="ja_JP.UTF-8"
LC_MONETARY="ja_JP.UTF-8"
LC_NUMERIC="ja_JP.UTF-8"
LC_TIME="ja_JP.UTF-8"
LC_ALL=

上手く.moファイルを読み込むことができているか調べるにはgettext.findを利用すると良さそう。

# -*- coding:utf-8 -*-
import gettext
print(gettext.find("sample", "./locale",["ja"]))
# ./locale/ja/LC_MESSAGES/sample.mo

.poファイルをどこに作るかというのはgettext.findのコードを見てみれば良い。

gettext.py

# Locate a .mo file using the gettext strategy
def find(domain, localedir=None, languages=None, all=False):
    # Get some reasonable defaults for arguments that were not supplied
    if localedir is None:
        localedir = _default_localedir
    if languages is None:
        languages = []
        for envar in ('LANGUAGE', 'LC_ALL', 'LC_MESSAGES', 'LANG'):
            val = os.environ.get(envar)
            if val:
                languages = val.split(':')
                break
        if 'C' not in languages:
            languages.append('C')
    # now normalize and expand the languages
    nelangs = []
    for lang in languages:
        for nelang in _expand_lang(lang):
            if nelang not in nelangs:
                nelangs.append(nelang)
    # select a language
    if all:
        result = []
    else:
        result = None
    for lang in nelangs:
        if lang == 'C':
            break
        mofile = os.path.join(localedir, lang, 'LC_MESSAGES', '%s.mo' % domain)
        if os.path.exists(mofile):
            if all:
                result.append(mofile)
            else:
                return mofile
    return result

大雑把に('LANGUAGE', 'LC_ALL', 'LC_MESSAGES', 'LANG')の中から"ja"などのlanguagesを取得し、
localedir/ja/LC_MESSAGS/以下の.moファイルから値を取り出そうしていることが分かる。

引数を取る文字列の翻訳について

translationstringは引数を取る文章の翻訳も可能。

msg2 = _("add-number", default="Add ${number}", mapping={"number": 1})

lang = gettext.translation("sample", "./locale", languages=["ja"], codeset="utf8")
translator = translationstring.Translator()

print(translator(_("add-number", mapping={"number": 10})))
# 10を足してください

この時.poファイルには以下の様に.プレースホルダーを${}で囲んで書く。

msgid "add-number"
msgstr "${number}を足してください"
15
16
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
15
16