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>.
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)