国際化とローカライズ

はじめに

国際化とローカライズは、ソフトウェアを非ネイティブの環境、特に他の国や 文化のために適合させる手段です。

ローカライズが必要なアプリケーションの部分は以下のようなものがあります:

  • 言語
  • 日付/時間の形式
  • 数の形式、例えば小数点やセパレータの位置、セパレータとして使用される文字
  • タイムゾーン (国際化環境では UTC)
  • 通貨
  • 重さと長さ

国際化とローカライズの区別は微妙ですが、重要です。国際化とは潜在的にあ らゆる場所で使用できるように製品を適合させることです。一方、ローカライ ズは特定のロケールで使用するための特別な機能追加です。

例えば、ソフトウェアで使用される言語に関して、国際化は翻訳が必要になる 可能性のあるすべての文字列に印をつけるプロセスであり、ローカライズは特 定のロケールのための翻訳を行うプロセスです。

Pylons は言語を国際化することを可能にする標準サポートを提供しますが、ア プリケーションに特有の国際化の他の側面はすべてあなたに任されています。

Note

国際化はしばしば I18N (または i18n, I18n) と省略されます。ここで 18 という数字は、省略された文字の数を表しています。同様に、ローカラ イズはしばしば L10n または l10n と省略されます。これらの略語はまた、 2つのスペル (internationalisation か internationalization か) から 1 つを選ぶことを回避します。

複数の言語の文字を表現するためにはユニコードを使用する必要があります。 この文書では、読者がすでに Understanding Unicode を読んでいることを前提として います。

あなたはすでに、ユニコードが何であるか、 Python でそれをどのように使用 するか、そしてアプリケーションのどの領域でユニコードデータのデコーディ ングとエンコーディングに特別な注意を向ける必要があるかについて、よく理 解していることでしょう。

この最終章では、アプリケーションを複数の言語で動かす際の問題について見 ていきます。

Pylons は国際化のために Python gettext モジュール を使用します。それは GNU gettext API に基づいてい ます。

Getting Started

コードのあらゆる場所で、異なる言語で利用できるようにしたい文字列を _() 関数で囲みます。その他にも多数の翻訳関数があり、それらは http://pylonshq.com/docs/module-pylons.i18n.translation.html の API リ ファレンスに記載されています。

Note

_() 関数は ugettext() 関数への参照です。 _() は、翻訳す べきテキストに印をつけるための規約であり、キーストロークを節約しま す。 ugettext()gettext() のユニコードバージョンです; そ れはユニコード文字列を返します。

ここでの例では、文字列 'Hello' を英語、フランス語、スペイン語という 異なる 3 つの言語で表示したいとします。 また、デフォルトの言語で 'Hello' という言葉を表示したいと思います。 さらに、いくつかの複数形 の単語も使用します。

プロジェクト名を translate_demo としましょう:

$ paster create -t pylons translate_demo

次に hello と挨拶するフレンドリーなコントローラを追加します。

$ cd translate_demo
$ paster controller hello

controllers/hello.py を編集して、文字列 Hello が現れるすべての 場所で _() 関数を使うようにします:

import logging

from pylons.i18n import get_lang, set_lang

from translate_demo.lib.base import *

log = logging.getLogger(__name__)

class HelloController(BaseController):

    def index(self):
        response.write('Default: %s<br />' % _('Hello'))
        for lang in ['fr','en','es']:
            set_lang(lang)
        response.write("%s: %s<br />" % (get_lang(), _('Hello')))

gettext 関数で囲まれた文字列を書くとき、文の断片を手動で連結しないこと が重要です; ある言語では文法を逆にする必要があるかもしれません。 これを しないでください:

# BAD!
msg = _("He told her ")
msg += _("not to go outside.")

かわりにこれなら何の問題もありません:

# GOOD
msg = _("He told her not to go outside")

これでコントローラは国際化されましたが、代替の言語カタログをセットアッ プするまで LanguageError 例外が raise されます。

GNU gettext は、翻訳フレームワークの中で 3 種類のファイルを使用します。

POT (Portable Object Template) ファイル

ローカライズプロセスの第一歩。プログラムを使用して、プロジェクトのソー スコード全体から _() のような翻訳関数に渡されたあらゆる文字列を検索 します。このリストは、すべての翻訳の元になる、特別にフォーマットされた テンプレートファイルの中にまとめられます。これが .pot ファイルです。

