content-authoring

Internationalization

Internationalization (I18n) is the process of designing a software application so that it can be adapted to various languages and regions without engineering changes.

When creating an application with for single target locale and language, implementing the application with internationalization in mind can serve as a valuable foundation for decoupling the copywriting process from the application development process.

Let’s create a Marketing page

Our application’s landing page will include copy about a hypothetical product launch.

This commit introduces all constituent parts, including a root route declaration, a MarketingsController, and a view template for that rendering the HTML response from that controller’s index action.

For the most part, that portion of this changeset’s code is standard and uninteresting.

Rails ❤️ [I18n](#i18n)

Out of the box, [Rails provides framework-level support][rails-18n] for declaring and translation internationalized text.

The Rails Internationalization Guides include a section that outlines the changes involved in rendering translated text into a response’s HTML through a controller’s flash helper.

Declaring and rendering our Translations

This commit’s changes take that example’s code a step further by rendering translated text directly into the marketings#index template via calls to the translate helper, and into our document’s <title> element in the layouts/applications layout via a combination of calls to the content_for helper (both with and without a block argument):

--- /dev/null
+++ b/app/views/marketings/index.html.erb
@@ -0,0 +1,5 @@
+<% content_for :document_title do %>
+  <%= translate(".hero.title") %>
+<% end %>
+
+<h1><%= translate(".hero.title") %></h1>

Since the calls to translate are made from within a controller action’s view template, both the controller name (marketings) and action name (index) portions of the key can be omitted.

Finally, to set the text Content Authoring as the value for the en.marketing.hero.title translation key, declare it in the config/locales/en.yml configuration file:

--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -30,4 +30,7 @@
 # available at https://guides.rubyonrails.org/i18n.html.

 en:
-  hello: "Hello world"
+  marketings:
+    index:
+      hero:
+        title: "Content Authoring"

Testing out our translations

In order to make sure that reading and writing our translations is working, this commit also includes a test for the MarketingsController#index action, along with two changes to Rails’ internationalization configuration:

--- a/config/environments/development.rb
+++ b/config/environments/development.rb
@@ -62,7 +62,7 @@ Rails.application.configure do
   config.assets.quiet = true

   # Raises error for missing translations.
-  # config.i18n.raise_on_missing_translations = true
+  config.i18n.raise_on_missing_translations = true

   # Annotate rendered view with file names
   # config.action_view.annotate_rendered_view_with_filenames = true
diff --git a/config/environments/test.rb b/config/environments/test.rb
index b5edb26..0f7d25d 100644
--- a/config/environments/test.rb
+++ b/config/environments/test.rb
@@ -53,7 +53,7 @@ Rails.application.configure do
   config.active_support.disallowed_deprecation_warnings = []

   # Raises error for missing translations.
-  # config.i18n.raise_on_missing_translations = true
+  config.i18n.raise_on_missing_translations = true

   # Annotate rendered view with file names
   # config.action_view.annotate_rendered_view_with_filenames = true

Without these configuration changes, tests that cover controllers that render incorrectly declared translations fail with confusing messages:

Failure:
  test/controllers/marketings_controller_test.rb|9| MarketingsControllerTest#test_#index_renders_marketing_copy
  <<span class="translation_missing" title="translation missing: en.marketings.index.title">Title</span>> expected but was <Title>..
  Expected 0 to be >= 1.

The <span class="translation_missing" title="translation missing: en.marketings.index.title">Title</span> part of the exception is surprising: we didn’t write that code to render that <span> element, so what did?

By default, when Rails is running in a development or test environment mode, it is configured to render <span> elements like this when it fails to translate an internationalization key:

Rails adds a t (translate) helper method to your views so that you do not need to spell out I18n.t all the time. Additionally this helper will catch missing translations and wrap the resulting error message into a <span class="translation_missing">.

Section 3.1 of Rails I18n Guides

In an effort to make our test failures communicate more helpful information, we configure Rails to raise an exception instead.

After making this configuration change and re-running our tests, the failure message is much more helpful:

Error:
  MarketingsControllerTest#test_#index_renders_marketing_copy:
  ActionView::Template::Error: translation missing: en.marketings.index.title

Our application so far

At this point, we have a functional application that renders text from an internationalization-ready, configurable set of translations!

Visually, the application lacks… an aesthetic identity.

Desktop

marketings#index in desktop dimensions

Mobile

marketings#index in mobile dimensions

Let’s change that!