DjangoでJavaScriptの国際化を行う方法

前回のエントリ「GAEでポケモンのデータベースサイトを作成しました - saito’s blog」で紹介したサイトでDjangoの国際化機能でJavaScriptの翻訳を行う際に,いくつかハマったことがあったので,ここにまとめておこうと思います.

Djangoのリファレンスによると、以下の手順によって、JavaScriptを翻訳することができます。

1. JavaScriptのソースファイル内の翻訳したい文字列をgettext()やngettext()で囲んでおく。
2. 次のコマンドでJavaScriptの翻訳カタログdjangojs.poを作成する。

django-admin.py makemessages -d djangojs -l ja

3. 作成したdjangojs.poを編集する。
4. 次のコマンドで翻訳カタログをコンパイルする。

django-admin.py compilemessages

5. URLとjavascript_catalogビューを結びつけるために、urls.pyに設定を追加する。

urlpatterns = patterns('',
    (r'^jsi18n/(?P<packages>\S+?)/$', 'django.views.i18n.javascript_catalog'),
)

6. htmlファイル内で翻訳したいJavaScriptファイルを読み込む前に、urls.pyで指定したURLをロードする。

<script type="text/javascript" src="/prefix/jsi18n/package_name/"></script>

僕がハマったのは、翻訳ファイルを置くディレクトリの場所です。
この問題にハマる以前は、プロジェクトのディレクトリ構成を以下のようにしていました。

project/ (プロジェクトのトップディレクトリ)
├── bw (アプリケーションのディレクトリ)
│   └── *.py
├── static (静的ファイルの置き場所)
│   └── js (JavaScriptファイルの置き場所)
├── locale (翻訳ファイルの置き場所)
└── *.py

このように翻訳ファイルの置き場所とアプリケーションのディレクトリを分けていると、翻訳文字列が読み込まれません。
その理由は、JavaScriptの翻訳カタログをできるだけ小さくするために、DjangoJavaScriptの翻訳をアプリケーション単位で生成するためです。

以下のように、翻訳したいJavaScriptの入ったディレクトリと翻訳ファイルの入ったディレクトリを、アプリケーションのディレクトリの下に配置することによって、翻訳文字列を正しく読み込むことができます。

project/
├── bw
│   ├── static
│   │   └── js
│   ├── locale
│   └── *.py
└── *.py

このディレクトリ構成かつurls.pyを上記のように設定した場合、JavaScriptの翻訳ファイルのURLは、/prefix/jsi18n/bw/になります。

また、僕が作ったサイトでは、日本語のページと英語のページを別のURLにするために、URLによって英語と日本語を切り替えていました。
Djangojavascript_catalogビューは、現在の言語に基づいて適切な翻訳ファイルを生成してくれるのですが、ブラウザがJavaScriptをキャッシュするため、異なる言語間でページを移動すると、前に使用していた言語が表示されてしまうという現象が起こりました。

翻訳ファイルのURLを言語ごとに分けることで、この問題を解決することができます。
まず、urls.pyを以下のように修正します。

url(r'^(?P<a_locale>(?:en)|(?:ja))/jsi18n/$', views.jsi18n_handler, name="bw.jsi18n")

そして、views.py内でjsi18n_handlerを以下のようにします。

import functools
from django.utils.translation import activate
from django.views.i18n import javascript_catalog

def select_lang(handler):
  @functools.wraps(handler)
  def wrapper(request, *args, **kwargs):
    if kwargs.get('a_locale', False) == 'ja':
      activate('ja')
    else:
      activate('en')
    return handler(request, *args, **kwargs)
  return wrapper

@select_lang
def jsi18n_handler(request, **kwargs):
  return javascript_catalog(request, packages="bw")

Djangoで言語を手動で切り替えるには、django.utils.translation.activateを使用します。
言語の切替は他のビューでも使う汎用的な処理なので、このようにデコレータを作って対処しています。