Rails views, internationalization, special characters, and testing with Rspec

The problem

File this under small problems that take more time than they should to solve, and I couldn’t find an answer with a web search.

Let’s use a simple example. If you have text like this in your translation file (e.g. en.yml):

users:
  new:
    header: "Let's go!"

And then show it in a view template (e.g. app/views/users/new.html.erb):

<h3><%= t('.header') %></h3>

And then try to match it in an Rspec test, you’ll get an error that it couldn’t be found:

expect(response.body).to include(I18n.t('users.new.header'))

Failure/Error: expected "[...]Let&#39;s go![...]" to include "Let's go!"

This is because in the response.body the single quote character is rendered in the HTML as an html entity (aka a character reference). This is Rails sanitizing the text to prevent injection attacks. That Let&#39;s go! you see above appears as Let's go! in the browser.

Options for solutions

One thing you can’t do is just put Let&#39;s go! in your translation file, as it will be rendered as Let&amp;#39;s go! because Rails is now escaping the html entity itself.

So what can you do? You could:

  1. Declare the text in the translation is html safe – then Rails won’t sanitize it. You can do this by adding _html to the key:

    users:
      new:
        header_html: "Let's go!"
    
  2. Or, call CGI.escapeHTML which will also convert the quote character in the test, so it matches what’s rendered in the view:

    expect(response.body).to include(CGI.escapeHTML(I18n.t('users.liveness_checks.new.header')))

Neither solution is ideal – if the translation content changes at some point in the future you could easily overlook this kind of one-off escaping and leave it in place even though it’s not appropriate anymore. This would make it a likely point of confusion for someone inheriting your code. But I recommend option 2, as you’re dealing with the issue in the test rather than in the actual view rendering, so you’re not introducing any possible future risk in the production environment.

A note on the Faker gem: if you’re using it to generate random names, you will get random failures for these kinds of tests, when Faker gives you a name like O'Hara

Leave a Reply