The Last Rails I18n Guide You’ll Ever Need

The Last Rails I18n Guide You'll Ever Need

In this article we will discuss how to add support for I18n in a Rails application, where to store translations, what localized views are, how to format dates and times, how to introduce pluralization rules and more.

Internationalization (dubbed as I18n as there are exactly eighteen characters between the first “I” and the last “n”) means creating an application that can be adapted to various languages easily, without the need to do complex changes. This involves extracting various bits (like strings, date and currency formats) out of a (Rails) application and then providing translations and formats for them. The latter is called localization and sometimes is dubbed as L10n. If your company is growing and seeking to go international, localization is an important step to do.

In this article we will discuss how to add support for I18n in a Rails application, where to store translations, what localized views are, how to format dates and times, how to introduce pluralization rules and more. By the end of this article you will have a solid understanding of using I18n with Rails and be ready to employ the described techniques when building the real-world apps.

The source code for the demo app is available at GitHub.

Our First Translation with Rails

I18n was the Rails’ core feature starting from version 2.2. It provides a powerful and easy to use framework that allows to translate an app into as many languages as you need. To see it in action while discussing various concepts, let’s create a demo Rails application:

For this article I am using Rails but most of the described concepts apply to Rails 3 and 4 as well.

Go ahead and create a static pages controller with a single action:



Set up the root route:


So we have the header on the main page that contains the hard-coded “Welcome!” word. If we are going to add support for multiple languages this is not really convenient – this word has to be extracted somewhere and replaced with a more generic construct.

By default all translations should be placed inside the config/locales directory, divided into files. They are being loaded automatically as this directory is set as I18n.load_path by default. You may add more paths to this setting if you wish to structure your translations differently. For example, to load all the YAML and Ruby files from the locales directory and all nested directories, say

inside your config/application.rb file.

By default there is already an en.yml file inside the locales directory present, so let’s change its contents to


yml stays for YAML (Yet Another Markup Language) and it is a very simple format of storing and structuring data. The top-mosten key means that inside this file we are storing English translations. Nested is the welcome key that has a value of “Welcome!”. This string is an actual translation that can be referenced from the application. Here is the nice guide to naming your keys.

The core method to lookup translations is called translate or simply t:


So now instead of hard-coding an English word, we tell Rails where to fetch the its translation. welcome corresponds to the key introduced inside the en.yml file. English if the default language for Rails applications so when reloading the page you’ll see the same “Welcome!” word. Nice!

Adding Support for an Additional Language in Rails