PO (Portable Object) ファイル

ローカライズプロセスにおける第 2 ステップ。 POT ファイルをテンプレート として使用して、メッセージのリストが翻訳されて .po ファイルとして保 存されます。

MO (Machine Object) ファイル

ローカライズプロセスの最終的なステップ。 PO ファイルをプログラムで処理 することによって、最適化された機械可読なバイナリーファイルに変換します。 それが .mo ファイルです。翻訳を機械コードにコンパイルするので、ロー カライズされたプログラムは実行中に翻訳を検索するのに比べてはるかに速く なります。

GNU gettext は、ソースコードからメッセージを抽出したり、関連する gettext カタログを処理したりするための、コマンドラインプログラム一式を 提供します。 Babel プロジェクトはこれら のツールの pure Python による代替実装を提供します。 GNU gettext ツール xgettext と比較すると、 Babel は Python テンプレート言語 (現在は Mako と Genshi) からの翻訳可能な文字列の抽出をサポートしています。

Babel を使う

_images/babel_logo.png

Babel を使用するためには、最初に easy_install でインストールしなければ なりません。次のコマンドを実行してください:

$ easy_install Babel

Pylons (0.9.6 以降) では、 setup.cfg ファイルに Babel distutils コマン ドのための適切なデフォルト値が含まれています。

setup.py ファイルには抽出方法マッピングも含まれています。それはデフォル トではコメントアウトされていて、 Babel がインストールされていないときに distutils が認識できないオプションに対して警告を出さないようになってい ます。この文書の続きを行うために、これらの行のコメントを外して有効にし なければなりません:

message_extractors = {'translate_demo': [
        ('**.py', 'python', None),
        ('templates/**.mako', 'mako', None),
        ('public/**', 'ignore', None)]},

Babel を使って、プロジェクトの i18n ディレクトリの中の .pot ファ イルにメッセージを抽出します。最初にディレクトリは作成しておく必要があ ります。リビジョンコントロールシステムを使用しているなら、それにもディ レクトリを追加するのを忘れないでください:

$ cd translate_demo
$ mkdir translate_demo/i18n
$ svn add translate_demo/i18n

次に、以下のコマンドですべてのメッセージをプロジェクトから抽出すること ができます:

$ python setup.py extract_messages
running extract_messages
extracting messages from translate_demo/__init__.py
extracting messages from translate_demo/websetup.py
...
extracting messages from translate_demo/tests/functional/test_hello.py
writing PO template file to translate_demo/i18n/translate_demo.pot

これは i18n ディレクトリに次のような .pot ファイルを作成します:

# Translations template for translate_demo.
# Copyright (C) 2007 ORGANIZATION
# This file is distributed under the same license as the translate_demo project.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2007.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: translate_demo 0.0.0\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2007-08-02 18:01-0700\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"
"Generated-By: Babel 0.9dev-r215\n"

#: translate_demo/controllers/hello.py:10 translate_demo/controllers/hello.py:13
msgid "Hello"
msgstr ""

ここで生成される .pot の詳細は、プロジェクトの setup.cfgextract_messages 設定を通してカスタマイズすることができます (すべて の設定オプションについては、 Babel コマンドラインインタフェースのドキュ メンテーション を見てください)。

次に、スペイン語のためのカタログ (.po ファイル) を初期化します:

$ python setup.py init_catalog -l es
running init_catalog
creating catalog 'translate_demo/i18n/es/LC_MESSAGES/translate_demo.po' based on
'translate_demo/i18n/translate_demo.pot'

そうすると、 "Hello" の翻訳を加えるために、新しいスペイン語の .po ファイルの最後の行を編集することができます:

msgid "Hello"
msgstr "¡Hola!"

最後に、アプリケーションでこれらの翻訳を利用するために .po ファイル を .mo ファイルにコンパイルする必要があります:

$ python setup.py compile_catalog
running compile_catalog
1 of 1 messages (100%) translated in 'translate_demo/i18n/es/LC_MESSAGES/translate_demo.po'
compiling catalog 'translate_demo/i18n/es/LC_MESSAGES/translate_demo.po' to
'translate_demo/i18n/es/LC_MESSAGES/translate_demo.mo'

.pot ファイルから .po ファイルに新しいメッセージをマージするた めに update_catalog コマンドを使うこともできます。たとえば、後で HelloController の index メソッドの最後に以下のコードを加えたなら:

