FBCのRailsのi18nのプラクティスを進めています。
_form.html.erb
内のフォーム内のsubmitボタンの翻訳情報をhelpers.submit.create
やhelpers.submit.update
などで参照できるのか分からなかったのでソースコードを読んで調べてみました。
ちなみにVSCodeでソースコードを開く設定は前回の記事に書いたので参考にしてみてくださ。
調べる対象のRailsのコードです。
# app/views/books/_form.html.erb <%= form_with(model: book, local: true) do |form| %> ... <%= form.label :title %> <%= form.text_field :title %> ... <%= form.submit %> # ここの翻訳情報をどう参照しているのか知りたい <% end %>
# config/locales/ja.yml ja: helpers: submit: create: 登録する # newメソッド時に参照される update: 更新する # editメソッド時に参照される
ソースコードを読むこと自体が初めてだったので解釈や読む場所が違うなど間違っている可能性があるかもしれません。 間違い等はコメント等で指摘していただけると助かります。
結論
i18n gemとrails gem で以下3点が設定されており、自動で翻訳情報の参照先を切り替えているということが分かりました。
仕組みを調べるにあたって特に分からなかった以下の4つを個別にまとめました。
- Q1:
helpers.submit.create
で参照するロケールファイルはどこにあるのか? - Q2.
helpers.submit.create
で参照するロケールファイルはどこで設定されているのか? - Q3. i18nの
t
メソッドのロケールの切り替えはどこで設定されているのか? - Q4. 翻訳情報の参照先の
create
やupdate
の切替はどこで設定されているのか?
Q1. helpers.submit.create
で参照するロケールファイルはどこにあるのか?
以下のファイルに参照するロケールファイルが定義されていました。
- gem:
rails
- ファイル:
actionview/lib/action_view/locale/en.yml
"en": ... helpers: ... submit: create: 'Create %{model}' update: 'Update %{model}' submit: 'Save %{model}'
rails/en.yml at main · rails/rails
この翻訳情報を参照してhelpers.submit.create
で翻訳できるようになっているようです。
Q2. helpers.submit.create
で参照するロケールファイルはどこ設定でされているのか?
以下のファイルで設定されていました。
- gem:
rails
- ファイル:
actionview/lib/actionview.rb
# lib/action_view.rb ActiveSupport.on_load(:i18n) do I18n.load_path << File.expand_path("action_view/locale/en.yml", __dir__) end
rails/action_view.rb at main · rails/rails
参照するロケールファイルのactionview/lib/action_view/locale/en.yml
がload_path
に設定されています。load_path
についてはRailsガイドに詳細が記載されています。load_path
にデフォルトのロケールファイルであるactionview/lib/action_view/locale/en.yml
を設定することで結果的にen.helpers.submit.create
で翻訳できるようになっているのだと思います。
訳文読み込みパス (I18n.load_path) はファイルへのパスの配列であり、自動的に読み込まれます。このパスを設定することで、訳文のディレクトリ構造やファイル命名スキームをカスタマイズできます。
Q3. i18nのt
メソッドのロケールの切り替えはどこで設定されているのか?
以下のファイルでメソッドが定義されていました。
- gem:
i18n
- ファイル:
lib/i18n.rb
- メソッド:
translate
translate
メソッドのソースコード。
# lib/i18n.rb module I18n ... def translate(key = nil, throw: false, raise: false, locale: nil, **options) # TODO deprecate :raise locale ||= config.locale # ロケール設定 raise Disabled.new('t') if locale == false enforce_available_locales!(locale) backend = config.backend result = catch(:exception) do if key.is_a?(Array) key.map { |k| backend.translate(locale, k, options) } else backend.translate(locale, key, options) end end if result.is_a?(MissingTranslation) handle_exception((throw && :throw || raise && :raise), result, locale, key, options) else result end end alias :t :translate # tメソッドを定義(エイリアス) ... end
trancelate
メソッドのロケール設定で呼ばれるlocale
とdefault_locale
メソッドについては以下のファイルで定義されていました。
- gem:
i18n
- ファイル:
lib/i18n/config.rb
- メソッド:
locale
、default_locale
trancelate
メソッドのロケール設定で呼ばれるlocale
メソッド、その中で呼ばれるdefault_locale
のソースコード。
# lib/i18n/config.rb module I18n class Config ... def locale defined?(@locale) && @locale != nil ? @locale : default_locale end def locale=(locale) I18n.enforce_available_locales!(locale) # 渡されたロケールが使用不可で例外を発生させる @locale = locale && locale.to_sym end ... def default_locale @@default_locale ||= :en end ... end
i18n/i18n.rb at master · ruby-i18n/i18n
i18nのt
メソッドはtranslate
メソッドのエイリアスとして定義されており、メソッド内のconfig.locale
でロケール設定されています。
translate
メソッドのlocale
オプションがnil
の場合はデフォルトロケールのen
が設定され、そうでない場合は指定したロケールがシンボルで渡されてロケールが設定されるようです。
Q4. 翻訳情報の参照先のcreate
やupdate
の切替はどこで設定されているのか?
form_with
メソッド内のsubmit
はレコードの有無によって翻訳情報の参照先を自動的に選択される仕様になっています。
- レコード無し:
helpers.submit.create
を参照 - レコード有り:
helpers.submit.update
を参照
# app/views/books/_form.html.erb <%= form_with(model: book, local: true) do |form| %> ... <%= form.label :title %> <%= form.text_field :title %> ... <%= form.submit %> <% end %>
# config/locales/ja.yml ja: helpers: submit: create: 登録する # newメソッド時に参照される update: 更新する # editメソッド時に参照される
以下のファイルで翻訳情報の参照先を自動的に切り替えるsubmit_default_value
メソッドが定義されていました。
- gem:
rails
- ファイル:
lib/action_view/helpers/form_helper.rb
- メソッド:
submit_default_value
submit_default_value
メソッドのソースコード。
# module ActionView module Helpers ... def submit_default_value object = convert_to_model(@object) key = object ? (object.persisted? ? :update : :create) : :submit # レコードが存在する場合はupdate、存在しない場合はcreate model = if object.respond_to?(:model_name) object.model_name.human else @object_name.to_s.humanize end defaults = [] # Object is a model and it is not overwritten by as and scope option. if object.respond_to?(:model_name) && object_name.to_s == model.downcase defaults << :"helpers.submit.#{object.model_name.i18n_key}.#{key}" else defaults << :"helpers.submit.#{object_name}.#{key}" end defaults << :"helpers.submit.#{key}" defaults << "#{key.to_s.humanize} #{model}" I18n.t(defaults.shift, model: model, default: defaults) end ... end end
rails/form_helper.rb at main · rails/rails
persisted?
メソッドでレコードの有無を判断し:update
と:create
を切り替えてkey
に代入し、:"helpers.submit.#{key}"
で文字列展開して参照するキーを作成しているようです。
送信されているhttpメソッドで切り替えていると予想していましたが、レコードの有無で判断していると知ることができました。
まとめ
調べた結果を簡単にまとめると「rails
とi18n
の2つのgemがいい感じに翻訳情報の参照先を切り替えてsubmit
ボタンの多言語化ができるようになっている」ということになりました。
初めてgemのソースコードを読んでみて難しさと学びの多さを感じることができました。
Railsはとても複雑で全てを理解することは不可能ですがポイントを絞って読んでみることで理解が深まりそうです。
今後も気になる部分は積極的にソースコードを読んでみたいと思います。