実行環境:
ruby 1.9.3
Rails 3.1.3
I18n
Railsアプリで表示を担当するのはビューなので国際化するのは当然ビューですね。ページに表示される文字すべてが対象となります。
ページに表示される文字をざっと分類してみましょう。
- DBに格納されている文字列
- HTMLに直接書く単語や文章
- モデルの名前や属性の名前などモデルに関連する文字列
- Railsのエラーメッセージ
1. DBに格納されている文字列
DBに格納されているデータを国際化(多言語化)する場合は、各ロケールごとのデータを用意する必要があります(日本語だけなら不要です)。
シンプルな方法としては、テーブルに複数のロケール用データをカラムを増やして各ロケールのデータを格納します。
TABLEの構造
+------+----------+------------+
| id | hello_en | hello_ja |
+------+----------+------------+
| 1 | Hello | こんにちは |
+------+----------+------------+
もしくはハッシュの形で各ロケールのデータを1つにまとめて(=シリアライズして)DBの1つのカラムに突っ込むという手もあります。
#{RAILS.ROOT}/app/models/greetings.rb
class Greeting < ActiveRecord::Base
serialize :data, Hash
end
@greeting.data = {:en => "Hello", :ja => "こんにちは"}
いずれの方法にせよ、表示の際にロケールにあわせて表示し分けるようにします。
2. HTMLに直接書く単語や文章
とりあえず自由に辞書を定義する
HTMLテンプレートに書くタイトルや文章などを辞書ファイルで定義していく、というのがとりあえず思いつくところでしょうか。辞書ファイルはハッシュの形で定義します。基本的にキーを自由につけることができますし、整理のために自由に階層をつけることができます。
#{RAILS.ROOT}/config/locale/ja.yml
ja:
hello: こんにちは
foods:
vegetables:
cucumber: きゅうり
参照するには I18n.t メソッドを使います。
Controllerの中では
#{RAILS.ROOT}/app/controllers/users_controller.rb
I18n.t('hello')
I18n.t('foods.vegetables.cucumber')
Viewの中では
#{RAILS.ROOT}/app/views/users/index.html.erb
<%= t('hello') %>
<%= t('foods.vegetables.cucumber') %>
という形で参照します。
「きゅうり」のように階層になっている用語については、上位の階層をscopeと見ることもでき、
#{RAILS.ROOT}/app/views/users/index.html.erb
<%= t('cucumber', :scope => 'foods.vegetables') %>
<%= t('vegetables.cucumber', :scope => 'foods') %>
という書き方も可能。
キーの指定は文字列だけでなく、シンボルでもいけます。
#{RAILS.ROOT}/app/views/users/index.html.erb
<%= t(:hello) %>
<%= t(:foods, :scope => 'vegetables.cucumber') %>
<%= t(:foods, :scope => [:vegetables, :cucumber]) %>
ビュー別の定義
特定のビューで使用する辞書定義は、ルールに従って定義するとビューファイルの中で簡単に参照できます。
#{RAILS.ROOT}/config/locale/ja.yml
ja:
user:
index:
title: ユーザー一覧
show:
title: ユーザーの詳細
このように定義するとビューファイルの中で <%= t('.title') %> で参照でき、index.html.erb と show.html.erb でそれぞれの定義が使われます。
ロケール別のHTMLテンプレート
単語・文章レベルで表示を差し替えればよいだけであれば上記の方法で対応できますが、ロケールによってページ内の構成が大幅に異なる場合などはロケールごとに別々にHTMLテンプレートを用意しておくこともできます。
#{RAILS.ROOT}/app/views/users/
[users]
|-- index.en.html.erb
|-- index.ja.html.erb
|-- index.de.html.erb
ロケールにあわせて適切なHTMLテンプレートを選択してくれます。
3. モデルの名前や属性の名前などモデルに関連する文字列
モデルに関連する名前は次のように辞書ファイルに書きます。
#{RAILS.ROOT}/config/locale/ja.yml
ja:
activerecord:
models:
user: ユーザー
attributes:
user:
name: 名前
password: パスワード
モデル名、モデルの各属性名は
User.model_name.human
User.human_attribute_name(name)
のように簡単に参照できます。もちろん通常通り I18n.t('activerecord.models.attributes.name') でも参照できます。
form の中ではモデルの属性値を表す label が自動的に辞書ファイルの内容で置き換えられます。
#{RAILS.ROOT}/app/views/edit.html.erb
<%= form_for(@user) do |f| %>
<%= f.label :name %>
<%= end %>
※個人的には、毎回 User.model_name.human、User.human_attribute_name(name)とか書くのは若干面倒だなぁと思うので、こんなヘルパーメソッドを作ってモデル名やモデルの属性名が超簡単に拾えるようにしてます。
#{RAILS.ROOT}/app/helpers/application_helper.rb
def t_ar(label)
arr = label.split(/./)
if arr.length <= 2 then
begin
model = arr[0].constantize
else
if arr.length == 1
return model.model_name.human
else
return model.human_attribute_name(arr[1])
end
end
end
label
end
t_ar('User')
t_ar('User.name')
4. Railsのエラーメッセージ
エラーメッセージも辞書ファイルで定義することで日本語化できます。
#{RAILS.ROOT}/config/locale/ja.yml
ja:
activerecord:
errors:
messages: ユーザー一覧
blank: "が記入されていません。"
invalid: "が不正な値です。"
confirmation: "が一致しません。"
...
エラーの種類に対応したエラーメッセージをこのように定義します。この定義はすべてのモデルのエラーをカバーします。
一方、特定のモデルや特定の属性について別のエラーメッセージを指定するには次のようにします。
特定のモデル用のエラーメッセージ
#{RAILS.ROOT}/config/locale/ja.yml
ja:
activerecord:
errors:
models:
user:
blank: "が記入されていません。"
...
特定のモデルの特定の属性用のエラーメッセージ
#{RAILS.ROOT}/config/locale/ja.yml
ja:
activerecord:
errors:
models:
user:
attributes:
name:
blank: "が記入されていません。"
...
より細かいところでの定義が優先されます。
Userモデルのname属性についてのvalidationが以下のように定義されていてnameに値にblankエラーが発生した場合、③ → ② → ① の順に探して最初に見つかった定義が使われます。
#{RAILS.ROOT}/app/models/user.rb
class User < ActiveRecord::Base
validates_presence_of :name
end