Rails: Including HTML in your i18n locales

Updated . Posted . Visible to the public. Repeats.

TL;DR

Append your locale keys with _html to have them marked as html_safe and translate them with = t('.text_html').

When you're localizing a Rails application, sometimes there is this urge to include a little HTML. Be it some localized link, or a set of <em> tags, you'd like to have it included in the locale file. Example:

# Locale file
en:
  page:
    text: 'Please visit our <a href="https://www.corporate.com/en">corporate website</a> to learn more about <em>the corporation</em>.'
    
# HAML
= t('.text')
    
# Desired output
Please visit our <a href="https://www.corporate.com/en">corporate website</a> to learn more about <em>the corporation</em>.

# Actually rendered as ...
Please visit our <a href="www.corporate.com/en">corporate website</a> to learn more about <em>the corporation</em>.

# ... which looks like this in the HTML source:
Please visit our &lt;a href=&quot;https://www.corporate.com/en&quot;&gt;corporate website&lt;/a&gt; to learn more about &lt;em&gt;the corporation&lt;/em&gt;.

Alright. Rails is being helpful here and saves you from accidentally injecting HTML into the page. But how could you insert the desired HTML intentionally?
While blindly calling t(...).html_safe renders the text as desired, it removes the protection and leaves you vulnerable to rendering bad HTML

A workaround would be to split the translations around the HTML fragments.

= t('.please')
= link_to t('.website'), t('.corporate_url')
= t('.learn_more')
= succeed '.' do
  %em
    = t('.corporation')

This is annoying, especially in HAML where you need the #succeed helper to end the line with a dot.

The solution

Fortunately, they've built something into Rails. Namely: The t() helper (but not I18n.t()) will mark translations as .html_safe if their key ends with _html:

# locale file
en:
  page:
    text_html: 'Please visit our <a href="https://www.corporate.com/en">corporate website</a> to learn more about <em>the corporation</em>.'
    
# HAML
= t('.text_html')
    
# Output
Please visit our <a href="https://www.corporate.com/en">corporate website</a> to learn more about <em>the corporation</em>.

This will also properly escape unsafe interpolations:

# locale file
en:
  page:
    text_html: 'Please visit our %{link} to learn more about <em>the corporation</em>.'
    
# HAML
= t('.text_html', link: '<script>alert("hacked")</script>')
    
# Output
Please visit our &lt;script&gt;alert(&quot;hacked&quot;)&lt;/script&gt; to learn more about <em>the corporation</em>.
Profile picture of Dominik Schöler
Dominik Schöler
Last edit
Felix Eschey
License
Source code in this card is licensed under the MIT License.
Posted by Dominik Schöler to makandra dev (2018-05-02 10:17)