ActiveRecord モデルのフィールド名の国際化

2009/01/08

前回は、サンプルアプリケーション asagao にロケールを切り替える機能を加えました。

今回は、ActiveRecord 関連の国際化に挑戦します。

あるオブジェクトの中身や編集フォームを表示する際のフィールド名をロケールごとに切り替えられるようにします。

『基礎 Ruby on Rails』では、REAL_ATTRIBUTE_NAMES という定数と real_attribute_name というクラスメソッドをモデルごとに定義していました。

例えば、member.rb には次のような記述があります。

  # 属性に対応する日本語名
  REAL_ATTRIBUTE_NAMES = {
    :member_number => '背番号',
    :player => '選手登録',
    :family_name => '名前(姓)',
    :given_name => '名前(名)',
    :furigana => 'ふりがな',
    :email => 'メールアドレス',
    :phone => '電話番号',
    :birthday => '生年月日',
    :sex => '性別',
    :remarks => '備考',
    :login_name => 'ログイン名',
    :password => 'パスワードの変更',
    :administrator => 'サイト管理者',
    :uploaded_image => '画像'
  }
  
  def self.real_attribute_name(key)
    REAL_ATTRIBUTE_NAMES[key.to_sym]
  end

そして、テンプレートの中では次のように書いていたのです。

<%= Member.real_attribute_name(:phone) %>

Rails 2.2 からは、ActiveRecord::Base のクラスメソッド human_attribute_name が内部的に I18n モジュールを利用するようになりました。

新しい方法で書き換えることにします。

では、始めましょう。


まず、影響範囲を確かめるため、member.rb から上記のコードを削除して、テストを実行します。

単体テストではエラーは出ていません。これは必ずしもいいことではありません。real_attribute_name メソッドのテストが書かれていなかったことを意味します。要反省です。

機能テストでは、9 つのテストが失敗しています。

> rake test:functionals
(省略)
95 tests, 320 assertions, 9 failures, 0 errors

accountsmembersadmin/members コントローラで失敗しています。想定通りです。

real_attribute_name メソッドをすべて human_attribute_name で置換すれば、テストは通るでしょう。

お気に入りのエディタの「一括置換」機能で、app/views/accountsapp/views/membersapp/views/admin/members ディレクトリ以下の全ファイルを対象に、real_attribute_namehuman_attribute_name に置換してください。

再度、機能テストを実行します。

> rake test:functionals
(省略)
95 tests, 320 assertions, 9 failures, 0 errors

ああ!ダメです。こんなエラーが出ています。

undefined method `humanize' for :member_number:Symbol

real_attribute_name メソッドはシンボルを取りましたが、human_attribute_name メソッドの場合は文字列でフィールド名を指定する必要があるんですね。

ということは、human_attribute_name(:foo)human_attribute_name('foo') に置換する必要があるわけです。

こういう複雑な置換をしてくれるエディタもありますが、ここでは誰でもできるように小さな Ruby スクリプトを作ることにします。

次のようなスクリプトを作成して、scrpts ディレクトリの下に humanize.rb という名前で保存してください。

dir = ARGV[0]

Dir.glob("app/views/#{dir}/**/*.rhtml").each do |file|
  code = File.open(file).read
  code.gsub!(/real_attribute_name/, 'human_attribute_name')
  code.gsub!(/human_attribute_name\(:(\w+)\)/, 'human_attribute_name("\1")')
  File.open(file, 'w').write(code)
end

置換を実行します。

> ruby script/humanize.rb accounts
> ruby script/humanize.rb members
> ruby script/humanize.rb admin/members

再度、機能テストを実行します。

> rake test:functionals
(省略)
95 tests, 338 assertions, 3 failures, 0 errors

あらら。まだ、エラーが出ますね。

app/views/admin/members/_errors.rhtml が原因です。

      <strong><%= h(Member.human_attribute_name(attr.to_sym)) %></strong> <%= h(msg) %>

を次のように修正します(.to_sym を除去)。

      <strong><%= h(Member.human_attribute_name(attr)) %></strong> <%= h(msg) %>

再度、機能テストを実行します。

> rake test:functionals
(省略)
95 tests, 341 assertions, 0 failures, 0 errors

OK です。

> rake test:integration
(省略)

2 tests, 67 assertions, 0 failures, 0 errors

統合テストも通りました。


次に、翻訳ファイルを作成します。

config/locales ディレクトリに activerecord_en.ymlactiverecord_ja.yml を作成します。

activerecord_en.yml

en:
  activerecord:
    attributes:
      member:
        member_number: Member Number
        player: Player
        family_name: Family Name
        given_name: Given Name
        furigana: Furigana
        email: E-mail address
        phone: Phone Number
        birthday: Birthday
        sex: Sex
        remarks: Remarks
        login_name: Login Name
        password: Change Password
        administrator: Administrator
        uploaded_image: Uploaded Image

activerecord_ja.yml

ja:
  activerecord:
    attributes:
      member:
        member_number: 背番号
        player: 選手登録
        family_name: 名前(姓)
        given_name: 名前(名)
        furigana: ふりがな
        email: メールアドレス
        phone: 電話番号
        birthday: 生年月日
        sex: 性別
        remarks: 備考
        login_name: ログイン名
        password: パスワード
        administrator: サイト管理者
        uploaded_image: 画像'

サーバを起動して、メンバーとしてログインしてください。

自分のアカウントを変更したり、会員名簿や管理ページの会員管理機能を使ってみてください。

そして、日本語と英語を切り替えて、フィールド名が正しく表示されることを確認してください。

新しい翻訳ファイルを追加した時は、development 環境でもサーバの再起動が必要ですので注意してください。

単に翻訳ファイルを修正しただけなら、再起動は不要です。

asagao アカウント英語表示

asagao アカウント日本語表示

前述したように、Rails 2.2 の human_attribute_name は内部的に I18n モジュールを利用しています。

Member.human_attribute_name('phone') と記述すると、t('activecord.attributes.member.phone') と書いたのと同じことになります。

本日はここまでとしましょう。