ActiveRecordのenumをi18n対応させたり、formのselectでの表示に対応するためにgemが幾つかありますが、そこまで複雑なことがやりたかったわけでは無いので、手軽に自分で実装してみます。
ja.yml
モデルのattributeのi18nは通常、'activerecord.attributes.#{model_name}.#{attribute}'
に定義します。例えば、Userモデルのnameに適用する場合には、
ja:
activerecord:
models:
user: ユーザー
attributes:
user:
name: 名前
という形で定義し、User.human_attribute_name(:name)
という形でアクセスします。
それでは、例えばfemale
とmale
というを値を持つgender
という属性を定義します。
class User < ApplicationRecord
enum gender: [:female, :male]
end
この場合、それぞれの値に対する国際化対応の手段は標準では用意されていません。正確には、定義の仕方は用意されていますが簡単にアクセスできる手段はありません。
定義する場合には、以下のように直接定義することは出来ません。
ja:
activerecord:
models:
user: ユーザー
attributes:
user:
gender:
female: 女性
male: 男性
# このネストの仕方は、複数値で形が変わる英語のような言語のための機能とバッティングします
この場合、以下のような形でモデルと同じ階層に定義してあげる必要があります。(http://guides.rubyonrails.org/i18n.html)
ja:
activerecord:
models:
user: ユーザー
attributes:
user:
name: 名前
gender: 性別
user/gender:
female: 女性
male: 男性
このように定義することで、User.human_attribute_name('gender.female')
のようにアクセス出来るようになります。また、この場合User.human_attribute_name('gender')
も問題なく呼ぶことが出来ます。
Model
定義とアクセスの方法がわかったので、後はモデルにメソッドを追加していきます。
class ApplicationRecord < ActiveRecord::Base
self.abstract_class = true
def self.human_attribute_enum_value(attr_name, value)
human_attribute_name("#{attr_name}.#{value}")
end
def human_attribute_enum(attr_name)
self.class.human_attribute_enum_value(attr_name, self[attr_name])
end
end
これで、以下のようにアクセスすることが出来ます。
u = User.new(gender: 'female')
u.human_attribute_enum(:gender) #=> 女性
User.human_attribute_enum_value(:gender, :male) #=> 男性
selectに値を渡したい場合には
User.genders.map {|k, _| [User.human_attribute_enum_value(:gender, k), k] }.to_h #=> {"女性"=>"female","男性"=>"male"}
のように定義出来ます。或いはメソッドを更に追加しても良いかもしれません。
class ApplicationRecord < ActiveRecord::Base
# ...
def self.enum_options_for_select(attr_name)
self.send(attr_name.to_s.pluralize).map {|k, _| [self.human_attribute_enum_value(attr_name, k), k] }.to_h
end
end
User.enum_options_for_select(:gender) #=> {"女性"=>"female","男性"=>"male"}
まとめ
むやみにgemを追加していくことは避けたいと思うので、国際化対応が出来ればそれでOKくらいの用途であればこのくらい手軽な定義でも困らないんじゃないかなと思います。