Rails i18n supports lazy lookup so that if you have a locales file shaped like this:
en:
newsletter_cta:
title: "Stay Up To Date With The Latest Happenings"
You can reference the value in newsletter_cta.title
via t('.title')
when in app/views/_newsletter_cta.html.erb
In many Rails applications, I extract partials to encapsulate components so any HTML/CSS oriented team member doesn't have to learn how to write helper methods when writing components; and so we don't wind up with copy-pasted code that would necessitate massive pain when upgrading or shifting the underlying UI framework.
Here's an example of a bootstrap section:
# app/views/application/_section.html.erb
<section class="section-wrap">
<div class="container">
<%= yield %>
</div>
</section>
Which I can then use in other views, like this:
# app/views/_newsletter_cta.html.erb
<%= render "section" do %>
<h2><%= t('newsletter_cta.title') %></h2>
<%= render "cta-button", text: t('newsletter_cta.title') %>
<% end %>
This works fine, so long as don't rely on lazy lookup and I hardcode the full lookup string for the translation.
However, as soon as I use lazy lookups the translation is not found.
# app/views/_newsletter_cta.html.erb
<%= render "section" do %>
<h2><%= t('.title') %></h2>
<%= render "cta-button", text: t('.title') %>
<% end %>
Tracing through to the translate method in ActionView::Helpers::TranslationHelper we see that it relies on a method named scope_key_by_partial
which in turn, relies on an instance variable @virtual_path
.
Do any of y'all know of a way to preserve the virtual path while using partials with blocks? Is there a gem somewhere that changes this behavior? I'm a bit surprised no one else has stumbled into this.
Top comments (4)
I've also been running into this issue. But because I use the i18n-tasks gem to keep my localisation file in check I've chosen to write out the absolute path for a translation when using them within render blocks.
Basically what happens is as soon as you put a relative translation in a render block the path gets changed. In your example it would be: "application.section.title".
One way to approach this could be to pass the i18n scope of the view to the partial and use that when translating a key.
Something like:
Note the removed dot before "title" indicating this is not a relative lookup.
I think landing on "Use absolute paths at all times with I18n" is great advice, especially since the lookup inference pattern is pretty unique to Rails.
When you call
render "section"
what it seems actually happens is it treats the partial as a template, this changes the scope of execution and therefore changes where those translations are executed.Instead you can explicitly render it as a partial by passing it as
render partial: "section"
. However, partials don't allow you to pass blocks to them, instead you'll need to capture the content into a variable and pass it as a local:Putting these in a helper tidies things up in the views:
Then in your view you can do:
In case somebody is looking for a workaround, here it is: