TL;DR
rails-i18n は、キー名に _html
を付ければ安全に HTML を出力してくれる。 raw
や html_safe
を使ってはいけない。
I18n の翻訳に HTML を突っ込みたい!
Rails で構築された多言語対応のサイトで、 I18n を使っているときに、翻訳メッセージに HTML を含みたくなることがあります。
例えばこんな感じ。
ja:
incoming_message: '<strong>%{username}</strong>さんからメッセージがあります!'
これを表示するときは、 View のテンプレートで、
<%= t 'incoming_message', username: @username %>
みたいなのを書くわけですが、このままだと html_safe
でないので自動的にエスケープされてしまいます。
<strong>tnj</strong>さんからメッセージがあります!
悲しい!
正しい HTML の含み方
この問題に直面した Rails I18n 初級者は、「じゃーエスケープ避けてそのまま出力させるしかなくない?」と、 raw
だったり html_safe
を付けてしまいがちです。
でもそうなると、もし @username
にタグが含まれていた場合もエスケープされずに出力されてしまうので、簡単に XSS を引き起こしてしまいます。「ここはユーザー入力値は入らないから大丈夫だよ!」は絶対守り切れないし、変数を必要とするたびに「本当にユーザー入力値入ってこないんだっけ?」なんて判断をいちいち必要としていては時間がいくらあっても足りないので、機械に任せたいところです。
で、 Rails がそんな基本的な部分をカバーしていないはずがない!と思って落ち着いて調べてみたら、やっぱりちゃんと書いてありました。
Keys with a '_html' suffix and keys named 'html' are marked as HTML safe. When you use them in views the HTML will not be escaped.
というわけで、 _html
で終わる、もしくは html
というキー名は自動的に HTML として出力されます。 中に変数がある場合も、きちんとそこだけエスケープ対象にしてくれます。ドキュメントはちゃんと読まないとあかんですね。
例
今回の場合だと、キーの名前を incoming_message_html
に変えてあげれば解決です。
incoming_message_html: '<strong>%{username}</strong>さんからメッセージがあります!'
<%= t 'incoming_message_html', username: @username %>
としてやるだけで、期待した結果が得られます。
tnjさんからメッセージがあります!
もし、 @username
に HTML タグが含まれていたとしても、こちらは自動的にエスケープされるので問題ありません。
<script>alert(1);</script>さんからメッセージがあります!
安心して眠れますね!
結論
Rails の I18n は、キーの命名次第でちゃんと HTML も出力できるから、 raw
なんて使わなくても大丈夫だよ!