response.write('Goodbye: %s' % _('Goodbye'))

プロジェクトからメッセージを再抽出する必要があり、そのため update_catalog コマンドを実行します:

$ python setup.py extract_messages
running extract_messages
extracting messages from translate_demo/__init__.py
extracting messages from translate_demo/websetup.py
...
extracting messages from translate_demo/tests/functional/test_hello.py
writing PO template file to translate_demo/i18n/translate_demo.pot
$ python setup.py update_catalog
running update_catalog
updating catalog 'translate_demo/i18n/es/LC_MESSAGES/translate_demo.po' based on
'translate_demo/i18n/translate_demo.pot'

それから、カタログを編集して “Goodbye” の翻訳を追加して、先ほどしたよう に .po ファイルを再コンパイルします。

詳細については Babel ドキュメンテーションGNU Gettext マニュアル を見てくだ さい。

作業に戻る

次に、 en ロケールと fr ロケールのために .mo ファイルを作成 するプロセスを繰り返す必要があります:

$ python setup.py init_catalog -l en
running init_catalog
creating catalog 'translate_demo/i18n/en/LC_MESSAGES/translate_demo.po' based on
'translate_demo/i18n/translate_demo.pot'
$ python setup.py init_catalog -l fr
running init_catalog
creating catalog 'translate_demo/i18n/fr/LC_MESSAGES/translate_demo.po' based on
'translate_demo/i18n/translate_demo.pot'

fr カタログの最後の行をこのように修正してください:

#: translate_demo/controllers/hello.py:10 translate_demo/controllers/hello.py:13
msgid "Hello"
msgstr "Bonjour"

オリジナルのメッセージはもともと英語なので、 en カタログは空白のま まにすることができます; gettext はオリジナルへ fallback します。

これらの新しい .po ファイルを編集して、それらを .mo ファイルに コンパイルしたら、 i18n ディレクトリには以下のファイルが含まれてい るでしょう:

i18n/translate_demo.pot
i18n/en/LC_MESSAGES/translate_demo.po
i18n/en/LC_MESSAGES/translate_demo.mo
i18n/es/LC_MESSAGES/translate_demo.po
i18n/es/LC_MESSAGES/translate_demo.mo
i18n/fr/LC_MESSAGES/translate_demo.po
i18n/fr/LC_MESSAGES/translate_demo.mo

アプリケーションをテストする

次のコマンドでサーバを動かしてください:

$ paster serve --reload development.ini

http://localhost:5000/hello を訪問してコントローラをテストしてください。 以下の出力が見られるはずです:

Default: Hello
fr: Bonjour
en: Hello
es: ¡Hola!

コントローラで使用される言語を on the fly でセットすることができます。

たとえば、ユーザーがアプリケーションに対してどの言語で動作するかを設定 できるようにするためにこれを用いることができます。セッションオブジェク トに値を保存することができます:

session['lang'] = 'en'
session.save()

そして、コントローラが呼び出される毎に使用する言語をセッションから読み こみ、コントローラの __before__() メソッドでそれを設定することによっ て、他のページでも以前設定されたのと同じ言語を維持するようにできます。

def __before__(self):
    if 'lang' in session:
        set_lang(session['lang'])

また、 Pylons は使用するデフォルト言語を設定ファイルで定義することもサ ポートしています。 development.ini ファイルで lang 変数を望まし いデフォルト言語に設定してください。すると、 Pylons はあらゆるリクエス トの始めにその言語で set_lang を自動的に呼び出します。

例えば、デフォルト言語をスペイン語に設定するためには、 development.inilang = es を加えます:

[app:main]
use = egg:translate_demo
lang = es

サーバーを --reload オプションで動かしているなら、 development.ini ファイルを変更するとサーバーは自動的に再起動します。 そうでなければ、手動でサーバーを再起動してください。そして、今度は出力 は以下のようになります:

Default: ¡Hola!
fr: Bonjour
en: Hello
es: ¡Hola!

fallback 言語

言語カタログの中に存在しない文字列で _() が呼ばれた場合、代わりに _() に渡された文字列が返されます。

hello コントローラの最後の行を、このように修正してください:

response.write("%s %s, %s" % (_('Hello'), _('World'), _('Hi!')))

Warning

