シンプル!flaskを多言語化する方法

さてさて、この前「意外と簡単!FlaskをHTTPS対応する方法」という記事を投稿したとおり、現在pythonの軽量フレームワーク・flaskでサイト開発を行っています。

すでに必要な機能の開発はほぼ完了しているので、最後に残った「多言語化」をしようとネット上を探してみたところ「Flask-Babel」を使えばいいとのことだったんで、pipでインストールしてコードを書いてみたところ、

flask.ext.babel is deprecated, use flask_babel instead.

というエラーが発生。内容は、

flask.ext.babelはもう使われてねぇんだよー。代わりにflask_babelを使いな!

と言っているので、仕方なく、flask_babelをインポートしてみると、今度は

そんなパッケージねぇよ!

と言われる始末。その後いろいろとチェックしてみたんですけど、時間だけが過ぎてしまって、ついに、

「いやいや、多言語化なんてjson作って該当データ呼び出すだけじゃん・・・もう自分で作るわ(ぷんぷん!)」

ってなったんで、今回は「シンプルにflaskの多言語化する方法」をまとめてみたいと思います。

方法としては、Laravelのようにlang/ja/messages.jsという形でテキストのマッピング・データを格納しておき、以下の形で呼び出すというシンプルなものです。

gettext('messages.app_name')

では、実際の作業を見ていきましょう。

言語の取得コード

といっても、難しいことをするわけじゃなく、リクエスト・ヘッダーの「Accept-Language」を分解して言語コードを取得するだけ。

※以下のコードでは、英語、日本語以外の場合はすべて英語になるようになっています。もっと言語を増やしたい場合は、各自追加してくださいね。

def get_locale():
    locale = 'en'
    languages = request.headers.get('Accept-Language').split(',')
    for language in languages:
        locale_long = language.split(';')[0]
        locale = locale_long.split('-')[0]
        break
    if locale not in ['ja', 'en']:
        locale = 'en'
    return locale.lower()

言語テキストを取得するコードを書く

def get_text(key):
    text = ''
    locale = get_locale()
    keys = key.split('.')
    path = APP_DIR +'/lang/'+ locale +'/'+ '/'.join(keys[:-1]) +'.json'
    try:
        with open(path) as f:
            data = json.load(f)
            text = data[keys[-1]]
    except:
        pass
    return unicode(text)

次にjsonから該当するテキストを取得するコードです。
Laravelのように「messages.app_name」という形でテキストを呼びだせるようにしています。(といっても、文字列フォーマットの代入は使えないですし、多重配列には対応してはいません。あくまでシンプルでいきます)

気をつけなければいけないところは、APP_DIRの部分です。前にも紹介したmod_wsgiで実行すると相対パスがおかしなことになってしまうんで、絶対パスで指定するようにしています。

あとは、returnの部分です。
ローカル環境だと、文字コードでエラーは出なかったのに、サーバーの方ではunicode()をかましてないと以下のようなエラーが発生してしまいました。


'ascii' codec can't encode ...

やはりpythonで日本語を使うときは少し気をつけないとダメですね(笑)

テンプレートからデータを呼び出せるようにする

flaskでは、context_processorを使うとテンプレートから関数を呼び出すことができます。

@app.context_processor
def utility_processor():
    return dict(get_text=get_text, get_locale=get_locale)

dictの中で、呼び出すキーと関数を指定しています。

つまり、上の例で言うと、

<html lang="{{ get_locale() }}">

という形でデータ取得ができますし、肝心の言語テキストも、

{{ get_text('messages.app_name') }}

と、シンプルな形で置き換えができるようになります。

ちなみに、今回はわかりやすく説明するためにget_text()という名前にしましたが、実際には次のようにアンダーバーだけの関数を作りました。

{{ _('messages.app_name') }}

その場合は、context_processorを以下のように変更してください。

@app.context_processor
def utility_processor():
    return dict(_=get_text, get_locale=get_locale)

言語マッピングjsonを作る

といっても、これも簡単で、今回の例では、

  • lang/ja/messages.js
  • lang/en/messages.js

というフォルダ&ファイルを作成して、翻訳テキストを書き込むだけでOKです。

{
    "app_name": "サイト名",
    "top_page": "トップページ"
}

なので、もしも、

{{ _('errors.requied') }}

という形でテキストを表示したい場合は、

  • lang/ja/errors.js
  • lang/en/errors.js

というファイルを作成して同じようにjson内にテキストをかき込んでください。

ということで、今回はシンプルでしたけどflaskの多言語化をやってみました。もしLaravelだったら今後のことを考えてパッケージ化しておくところなんですけど、flaskは今後そこまで利用頻度が高くなさそうなので、今回はここで終了することにしました。(Laravelにはもうすでに素晴らしい多言語化機能がありますしね^^)

ではでは〜。



にほんブログ村 IT技術ブログへ  にほんブログ村 IT技術ブログ プログラム・プログラマーへ


BugGUI バグ報告を効率化
たった3分でバグ報告完了!? BugGUI