ほんじゃらねっと

ダイエット中プログラマのブログ

Google App EngineでBabelとJinja2を使って国際化

Jinja2を導入すれば簡単に国際化できると思ったら大間違いだった。
Jinja2と国際化フレームワークのBabelを使うことで、Djangoの国際化機能と同じようなことが実現できる、
ということだったらしい。何度か「Kay Frameworkにしときゃ良かったか...」と思った。


苦労したけどどうにかこうにか国際化できたので、方法をメモっておく。
ちなみにappengineでJinja2を使う方法については別の記事に書いたのでそちらも良かったらお読みください。


Babelの本サイト(ダウンロードとマニュアル)
http://babel.edgewall.org/


Babelの使い方について参考にさせていただいた記事
http://d.hatena.ne.jp/nullpobug/20100923/1285251361


下記のような方法をとりました。

BabelとJinja2をインストール

Babel用のコマンドを使うのと、BabelでJinja2拡張を使えるようにするためにeasy_installで両方インストール。

easy_install Jinja2
easy_install Babel


それとは別にプロジェクト内でBabelモジュールを使用するので、
Babelのソースをダウンロードしてzip化してプロジェクト内に配置し、パスに追加しておく。

テンプレートを編集

国際化したい文字列を{% trans %}{% endtrans %}で囲んだり、{{ _("") }}で囲んだりします。
この辺はDjangoの国際化と似てる。翻訳文字列内でHTMLタグを使用したい時はtransタグにした方がいいみたい。

翻訳ファイルを作る

ここからBabelの出番。
Babelの設定ファイルを作成し、pybabelコマンドで各言語用の翻訳ファイルを生成します。


Babelの設定ファイルはひとまずプロジェクトの直下に配置。
Jinja2拡張を使えるようにして、pyファイルとhtmlファイルを翻訳対象にしてる。


babel.cfg

[extractors]
jinja2 = jinja2.ext.babel_extract
[python: **.py]
[jinja2: **/templates/**.html]
encoding=utf-8
extensions=jinja2.ext.with_


ここからコマンドを色々実行。プロジェクト直下で実行する。
まずextract処理を実行する。これは対象のファイルから翻訳対象の情報を抽出する処理らしい。

pybabel extract -F babel.cfg -o messages.pot .

これでmessages.potというファイルがプロジェクト直下に生成される。


次に言語用のpoファイルを作成する。今回はja。

pybabel init -i messages.pot -d project1/translations -l ja

これで、
プロジェクトディレクトリ/project1/translations/ja/LC_MESSAGES/message.po
が作成されるので、ファイルを開いて翻訳テキストを入力していく。このあたりはDjangoと同じ。


翻訳が終わったら、コンパイルしてmoファイルを作成する。

pybabel compile -d project1/translations

これで、
プロジェクトディレクトリ/project1/translations/ja/LC_MESSAGES/message.mo
が作成される。


テンプレートを変更したら、再extract後、initの代わりにupdateでpoファイルを更新する。
その後でcompileを実行する。

pybabel update -i messages.pot -d project1/translations

テンプレートのレンダリング処理に国際化処理を追加

ここがまだちゃんと書けてないところ。とりあえずjaのみ日本語で表示して、それ以外は英語で表示するようにしてる。

import re
import os
import jinja2
import werkzeug.urls
from babel.support import Translations
from babel.core import parse_locale
from django.template import defaultfilters as django_filters
import appengine_utilities.sessions
from project1 import settings
class NullUndefined(jinja2.Undefined):
def __int__(self):
return 0
def __getattr__(self, name):
return u""
def render_response(handler, template_name, params):
env = jinja2.Environment(
loader=jinja2.FileSystemLoader([settings.TEMPLATE_DIR], encoding="utf8"),
autoescape=True,
undefined=NullUndefined,
extensions=[
'jinja2.ext.with_',
'jinja2.ext.i18n',
]
)
env.filters.update({
"linebreaksbr": django_filters.linebreaksbr,
"urlencode": werkzeug.urls.url_quote_plus,
})
locale = "en_US"
if "Accept-Language" in handler.request.headers:
langs = handler.request.headers["Accept-Language"]
if langs:
lang_list = langs.split(",")
lang_list = [lang.replace(" ", "") for lang in lang_list]
for lang in lang_list:
if lang.startswith("ja"):
locale = "ja"
break
trans_file_path = os.path.join(settings.TRANSLATIONS_DIR, locale, 'LC_MESSAGES', 'messages.mo')
if os.path.exists(trans_file_path):
trans_file = open(trans_file_path, "rb")
trans = Translations(trans_file)
else:
trans = Translations()
env.install_gettext_translations(trans)
try:
template = env.get_template(template_name)
except jinja2.TemplateNotFound:
raise jinja2.TemplateNotFound(template_name)
handler.response.out.write(template.render(params))


スゴロコのトップページだけ国際化してみた。
http://sugolo.co