Read more

Rails: Including HTML in your i18n locales

Dominik Schöler
May 02, 2018Software engineer at makandra GmbH

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;.
Illustration online protection

Rails Long Term Support

Rails LTS provides security patches for old versions of Ruby on Rails (2.3, 3.2, 4.2 and 5.2)

  • Prevents you from data breaches and liability risks
  • Upgrade at your own pace
  • Works with modern Rubies
Read more Show archive.org snapshot

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
May 02, 2018Software engineer at makandra GmbH
Posted by Dominik Schöler to makandra dev (2018-05-02 12:17)