Rails escapes string inside label tag

988 Views Asked by At

I have a label tag, whose content is loaded from a en.yml file.

html.erb

<%=label_tag(:name, t(:name, scope:[:helpers, :form], name: person_name(person))).html_safe%>

person_name is a helper and outputs a string

persons_helper.rb

def person_name(person)
    content_tag(:span,
                formatted_name(person.name) || t("helpers.persons.default_name"),
                class: 'name').html_safe
end

output string from the helper is passed on t method and concatenated as following

en.yml

  name: "Person Name: (%{name})"

I want the output to be like

<label for="person">
  Person Name:
  <span class='name> John Doe </span>
</label>

but Instead I get

<label for="person">
  Person Name:(&lt;span class="name"&gt;John Doe&lt;/span&gt;)
</label>

I understand that it got to do with html_safe, raw and escaping strings but I just could not get it to work!

Thanks!

3

There are 3 best solutions below

0
On

It appears that the I18n.t method does not return a SafeBuffer (i.e. an html_safe string). So you should call .html_safe on the output from this method.

<%= label_tag(:name, t(:name, scope:[:helpers, :form], name: person_name(person)).html_safe) %>

Note the .html_safe call has been moved in one parenthesis from where you had it. This can also be made marginally easier to see by using the block form of the label_tag helper.

<%= label_tag(:name) { t("helpers.form.name", name: person_name(person)).html_safe } %>

Note: I also switched to the "helpers.form.name" method of selecting the I18n translation in this example to further increase readability (but this may be just a personal preference -- so use your original style if you prefer!).

Finally, for security purposes -- so that a user's name doesn't come through unescaped -- you should remove the .html_safe from your person_name helper and add a strict html_escape (or sanitize) so that it looks like this:

def person_name(person)
  content_tag(:span,
    h(formatted_name(person.name)) || t("helpers.persons.default_name"),
    class: 'name')
end

In this form, the content_tag will make sure everything is html_safe except for the content. Meaning that the person.name will come through as is and be escaped as needed. However, this is not needed if the formatted_name method returns an already escaped or html_safe name. Basically the point is that you don't want to blindly mark strings as html_safe when they come from user inputted values because you don't know if they contain script tags or what. Hopefully this didn't confuse. :) In general, only mark strings as html_safe when you are 100% sure that they are actually always going to be safe (i.e. they come from within your system and not from user input of any sort).

0
On

Call .html_safe on the method call inside the label_tag. E.g:

<%=label_tag(:name, t(:name, scope:[:helpers, :form], name: person_name(person).html_safe))%>
0
On

Rails translations can be automatically marked as html_safe but using a naming convention. If a translation is suffixed with _html then the string is marked as html_safe, likewise keys named html are also marked as html_safe.

# config/locales/en.yml
en:
  welcome: <b>welcome!</b>
  hello_html: <b>hello!</b>
  title:
    html: <b>title!</b>

In the above t('hello_html') and t('title.html') will be html_safe strings and will not require a call to raw or .html_safe where as t('welcome') will not be html_safe and will require calling raw or .html_safe to avoid the html in the string being escaped.

See http://guides.rubyonrails.org/i18n.html#using-safe-html-translations