SOLG System Blog

[Rails]多言語対応をさっくりやってみた

2016年04月18日

global
やっと春らしい気温になり過ごしやすくやってきました
今まですっかり名乗り忘れていましたが、榊原というものです。以後、よろしくお願いします

さっそくですが、本題に入ります!
今回はタイトルを見ての通りですが、さっくりと多言語対応しようということで、Railsのglobalizeというgemを使用した際の話をしていこうと思います

globalizeとは


globalize
Railsには元々国際化(i18n)する機能が備わっていますが、それを更に拡張・手軽に実装するためのgemです
公式のDocument等については、githubに公開されていますので、こちらをごらんください

globalizeのインストール


まずは以下のようにインストールされているActiveRecordのバージョンを確認
$ bundle list | grep activerecord
* activerecord (4.2.5.1)

ActiveRecordのバージョンが4.1以降のものなので、githubのドキュメントの通りにGemfileにglobalizeのgemの記述を追加、っといいたいところだったのですが…sidekiqを導入してスレッドで処理を行う箇所があり、5.0.0だと不具合があったため、5.1.0をインストールする必要がありました。そのため、以下のように記述してbundle installを実行
gem 'globalize', git: 'https://github.com/globalize/globalize.git'

bundle install時に以下のような記述がでていれば問題なくglobalizeのインストールができたことになります
$ bundle install
Using globalize 5.1.0 from https://github.com/globalize/globalize.git (at master@f8a8e0a)


多言語化したいmodelの変更・テーブル作成


今回は該当modelに紐づくテーブルのカラムが[id, name, address, geom, created_at, updated_at]であり、かつ、既にデータが入っていることを前提とします
では、[name, address]を多言語化したい場合、以下のように該当modelに記述します。
記述することによって[name, address]が多言語化の対象と認識されます
class Sample < ActiveRecord::Base
translates :name, :address
end

次に各言語情報を保持するためのテーブルを作成するため、以下のようなmigrate用のファイルを作成し、rake db:migrateを実行します。この際、:migrate_dataをtrue指定することで、作成されるテーブルに元のテーブルのデータをmigrateしてくれます
※元のテーブルのデータをmigrateする際に設定される言語については、config/application.rbのconfig.i18n.default_localeに設定されている値に依存すると思うのですが…試した方いたら教えて下さい!
class CreateTranslateSample < ActiveRecord::Migration
def self.up
Sample.create_translation_table!({
:name => :string,
:address => :string
}, {
:migrate_data => true
})
end

def self.down
Sample.drop_translation_table! :migrate_data => true
end
end

migrateに成功すると以下の構成でsample_translationsテーブルが作成されます。
以降、sampleテーブルの[name, address]カラムは参照・保存・更新はされず、sample_translationsテーブルの各言語の[name, address]カラムが参照・保存・更新されます

FieldTypeNullKeyDefaultExtra
id int(11) NO PRI NULL auto_increment
sample_id int(11) NO MUL NULL
locale varchar(255) NO MUL NULL
created_at datetime NO NULL
updated_at datetime NO NULL
name varchar(255) YES NULL
address varchar(255) YES NULL


自分の場合は、更新されないカラムが残り続けるのが気持ち悪かったので、以下のようにmigrateファイルを作成してsampleテーブルのカラムを削除しました
class RemoveSampleTranslateColumns < ActiveRecord::Migration
def change
remove_column :locations, :name
remove_column :locations, :address
end
end


多言語化したテーブルの保存・更新・参照


設定については、一通り終了したので、実際に多言語化したテーブルに複数の言語の登録等を行っていきます
保存・更新・参照は以下のようにI18n.localeの値を切り替えるだけで、自動的にやってくれます
#日本語で新規データ作成
I18n.locale = :ja
ja_data = Sample.create({name: '名前', address: '住所'})
ja_data.name # => '名前'
ja_data.address # => '住所'

# 英語で新規データ作成
I18n.locale=:en
en_data = Sample.create({name: 'name', address: 'address'})
en_data.name # => 'name'
en_data.address # => 'address'

# 各言語データ取得
data = Sample.first
I18n.locale = :ja
data.name # => '名前'
I18n.locale = :en
data.name # => 'name'

