Railsのi18nで予約語をキーに指定しても例外が発生しない不具合を改善した
はじめに
現在、FBCで学習中の身です。
RailsのI18n実装中に表題の不具合を発見し、issueを起票し修正されたのでその流れを記したいと思います。
問題
I18nを用いて、訳文に変数を渡そうとしていたのですが、、
なぜか変数名がそのまま返ってきてしまいました。
いろいろ試してみる
まず、consoleで確認。
#config/locales/ja.yml
controllers:
default:
update: "%{object}の更新に成功しました。"
irb(main):001:0> I18n.t('controllers.default.update', object: Book.model_name.human)
=> "%{object}の更新に成功しました。"
「本の更新に成功しました。」と出力されてほしいのに、なぜか「%{object}の更新に成功しました。」とそのままの文字列ができてしまっています。
ただ、モデル名の翻訳はできてる。
irb(main):003:0> Book.model_name.human
=> "本"
普通に文字列を渡してもダメ。
irb(main):002:0> I18n.t('controllers.default.update', object: '本')
=> "%{object}の更新に成功しました。"
Rails 国際化(I18n)API - Railsガイド (railsguides.jp)を見てもI18n::ReservedInterpolationKey例外が発生します。
って書いてるし、、
defaultキーワードおよびscopeキーワードは予約されているため、変数には使えません。利用するとI18n::ReservedInterpolationKey例外が発生します。 ある訳文で式展開変数が期待されているにもかかわらず、#translateに値が渡されない場合は、I18n::MissingInterpolationArgument例外が発生します。
お手上げ。
どうしたらいいんだー!
メンターの方から Let's go Q&A!とのことで、早速質問しました。
そしたらすぐに回答が!👀
どうやら object
は予約語で他にもあるらしい。
# https://github.com/ruby-i18n/i18n/blob/v1.14.1/lib/i18n.rb#L19-L34
RESERVED_KEYS = %i[
cascade
deep_interpolation
default
exception_handler
fallback
fallback_in_progress
fallback_original_locale
format
object
raise
resolve
scope
separator
throw
]
「予約語はたくさんあるし、I18n::ReservedInterpolationKey例外も起きないし。
これはもしかするとコントリビューションチャーンス!?」
とお言葉をいただき、折角なのでissue立ててみた。
立てたissueは以下。
Inquiry About Reserved Keywords When Passing Variables to Translations · Issue #49879 · rails/rails
あっという間に修正。
回答が来ました。以下ざっくり。
scope
キーを使わないとエラーは出ないよ~。じゃないと値はそのまま返されるよ。
I18n.t('index', scope:'books.title', object:Book.model_namu.human)
のようにね。
でもバグかもしれないから一旦確認するね~。
ちなみに
この記事執筆時点で、scope
キーを使って試してみましたがエラーは出ませんでした。。
修正版のgemでは、ちゃんとエラーになりました。
irb(main):003:0> I18n.t('update', scope:'controllers.default', object:Book.model_name.human)
=> "%{object}の更新に成功しました。"
後日見ると、やはりエラーを出した方がいいということで修正されてました。
今回された修正
予約語を使用した時、エラーを吐くように修正!(かなりざっくり)
scopeが空の場合にも予約語が使用されていないかチェックしてくれるようになった。
if values && !values.empty?
entry = if deep_interpolation
deep_interpolate(locale, entry, values)
else
interpolate(locale, entry, values)
end
elsif entry.is_a?(String) && entry =~ I18n.reserved_keys_pattern
raise ReservedInterpolationKey.new($1.to_sym, entry)
end
entry
未リリースのgemで試した→ちゃんとエラーになった!
未リリースのgemを使用するようにGemfileを編集します。
#Gemfile
gem 'i18n', github: 'ruby-i18n/i18n'
そして、bundle update
します。
rails consoleで確かめたところ、ちゃんとエラーを吐くようになっていました!!🎉
Loading development environment (Rails 7.0.8)
irb(main):001> I18n.t('controllers.default.update', object:Book.model_name.human)
/Users/xxxxxxx/.rbenv/versions/3.2.2/lib/ruby/gems/3.2.0/bundler/gems/i18n-baf8a889391c/lib/i18n/backend/base.rb:65:in `translate': reserved key :object used in "%{object}の更新に成功しました。" (I18n::ReservedInterpolationKey)
raise ReservedInterpolationKey.new($1.to_sym, entry)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
irb(main):002> I18n.t('update', scope:'controllers.default', object:Book.model_name.human)
/Users/xxxxxxx/.rbenv/versions/3.2.2/lib/ruby/gems/3.2.0/bundler/gems/i18n-baf8a889391c/lib/i18n/backend/base.rb:65:in `translate': reserved key :object used in "%{object}の更新に成功しました。" (I18n::ReservedInterpolationKey)
raise ReservedInterpolationKey.new($1.to_sym, entry)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
未リリースのgem試行にあたり、以下の記事を参考にしました。
Rails初心者に送る「Bundlerチョットワカルマニュアル」 #Ruby - Qiita
さいごに
この記事ではi18nの不具合修正の過程を通して、「質問することは悪いことじゃない!」ということをお伝えしたかったです。
記事全体でみると、自分はバグを発見しただけでほとんど何もしていません。。
ですが、質問というアクションを起こしてよかったと思います。
「あー、なんか他の単語なら使えたからほっとこ」となっていたら、この現象は次の誰かが見つけるまで残っていたことになります。それはよくありません。
今からRailsを学習する方達が同じシチュエーションに遭遇したとき、「え、どういうこと?!もういやや😭」となるのを防げたと考えたら嬉しいです ☺️
Discussion