Rails のためのものぐさな Web アプリケーションの国際化手法
最近、自分が作る Web アプリケーションで、日本語圏に限らず使ってもらえそうな物は最初から一応国際化(というか英語対応)して作るようにしています。国際化対応しておくと、はてブに限らず、del.icio.us や digg で取り上げられたりして、いろいろな人に使えてもらって嬉しいし海外からも adsense 収入gです。del.icio.us のトップや /popular/ からのリファラは、はてブトップ or 人気エントリーからのリファラとそんなに変わらないぐらいなのですが、digg からのアクセスはその十数倍あって驚きでした。
で、本題の国際化の方法なのですが、favicon2dots や polaroizeぐらいの小粒なアプリケーションなら、ほんの数分〜十数分作業時間を増やすだけで対応できてしまうので、その方法のご紹介を。
ruby-gettext
武藤さん作の ruby-gettext をインストールすると、Rails から使える gettext/rails も一緒にインストールされるので、本当に簡単に国際化対応ができるようになります。
# gem install gettext
すごい簡単ですね。あとは config/environment.rb に
require 'gettext/rails'
を、ApplicationController 内部に
init_gettext "example" # アプリケーション名にあわせて適切に変える
を書くだけで終了です。で、その後各種言語の *.rhtml に各種言語で書かれた rhtml ファイルを作るだけで自動で選択してくれます。
- app/view/foo/index.rhtml (デフォルト)
- app/view/foo/index_ja.rhtml (日本語用)
ちなみにこのロケールの選択は次のような順番で選択されます。
http://www.yotabanana.com/hiki/ja/ruby-gettext-howto-cgi.html
ロケール情報は、QUERY_STRINGのlangの値 > Cookieのlangの値 > WWWブラウザが返すHTTP_ACCEPT_LANGUAGE環境変数 > "en"(英語) の順番で決まります。
プライマリの言語を ja にしてしまう
さて、ここからが真のものぐさな国際化(というか英語)対応の方法を。趣味で作るアプリなどで英語が得意な人が作るならいざ知らず、英語が大変苦手な人間(自分)が最初から英語で書くのはめんどくさいし、そもそもそんなことを考えてるスキマは無いですね。また、過去に作ったアプリを英語化しようと思ったら以前のを *_ja.rhtml にリネームして、というのもちょっと微妙かもしれない、とか思ったりします。
そんな時はデフォルトは ja、HTTP_ACCEPT_LANGUAGE の en の優先度が高かったら en を返すように割り切ってしまいましょう。といっても、とくに難しく考えることはなくて、
- app/view/foo/index.rhtml (デフォルト・日本語用)
- app/view/foo/index_en.rhtml (英語圏用)
とするだけです。これなら後から英語のテンプレートを付け足すだけで簡単ですね。また、ApplicationController に
after_init_gettext :locale_setting
def locale_setting
GetText.locale = ::Locale::SystemCGI.system.language rescue nil
end
次のような gettext 初期化前のメソッドを定義しないと、うちの Rails アプリではうまくテンプレートが選択されません*1でした。
_() 関数での国際化
これで別テンプレートを用意することでのものぐさな国際化は完了します。が、別のテンプレートを用意すると云うことはすなわちテンプレートの実装*2が変わったら二つのテンプレートを修正、などめんどくさくてやってられませんね。そんなときのために、_() 関数です。
_() 関数の間に文字列を挟むことで、その文字列を国際化させることができます。詳しい _() の説明はRuby-GetText-Package 開発手順 - よたらぼ 保管庫をご覧下さい。
通常は
class FooController
def index
hello = 'こんにちは'
と書くような箇所も _() で囲むだけです。
class FooController
def index
hello = _('こんにちは')
oh, kantan ですね。プライマリは ja と割り切っているおかげで普通に日本語を囲むだけです。また文字列に変数を入れたいときは String#% メソッド を使います。
class FooController
def index
hello = "こんにちは、#{name}さん"
なら
class FooController
def index
hello = _("こんにちは、%sさん") % name
のようになります。このように、日本語を書く箇所にちょっとした配慮をしていくだけで国際化できてしまいます。また開発時は他に何もせずとも、エラーなどは発生しないため、ストレスも発生しません。
最後の国際化
このように開発を続けていき(といっても小粒なアプリケーションでは1〜20ぐらいしか_()で囲む箇所は発生しないと思います)完成したら po ファイルをつくってちょろっと翻訳するだけです。
Rakefile に updatepo/makemo を追加
gettext には update_pofiles, create_mofiles という便利なメソッドが用意されており、これらのメソッドを呼び出すだけで国際化するのに必要なファイルやバイナリを作成してくれます。ので、より簡単に使うために Rakefile に記述するしておきましょう。
require 'rubygems'
require 'gettext/utils'
desc 'Update pot/po files.'
task :updatepo do
GetText.update_pofiles('example',
Dir.glob("{app,config,lib}/**/*.{rb,rhtml}"),
'example1.0.0'
)
end
desc 'Create mo-files'
task :makemo do
GetText.create_mofiles(true, 'po', 'locale')
end
次に、国際化するためのテンプレートファイルを生成します。
$ rake updatepo
po/example.pot
po/example.pot というのが作成されましたね。英語対応のために po/en/example.po (コピー先のファイル名は pot じゃないです)にファイルをコピーし、その後 po/en/example.po を編集します。
#: app/controllers/foo_controller.rb:3
msgid "こんにちは"
msgstr ""
などとなってると思うので、英訳後の文字列を msgstr に入れます。
#: app/controllers/foo_controller.rb:3
msgid "こんにちは"
msgstr "hello"
その後 makemo で .mo ファイルを適切な場所に自動生成します。
$ rake makemo
po/en/example.po -> locale/en/LC_MESSAGES/example.mo
また、文言の追加、修正などがあったら rake updatepo して po/en/example.po を編集して makemo すれば終わりです。
終わりに
これでものぐさな国際化方法は終わりです。
はたしてプライマリーな言語を ja に持ってくることが良いことなのかどうかはさておいて、日本語の文字列に _() つけるだけなので開発中は全然国際化対応の事を考えなくてすみますし、最後にちょこっと po 作って翻訳するだけでいいので、個人的には英語化するコストがぐんと減りました。また最後の最後で国際化めんどくさーい、と思っても国際化せずにリリースすることもできます。
なので Rails で、日本語圏以外でも有益そうな Web アプリケーションを作っている方は英語化対応を考えてみてはどうでしょうか。本当に簡単にできますよ。
またこれほど簡単に gettext で国際化ができるのも ruby-gettext 作者の武藤さんのおかげです。ありがとうございます。
次回は Rails での TimeZone 対応についてのお話です、といきたいところですが今のところめっちゃスマートにはできてません。だれか書いてください!特に ActiveRecord とタイムゾーンのマッピングがいまいちスマートにできてません。。
*1:GetText.locale の値が HTTP_ACCEPT_LANGUAGE のが適用されなくなる?まだ深追いしてません
*2:そもそもテンプレートに実装書くなという話は置いておきます。というか rhtml な以上実装を書きますよ!