Surely you are eager to check how this all is going to work with the support for multiple languages. In order to do that we need a way to provide the language’s name to use. There are multiple options available:

  • Provide language’s name as a GET parameter (
  • Provide it as a part of a domain name (
  • Provide it as a part of a URL ( Technically, that’s a GET parameter as well.
  • Set it based on the user agent sent by the browser
  • Set it based on the user’s location (not really recommended)

To keep things simple we will stick with the first solution. Introduce a new before_action inside theApplicationController:


The idea is simple: we either fetch a GET parameter called locale and assign it to the I18n.locale option or fetch the default locale which, as you remember, is currently set to en.

Now try navigating to http://localhost:3000?locale=de and… you’ll get an InvalidLocale error. Why is that? Add the following contents to the index page


and reload the page (while stripping out the ?locale part). You’ll note that only [:en] is being rendered meaning that we do not have any other locales available at all. To fix that, add a new gem into the Gemfile:


rails-i18n provides locale data for Ruby and Rails. It stores basic translations like months’ and years’ names, validation messages, pluralization rules and many other ready to use stuff. Here is the list of supported languages.


boot the server and reload the page once again: now you’ll see a huge array of supported languages. That’s great, but most likely you won’t need them all, therefore let’s redefine the available_locales setting:


Now we support English and Russian languages. Also, while we are here, let’s set a new default locale for our app for demonstration purposes:


Don’t forget to reload the server after modifying this file!

The code for the before_action should be modified:


As long as we’ve used symbols when defining available locales, we should convert the GET parameter to a symbol as well. Next we check whether this locale is supported and either set it or use the default one.

Now when you switch to a Russian language (or any other language you added support for, except for English), you’ll note that the header contains the “Welcome” word, but without the “!” sign. Use a tool like Firebug and inspect the header’s markup:

What happens is Rails cannot find translation for the welcome key when switching to Russian locale, so it simply converts this key to a title and displays it on the page. You may provide a :default option to the t method in order to say what to display if the translation cannot be found:

Let’s create a new translations file for the Russian locale:


Now everything should be working just great, however be sure not to fall for some common mistakes developers usually do while localizing an app.

Also note that the t method accepts a :locale option to say which locale to use:

Using Scopes

Having all translations residing on the same level of nesting is not very convenient when you have many pages in your app:

As you see, those translations are messed up and not structured in any way. Instead we can group them using scopes:



So now the welcome key is scoped under the pages.index namespace. To reference it you may use one of these constructs:

What’s even better, when the scope is named after the controller (pages) and the method (index), we can safely omit it! Therefore this line will work as well

when placed inside the pages/index.html.erb view or inside the index action of the PagesController. This technique is called “lazy lookup” and can save you from a lot of typing. Having this knowledge, let’s modify the view once again:


Localized Views

If your views contain too much static text, you may introduce the so-called localized views instead. Suppose, we need to create an “About Us” page. Add a new route


And then create two views with locale’s title being a part of the file name:



Rails will automatically pick the proper view based on the set locale.

HTML Translations

I18n in Rails support HTML translations as well, however there is a small gotcha. Let’s display some translated text on the main page and make it semibold:




This, however, will make the text appear as is, meaning that the HTML markup will be displayed as a plain text. To make the text semibold, you may say

or add an _html suffix to the key:

Don’t forget to modify the view’s code

Another option would be to nest the html key like this:

and then say

Translations for ActiveRecord

So now suppose we wish to manage blog posts using our application. Create a scaffold and apply the corresponding migration:

When using Rails 3 or 4, the latter command should be

Now add the link to create a new post:





Boot the server and click this new link. You’ll see a form to create a new post, however the problem is that it’s not being translated: the labels are in English and the button says “Создать Post”. The interesting thing here is that the word “Создать” (meaning “Create”) was taken from the rails-i18n gem that, as you remember, stores translations for some common words. Still, Rails has no idea how to translate model’s attributes and its title.

To achieve that, we have to introduce a special scope:



So the models’ names are scoped under the activerecord.models namespace, whereas attributes’ names reside underactiverecord.attributes.SINGULAR_MODEL_NAME.

The label helper method is clever enough to translate the attribute’s title automatically therefore this line of code inside the_form.html.erb partial does not require any changes:

Now provide some basic validation rules for the model:


Next try to submit an empty form and note that even the error messages are localized properly thanks to rails-i18n gem! The only part of the page left untranslated in the “New Post” title and the “Back” link – I’ll leave them for you to take care of.

Date and Time

Now let’s discuss how to localize date and time in Rails. Before moving on, create a post either using a form or by employingseeds.rb file. Also add a new link to the root page


Then translate it



and tweak the posts index view by introducing a new column called “Created at”:


We are not going to translate all the columns and titles on this page – let’s focus only on the post’s creation date. Currently it looks like “2016-08-24 14:37:26 UTC” which is not very user-friendly. To localize a date or time utilize the localize method (aliased as l).

The result will be “Ср, 24 авг. 2016, 14:37:26 +0000” – that’s the default (long) time format, but rails-i18n gem provides some additional ones that can be used out of the box – you can see their masks here (for the dates) and here (for the times). If you have used Ruby’s strftime method before, you’ll notice that those format directives (%Y, %m and others) are absolutely the same.

In order to employ one of the predefined formatting rules, say

If you are not satisfied with the formats available by default, you may introduce a new one for every language:

Now this format can be used inside the view:

Of course, if for some reason you don’t want to define a new format, the mask may be passed directly to the :format option:

Just like the t method, l also accepts the :locale option:

The last thing to notice here is that rails-i18n also has translations for the distance_of_time_in_words,distance_of_time_in_words_to_now and time_ago_in_words methods, so you may employ them as well:

Pluralization Rules and Variables

Different languages have, of course, absolutely different pluralization rules. English words are pluralized by adding an “s” flection (except for special some cases), but in Russian, for example, pluralization rules are much complex, therefore it is your job to add them.

Suppose we want to display how many posts were written for the blog. Create a new scope and add pluralization rules for the English locale:


The %{count} is the variable part – we will pass a value for it when using the t method.

As for the Russian locale, things are a bit more complex:


Rails automatically determines which key to use based on the provided number.

Now tweak the view:


Note that there is also an inflections.rb initializer file that can be used to store inflection rules for various cases.

Lastly, you may provide any other variables to your translations utilizing the same approach described above:

Then just say

PhraseApp Makes Your Life Easier!

Keeping track of translation keys as your app grows can be quite tedious, especially if you support many languages. After adding some translation you have to make sure that it does present for all languages. PhraseApp is here to help you!

  • Create a new account (a 14 days trial is available) if you don’t have one, fill in your details, and create a new project (I’ve named it “I18n demo”)
  • Navigate to the Dashboard – here you may observe summary information about the project
  • Open Locales tab and click Upload File button
  • Choose one of two locale files (en.yml or ru.yml). The first uploaded locale will be marked as the default one, but that can be changed later
  • Select Ruby/Rails YAML from the Format dropdown
  • Select UTF-8 for the Encoding
  • You may also add some Tags for convenience
  • Upload another translations file

Now inside the Locales tab you’ll see two languages, both having a green line: it means that these locales are 100% translated. Here you may also download them, edit their settings and delete.

Next suppose we want to add support for German language and track which keys need to be translated.

  • Click Add locale button
  • Enter a name for the locale (“de”, for example)
  • Select “German – de” from the Locale dropdown
  • Click Save

Now the new locale is present one the page. Note there is a small message saying “9 untranslated” meaning that you will have keys without the corresponding translation. Click on that message and you’ll see all the keys we’ve added while building the demo app. Now simply click on these keys, add translation for them and click Save (this button may be changed to Click & Next). Note that there is even a History tab available saying who, when and how changed translation for this key.

When you are done return to the Locales tab and click Download button next to the German locale. You’ll get a YAML file that only needs to be copied into the locales directory – the translation is done!

Localization is not only about translation and you may be not that familiar with a language you plan to support. But that’s not a problem – you can ask professionals to help you! Select Order Translations from the dropdown next to the locale, choose provider, provide details about your request and click Calculate price. Submit request and your translation will be ready soon! You can read more about professional translations and average prices.


So, in this article we’ve discussed internationalization and localization in Rails. We’ve set up basic translations, introduced localized views, translated ActiveRecord attributes and models, localized date and time, and also provided some pluralization rules. Hopefully, now you are feeling more confident about using I18n with Rails!

For additional information you may refer to this official Rails guide and read up some info on rails-i18n GitHub page. Storing translations inside the YAML files is not the only approach in Rails, so you may be interested in a solution called Globalize – with the help of it you may store translations in the database.

Also published on Medium.