その他
    ホーム技術発信DoRuby【Rails】I18nの言語データをDBから取得【i18n/i18n-active_record】

    【Rails】I18nの言語データをDBから取得【i18n/i18n-active_record】

    この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

    I18nの言語データをymlではなくDBから取得する方法を記述します。

    準備

    1.Gemfileに追記してbundle install

    Gemfile

    gem 'i18n-active_record', :require => 'i18n/active_record'
    

    2.migrateファイル作成

    $ bundle exec rails g migrations CreateTranslations locale:string key:string value:text interpolations:text is_proc:boolean
    
    class CreateTranslations < ActiveRecord::Migration
      def self.up
        create_table :translations do |t|
          t.string :locale
          t.string :key
          t.text   :value
          t.text   :interpolations
          t.boolean :is_proc, :default => false
    
          t.timestamps
        end
      end
    end
    

    3.config/initializers/i18n_active_record.rbの作成

    require 'i18n/backend/active_record'
    
    Translation = I18n::Backend::ActiveRecord::Translation
    
    if Translation.table_exists?
      I18n.backend = I18n::Backend::ActiveRecord.new
    
      I18n::Backend::ActiveRecord.send(:include, I18n::Backend::Flatten)
      I18n::Backend::Simple.send(:include, I18n::Backend::Pluralization)
    
      # 一度呼んだkey,valueをインスタンス変数に保存する場合に使用。
      # 毎回SQLを実行しなくて済むがI18n.reload!しないとDBの値が書き換わっても前回の値が表示される
      # マルチテナント非対応
      # I18n::Backend::ActiveRecord.send(:include, I18n::Backend::Memoize)
      # I18n::Backend::Simple.send(:include, I18n::Backend::Memoize)
    
      I18n.backend = I18n::Backend::Chain.new(I18n.backend, I18n::Backend::Simple.new)
    
      # I18n.t()でDBでもファイルでも言語がヒットしなかった場合、その時のkeyやoptions等の情報ををDBに保存したい時に使用
      # 後でレコードやlocaleファイルを追加・更新する時に便利になるが毎度saveが走るためパフォーマンス低下の恐れあり
      # I18n::Backend::Chain.send(:include, I18n::Backend::ActiveRecord::Missing)
      # I18n.backend = I18n::Backend::Chain.new(I18n::Backend::ActiveRecord.new, I18n::Backend::Simple.new)
    end
    
    

    使い方

    1. 通常

    config/locales/ja.yml

    ja:
      user:
        name: ymlのユーザー名
    
    $ bundle exec rails c
    
    [1] pry(main)> I18n.t('user.name')
    => 'ymlのユーザー名'
    
    [2] pry(main)> Translation.create(locale: :ja, key: 'user.name', value: 'DBのユーザー名')
    => #<I18n::Backend::ActiveRecord::Translation...........
    
    [3] pry(main)> I18n.t('user.name')
    => 'DBのユーザー名'
    

    2. proc

    $ bundle exec rails c
    
    [1] pry(main)> Translation.create(locale: :ja, key: 'test.proc', value: '1+2', is_proc: true)
    => #<I18n::Backend::ActiveRecord::Translation...........
    
    [2] pry(main)> I18n.t('test.proc')
    => 3
    

    Kernel.eval(value)してるだけみたい。ソース

    3. I18n::Backend::ActiveRecord::Missingの使用時

    config/initializers/i18n_active_record.rb

    # I18n::Backend::Chain.send(:include, I18n::Backend::ActiveRecord::Missing)
    # I18n.backend = I18n::Backend::Chain.new(I18n::Backend::ActiveRecord.new, I18n::Backend::Simple.new)
    

    ↓コメントアウトを外す

    I18n::Backend::Chain.send(:include, I18n::Backend::ActiveRecord::Missing)
    I18n.backend = I18n::Backend::Chain.new(I18n::Backend::ActiveRecord.new, I18n::Backend::Simple.new)
    
    $ bundle exec rails c
    
    [1] pry(main)>  I18n.t('user.name', name: 'なまえ', append: 'です')
    => "translation missing: ja.user.name"
    
    [2] pry(main)> Translation.last
    => #<...Translation... id: 1, locale: :ja, key: 'user.name', value: nil, interpolations: [:name, :append], ....
    

    DBに入っていないものを参照したときにレコードが自動的に作られる。
    interpolationsカラムにその時の引数の名前が入る。

    注意事項

    このままではI18n.tを呼ぶたびに毎回DBアクセスが走るのでキャッシュを使用する方法も次回記述します。
    次回の記事