I18n のバックエンドにデータベースを使用する
Rails 3.2.7 で I18n のバックエンドに RDBMS を使う、というのを試してみました。
i18n-active_record という gem を使うようです。
まずはお試し用のプロジェクトを作成します。
RDBMS には MySQL を選択しました。
$ rails new i18n_backend_example -d mysql --skip-bundle $ cd i18n_backend_example/
Gemfile を編集します。
$ vi Gemfile
i18n-active_record を追加。
gem 'i18n-active_record', git:'git://github.com/svenfuchs/i18n-active_record.git', require: 'i18n/active_record'
変更したら bundler を実行し、インストールを行います。
$ bundle install --path=vendor/bundle
インストールが完了したら、config/initializers/locale.rb というファイルを作成し、以下のように I18n.backend を初期化します。
require 'i18n/backend/active_record' I18n.backend = I18n::Backend::Chain.new(I18n::Backend::ActiveRecord.new, I18n.backend)
データベースに存在しない key が指定された場合、従来通り YAML ファイルも見てくれるようにしたいので、ここでは I18n::Backend::Chain を使用しています。
次にマイグレーションファイルを作成します。
model ジェネレータを使用して Translation モデルを作成しても良いのですが、ここではマイグレーションファイルのみを作成しました。
バックエンドとして動作させるにはテーブルだけあればモデルは無くても問題ないようです。
$ rails g migration CreateTranslations
db/migrate 以下に生成されたマイグレーションファイルの内容を以下のように編集します。
class CreateTranslations < ActiveRecord::Migration def up create_table "translations", :force => true do |t| t.string "locale" t.string "key" t.text "value" t.text "interpolations" t.boolean "is_proc", :default => false t.datetime "created_at", :null => false t.datetime "updated_at", :null => false t.timestamps end end def down drop_table :translations end end
マイグレーション実行前に、config/database.yml のデータベース接続設定を環境に合わせて変更しておいて下さい。
準備が出来たらマイグレーションを実行します。
$ rake db:create && rake db:migrate
これでデータベース、テーブルが作成されたので翻訳データを投入してみます。
ここでは Translation モデルを作成していないので、dbconsole から SQL でデータを投入しました。
$ rails dbconsole mysql> insert into translations(locale, `key`, `value`, created_at, updated_at) values("ja", "hoge", "ほげ", now(), now());
これで準備が出来ました。
rails console を起動して動作確認を行います。
$ rails c Loading development environment (Rails 3.2.7) irb(main):001:0> I18n.locale = :ja => :ja irb(main):002:0> I18n.t(:hoge) I18n::Backend::ActiveRecord::Translation Load (0.3ms) SELECT `translations`.* FROM `translations` WHERE `translations`.`locale` = 'ja' AND (`key` IN ('hoge') OR `key` LIKE 'hoge.%') => "ほげ"
Translations テーブルに投入した翻訳データが取得できていることが分かります。
次に、データベースに存在しない key が指定された場合に、YAML ファイルを見てくれているかを確かめるため、config/locales/ja.yml を作成します。
$ vi config/locales/ja.yml
データベースに存在しない key を設定しておきます。
ja: fuga: "ふが"
再度 rails console を起動し、先ほど YAML ファイルに追加したキーを指定してみます。
$ rails c Loading development environment (Rails 3.2.7) irb(main):001:0> I18n.locale = :ja => :ja irb(main):002:0> I18n.t(:fuga) I18n::Backend::ActiveRecord::Translation Load (0.8ms) SELECT `translations`.* FROM `translations` WHERE `translations`.`locale` = 'ja' AND (`key` IN ('fuga') OR `key` LIKE 'fuga.%') => "ふが"
YAML ファイルに設定した翻訳データが無事取得できました。
I18n::Backend::Chain を生成する際に先に I18n::Backend::ActiveRecord.new を指定しているので、データベースで検索が行われた後、見つからなかった場合に YAML ファイルの内容が検索されます。
両方に同じ key が存在している場合、 I18n::Backend::Chain に指定した順で検索が行われ、先に見つかった方が返されるようです。
実際に YAML 以外に翻訳データを持ちたい場合、RailsCasts にあるように KeyValueStore を使う方がパフォーマンス的に良い選択だと思いますが、使い慣れた RDBMS をそのまま使える点や、RDBMS 以外のミドルウェアを用意せずに使用できるのはお手軽で嬉しいですね。
参考:
6.1 Using Different Backends
http://guides.rubyonrails.org/i18n.html#using-different-backends
svenfuchs/i18n-active_record ・ GitHub
https://github.com/svenfuchs/i18n-active_record
Crowd Interactive Tech Blog :: Load i18n translations into your activerecord database
http://blog.crowdint.com/2012/07/26/load-i18-translations-into-your-activerecord-database.html
#256 I18n Backends - RailsCasts
http://railscasts.com/episodes/256-i18n-backends?language=ja&view=asciicast