もちろん、現実の世界においてこのように文を分割することは非常に危険 です。いくつかの文法では異なる語順を要求するかもしれないからです。

例を再び実行すると、出力はこのようになります:

Default: ¡Hola!
fr: Bonjour World!
en: Hello World!
es: ¡Hola! World!

これは文字列 'World!' に対して翻訳を用意しなかったからで、そのため 文字列自体が使われています。

Pylons も fallback 言語のためのメカニズムを提供しています。これは、ある 単語がメインの言語カタログで省略されていた場合に使われる他の言語を指定 することができるようにします。

この例では、メインの言語として fr を、fallback として es を選択 しています。

import logging

from pylons.i18n import set_lang

from translate_demo.lib.base import *

log = logging.getLogger(__name__)

class HelloController(BaseController):

    def index(self):
        set_lang(['fr', 'es'])
        return "%s %s, %s" % (_('Hello'), _('World'), _('Hi!'))

もし Hellofr .mo ファイルの中に Bonjour として存在 し、 Worldes の中にだけ Mundo として存在し、そして、カ タログのどれも Hi! を含まないなら、多国語メッセージを得ます: Bonjour Mundo, Hi! 。これは、フランス語とスペイン語と原語 (このケー スでは英語がソースコードの中で定義されています) の組合せです。

set_lang を呼んだ後で pylons.i18n.add_fallback 関数によって fallback 言語を追加することもできます。翻訳は追加された順番でテストされ ます。

Note

fallback は set_lang(lang) を呼んだ後でリセットされます – すな わち、 fallback は現在選択されている言語に関連付けられます。

このように fallback 使うことが特に役に立つ 1 つのケースは、 HTTP_ACCEPT_LANGUAGE ヘッダによってブラウザからリクエストされた言語 に基づいてコンテンツを表示したい場合です。一般に、ブラウザは多くの言語 を送信するので、ブラウザによって指定された順番で fallback を追加するこ とは有用です。それによって、常に望ましい言語で単語を表示しようとし、も し翻訳が見つけられない場合には順番に他の言語を探すことができます。 HTTP_ACCEPT_LANGUAGE ヘッダで宣言された言語は、 Pylons では request.languages オブジェクトとして利用でき、このように使うことが できます:

for lang in request.languages:
    add_fallback(lang)

テンプレートの翻訳

テンプレートの中でも、コードの中で行うのとまったく同じ方法で _() 関 数を使用することができます。たとえば Mako テンプレートで:

${_('Hello')}

これは設定された言語で文字列 'Hello' を表示します。

Babel は現在、 Mako と Genshi テンプレートからの gettext メッセージの抽 出をサポートしています。 Mako の抽出では、翻訳者コメントのサポートも提 供しています。 Babel は、 カスタム抽出方法プラグイン によって他のソースからメッセージを抽出するように拡張することができます。

Pylons (0.9.6 以降) は、 Python ソースコードと Mako テンプレートのため の Babel 抽出マッピングを自動的に設定します。これはプロジェクトの setup.py ファイルで定義されます:

message_extractors = {'translate_demo': [
        ('**.py', 'python', None),
        ('templates/**.mako', 'mako', None),
        ('public/**', 'ignore', None)]},

Mako の代わりに Genshi を使用しているプロジェクトでは、 Mako の行は次の ように置き換えられるでしょう:

