views:

790

answers:

1

As you probably know, starting from Rails 2.2, Rails is shipped with a simple localization and internationalization backend.

By default, you can store the strings you need to translate in the localization files within the config folder.

config/locales/en.yml
config/locales/it.yml

But Rails provides the ability to localize templates and partials as well. For example, the MainController#index action can select a localized template according to the template filename and current locale settings.

apps/views/main/index.it.html.erb
apps/views/main/index.en.html.erb

The first feature is useful when you need to translate single strings or short paragraphs. The latter is a good choice when the same action renders in different ways according to current locale value.

But how do you deal with fair simple templates that share the same business logic but contains a large amount of text? Take for example the following template

<% javascript_content_for :head do %>
$(function() {
  $("#choices :radio").change(function() {
    $(".choice-wizard").hide();
    $("#" + $(this).val()).show();
  });
});
<% end %>

<h1><%= title t(".title") %></h1>

<div class="widget">
  <div class="entry form">

    <h2><%= title t(".header_choices") %></h1>

    <% form_tag "#", :id => "choices" do %>
      <p>
        <%= radio_button_tag :choice, "with" %>
        <%= label_tag "choice_with", "..." %>
      </p>
      <p>
        <%= radio_button_tag :choice, "without" %>
        <%= label_tag "choice_without", "..." %>
      </p>
    <% end %>

    <div id="with" class="choice-wizard" style="display: none;">

      <!-- to be localized -->
      <h3>....</h3>
      <p>a long paragraph</p>
      <p>a long paragraph</p>

      <p class="textcentered">
        <%= link_to "Continue", new_path, :class => "button" %>
      </p>
      <!-- / to be localized -->

    </div>

    <div id="without" class="choice-wizard" style="display: none;">

      <!-- to be localized -->
      <h3>....</h3>
      <p>a long paragraph</p>
      <p>a long paragraph</p>

      <p class="textcentered">
        <%= link_to "Continue", new_path, :class => "button" %>
      </p>
      <!-- / to be localized -->

    </div>

  </div>
</div>

<% sidebar do %>
  <%= render :partial => "sidebar/user" %>
<% end %>

Here I have a form, a JavaScript content and a small amount of text. I need to translate the text but:

  1. the text is too long for creating a simple string in the .yml file and I don't want to end up creating O(n) strings, one for each paragraph
  2. the template contains some "features" and I don't want to create 5 template, one for each language, because it will make the app harder to maintain.

How would you organize the code?

A: 

In Rails (at least version 2.3.4), partials respect the same internationalization settings that the views and templates do, so what you could do is put your large bodies of text into partials that are translated, while keeping your features in the original view. For the labels and 'smaller' text, using the t(...) translation method could be used as you suggested. So, to run with your concrete example:

# app/wizards/edit.html.erb
<% javascript_content_for :head do %>
$(function() {
  $("#choices :radio").change(function() {
    $(".choice-wizard").hide();
    $("#" + $(this).val()).show();
  });
});
<% end %>

<h1><%= title t(".title") %></h1>
<div class="widget">
  <div class="entry form">
    <h2><%= title t(".header_choices") %></h1>
    <% form_tag "#", :id => "choices" do %>
      <p>
        <%= radio_button_tag :choice, "with" %>
        <%= label_tag "choice_with", "..." %>
      </p>
      <p>
        <%= radio_button_tag :choice, "without" %>
        <%= label_tag "choice_without", "..." %>
      </p>
    <% end %>
    <div id="with" class="choice-wizard" style="display: none;">
      <!-- to be localized -->
      <%= render :partial => 'dear_readers' %>
...

# app/views/wizards/_dear_readers.en.html.erb
<h3>A Title</h3>
...

# app/views/wizards/_dear_readers.sv.html.erb
<h3>Bork bork bork!</h3>
...

And so forth. My apologies to Sweden.

Another possibility to go with my comment below:

# app/views/wizards/edit.html.erb
<%= render :partial => 'dear_readers' %>
<% javascript_content_for :head do %>
$(function() {
  $("#choices :radio").change(function() {
    $(".choice-wizard").hide();
    $("#" + $(this).val()).show();
  });
});
<% end %>

<h1><%= title t(".title") %></h1>
<div class="widget">
  <div class="entry form">
    <h2><%= title t(".header_choices") %></h1>
    <% form_tag "#", :id => "choices" do %>
      <p>
        <%= radio_button_tag :choice, "with" %>
        <%= label_tag "choice_with", "..." %>
      </p>
      <p>
        <%= radio_button_tag :choice, "without" %>
        <%= label_tag "choice_without", "..." %>
      </p>
    <% end %>
    <div id="with" class="choice-wizard" style="display: none;">
      <!-- to be localized -->
      <%= yield :paragraph_1 %>
      <%= yield :paragraph_2 %>
      ...

# app/wizards/_dear_readers.en.html.erb
<% content_for :paragraph_1 %>
  <h3>Title ...</h3>
  <p>Content ... </p>
<% end %>
<% content_for :paragraph_2 %>
  ...
<% end %>
...

And so on for each language you support. As I mentioned in the comment that inspired this, the approach outlined here feels like we're shoe-horning a solution for one problem, DRYing up shared markup (in the form of site navigation, sidebars, etc.), into a solution for another problem, large bodies of translated text. It seems a bit unconventional to use content_for / yield in this way, but it may be an acceptable solution to your problem.

Ian
I'm using localized partials quite often. The downside of this approach is that, for this action only, I'll end up with ((1 view) + (5 * 2 partials = 10)) = 11 files. :S
Simone Carletti
I can see the problem with that, but I'm not coming up with a solution that doesn't result in X*L pieces of data (X being the number of translated components, L being the number of language translations offered.) You might be able to use the `yield :named_content` method in conjunction with L partials, each composed of X `content_for` blocks, but this feels like we're shoe-horning one concern into a solution for another.
Ian