# 各言語データ更新
data = Sample.first
I18n.locale = :ja
data.name # => '名前'
data.name = '名前_変更'
data.save
I18n.locale = :en
data.name # => 'name'
data.name = 'name_mod'
data.save

data = Sample.first
I18n.locale = :ja
data.name # => '名前_変更'
I18n.locale = :en
data.name # => 'name_mod'

ただし、I18n.localeなんですが、いろいろ調べた際、擬似グローバルという記述を見つけました。グローバルの値なので、複数のユーザが操作していると、タイミングによってはI18n.localeの値が変わってしまい、意図しない言語で保存してしまう場合があります。そのため、保存・更新時は基本的にI18n.localeの値を直接使うのではなく、以下のように対応することでI18nの値に左右されなくなります
実際、その擬似グローバルという記述を確認したサイトは、こことなりますので、気になる人はご確認ください
# 日本語で新規データ作成・更新
I18n.with_locale(:ja) {
data = Sample.create({name:'名前', address:'住所'})
data.name # => '名前'
data.address # => '住所'
data.name = '名前_変更'
data.address = '住所_変更'
data.save

data_mod = Sample.first
data_mod.name # => '名前_変更'
data_mod.address # => '住所_変更'
}

# 英語で新規データ作成
I18n.with_locale(:en) {
data = Sample.create({name:'name', address:'address'})
data.name # => 'name'
data.address # => 'address'
data.name = 'name_mod'
data.address = 'address_mod'
data.save

data_mod = Sample.first
data_mod.name # => 'name_mod'
data_mod.address # => 'address_mod'
}


設定されていない言語を表示した際に変わりの別言語を表示する


例えば以下のように、日本語のデータは作成したけど、英語のデータを作成していない場合、英語で該当データを参照するとnilが戻されます
# 日本語で新規データ作成・更新
I18n.with_locale(:ja) {
data = Sample.create({name:'名前', address:'住所'})
data.name # => '名前'
data.address # => '住所'
}
I18n.locale = :en
data.name # => nil
data.address # => nil

こういう場合、英語データがない場合は日本語データを表示するみたいなことやりたくなりますよね…?
もちろん方法があって、config/application.rbに以下のように記述すると対応できます。左から優先順位が高く、必ず元の言語も書かないとならないというところにご注意ください
config.i18n.enforce_available_locales = true
config.i18n.fallbacks = {
:en => %i(en ja)
}


まとめ


まだまだ注意点が多いのですが、ざっくりとはこんな感じになります
間違っていたり、回りくどいことやってる場合は、びしびし指摘してください

最初からglobalizeを使うのであれば、タイトル通りさっくりとできるのですが、すでにがっつりと運用しているサイトに使用するとなるとそれなりに影響範囲は広くなるので、使用する際はご計画的に!

っということで今回は長くなりましたがこの辺りで終わりにします。
次回はsidekiqの導入について書こうかなっと思います。

同じカテゴリー(rails)の記事画像
【未経験者にもおすすめ!】 おすすめプログラミング勉強サイト「Progate」
[Rails]AWS SDK for Ruby(2.5系)を使用してファイルをS3にアップロードしてみた
同じカテゴリー(rails)の記事
 【未経験者にもおすすめ!】 おすすめプログラミング勉強サイト「Progate」 (2016-11-07 21:14)
 [Rails]AWS SDK for Ruby(2.5系)を使用してファイルをS3にアップロードしてみた (2016-09-06 11:42)
 [Rails]Railsのバージョンを上げたことでspatial_adapterが使用できなくなりました (2016-01-14 23:03)

Posted by iA SEチーム at 17:44│Comments(1)rails
Responses to "[Rails]多言語対応をさっくりやってみた"
kuboon Says:
I18n.locale はスレッドローカルです。Railsであればリクエストの処理は1つのスレッドが最後まで受け持つので問題ありません。
上の画像に書かれている文字を入力して下さい
<ご注意>
書き込まれた内容は公開され、ブログの持ち主だけが削除できます。

PageTop

削除
[Rails]多言語対応をさっくりやってみた
    コメント(1)