('templates/**.html, 'genshi', None),

詳細は、 メッセージ抽出に関する Babel の ドキュメンテーション を参照してください。

遅延翻訳

時々、 _() または他の関数が呼ばれるときでなく、文字列にアクセスされ たときに翻訳を行う必要があるような状況に出くわすかもしれません。

この例を見てください:

import logging

from pylons.i18n import get_lang, set_lang

from translate_demo.lib.base import *

log = logging.getLogger(__name__)

text = _('Hello')

class HelloController(BaseController):

    def index(self):
        response.write('Default: %s<br />' % _('Hello'))
        for lang in ['fr','en','es']:
            set_lang(lang)
        response.write("%s: %s<br />" % (get_lang(), _('Hello')))
        response.write('Text: %s<br />' % text)

これを実行すると、以下の出力を得ます:

Default: Hello
['fr']: Bonjour
['en']: Good morning
['es']: Hola
Text: Hello

これは、 import の直後の関数 _('Hello') が、デフォルト言語が en のときに呼ばれたためで、文字列が使われたときのデフォルト言語がス ペイン語であったとしても、変数 text の値は英語の翻訳になります。

これらの状況の経験則は、リクエスト毎に実行されない状況では、翻訳関数を 使わないように努めるということです。これが可能でない状況 (おそらくレガ シーコードや国際化をサポートしないライブラリを扱っているような場合) で は、遅延翻訳を使う必要があります。

上の例で import 文と text への代入をこんな風に修正すれば:

from pylons.i18n import get_lang, lazy_gettext, set_lang

from helloworld.lib.base import *

log = logging.getLogger(__name__)

text = lazy_gettext('Hello')

期待する出力が得られます:

Default: Hello
['fr']: Bonjour
['en']: Good morning
['es']: Hola
Text: Hola

すべての標準的な Pylons 翻訳関数 に遅延 バージョンが存在します。

遅延翻訳を使う場合に気をつけなければならない欠点が 1 つあります: それは 実際には文字列でありません。つまり、先ほどの例で以下のコードを使用して いたら、エラー cannot concatenate 'str' and 'LazyString' objects で 失敗したでしょう。

response.write('Text: ' + text + '<br />')

この理由から、絶対に必要な場所でだけ遅延翻訳を使用すべきであり、本物の 文字列との操作に使用される前に str() または repr() を呼ぶことに よって、それらが確実に文字列に変換されるようにすべきです。

Python Egg の生成

最後に、翻訳ファイルを含むプロジェクトの egg をこのように生成することが できます:

$ python setup.py bdist_egg

setup.py は、アプリケーションを egg として配布することができるよう に、アプリケーションが必要とする .mo 言語カタログを自動的に含めます。 これは、 setup.py ファイルの中の以下の行で行われます:

package_data={'translate_demo': ['i18n/*/LC_MESSAGES/*.mo']},

複数形

Pylons は、 ungettext() 関数も提供しています。それは複数形の単語を 国際化するために設計されており、以下のように使用することができます:

ungettext('There is %(num)d file here', 'There are %(num)d files here',
          n) % {'num': n}

GNU Gettext マニュアルPO ファ イルフォーマット で説明されるように、複数形は .pot/.po ファイルにおいて異なった種類 のエントリーを持っています:

#: translate_demo/controllers/hello.py:12
#, python-format
msgid "There is %(num)d file here"
msgid_plural "There are %(num)d files here"
msgstr[0] ""
msgstr[1] ""

気をつけるべきことの 1 つは、他の言語の複数形は英語と同じではないという ことです。英語には 2 種類の複数形、単数と複数しかありませんが、スロベニ ア語には 4 つの複数形があります! これは、複数形を適切に扱うためには ugettext を使用し なければならない ということを意味します。特に、以下 は正しく動かないでしょう:

# BAD!
if n == 1:
    msg = _("There was no dog.")
else:
    msg = _("There were no dogs.")

まとめ

このドキュメントは、ウェブアプリケーションの国際化とローカライズのため のほんの基礎的な部分だけをカバーしています。

GNU Gettext は大規模なライブラリであり、詳しい情報を得るために GNU Gettext マニュアルは非常に推奨されます。

Babel は CLDR (Common Locale Data Repository) へのインターフェースのサ ポートも提供します。 CLDR は、様々なロケール表示名やローカライズされた 数、日付の形式などへのアクセスを提供しています。

Pylons の GNU gettext サポートを使用することで、アプリケーションを国際 化して、さらにローカライズすることができるでしょう。

さらに詳しく知るために

http://en.wikipedia.org/wiki/Internationalization

あらゆる誤りは遠慮なく Pylons メーリングリスト、または、著者に報告して ください。どんな修正または明確化も感謝して受けつけます。

Note

この文書はまだ未完成です。 Pylons における国際化、ローカライズ、お よびユニコードサポートが現在、頑健かつ柔軟であることを願っています が、何か問題があれば喜んで聞きたいと思います。 Google Groupsの pylons-discuss メーリングリストにメールを出して下さい。

babel.core – Babel core classes

Module Contents

babel.localedata — Babel locale data

babel.dates – Babel date classes

Module Contents

babel.numbers – Babel number classes

Module Contents

Read the Docs v: v1.0.1rc1
Versions
latest
v1.0.1rc1
v0.9.7
Downloads
PDF
HTML
Epub
On Read the Docs
Project Home
Builds

Free document hosting provided by Read the Docs.