Internationalization (I18n) API in Ruby on Rails 3 (Part II)

In the Internationalization (I18n) API in Rails 3 (Part I) you read about

  1. Definition of Internationalization & Localization
  2. How i18n mechanism works in Rails
  3. Setup/Configuration about the i18n mechanism
  4. Using the I18n with interpolation
  5. Using the I18n with pluralization

You can read the full article on Internationalization (I18n) API in Rails 3 (Part I), if you have not yet read it.

Now, let’s start with some other features of I18n in Ruby on Rails.

Localization of Times/Dates
As I18n API provides date/time localization feature, we can easily add localized version of timestamp in Ruby on Rails application. To use this feature, first of all we need to add our localized translation for time format. Let’s change our de.yml file to add a German version.

# config/locales/de.yml
de:
  time:
    formats:
      description: "Aktuelle Uhrzeit %H Stunden und %M Minuten"

Now, we can use I18n.l or Rails #l helper to localize the Time object in our desired time format.

You can add many more formats for date/time to make it work using the format that you want. You can also get a ready made version of different types of local files from rails-i18n github repository. Once you place those file(s) in config/locales/ folder, they are ready to use for localization.

Using views for translation
In Rails 2.3 a new feature named “localized views (templates)” was introduced. For example, your application has an ItemsController class with an index view. Your default index template page for ItemsController is rendered by app/views/items/index.html.erb. If you want a localized version of index page, you will just have to write your text in index.de.html.erb file and then will have to save in the same folder. By setting the locale to :de, you can force Ruby on Rails application to render the German version of that template. When the locale is set to the default locale, the generic index.html.erb view will be used.

Generally, this feature is used for large amount of static content pages, where putting this large text content in YAML or in Ruby dictionaries feels cumbersome.

Translating ActiveRecord errors
You can translate Active Record validation error messages easily. Active Record uses different namespaces which can be used to translate easily models, attributes, and/or validations.

For example, an Item model with a validates_presence_of validation for the name attribute like this:

In this case :blank is the key for the error message. So, Active Record will check this key in the namespaces:

After following keys in the above mentioned order the result will be:

If, your models are using inheritance then the messages are looked up in the inheritance chain.

For example, you might have an Book model inheriting from Item:

class Book < Item
  validates_presence_of :title
end

Then Active Record will look for messages in this order:

activerecord.errors.models.book.attributes.title.blank
activerecord.errors.models.book.blank
activerecord.errors.models.item.attributes.title.blank
activerecord.errors.models.item.blank
activerecord.errors.messages.blank
errors.attributes.title.blank
errors.messagges.blank

You can also add translations for Active Record error_messages_for helper like this.

Organization of resource files
If you are using default i18n library settings, you may find that all files are stored in plain-text format on the disc. Placing all translations text of your Ruby on Rails application in one file per locale could be tedious to organize. You can easily store these files in a hierarchy structure which is easy to understand for you. For example, your config/locales directory could be organized like this:

By following the above way to organize resource files, you can easily differentiate model and their attribute names from text inside views, and all of this from the “defaults”. In Ruby on Rails, locale files in nested dictionaries structure are not being loaded automatically. So, to make it work we have to do this settings in config/application.rb

Setting the Locale from the Domain Name
In the previous article you read about how to set the locale in Ruby on Rails using application.rb settings. There are also other ways to do so. You can set the locale from the domain name where your Ruby on Rails application runs. Like www.example.com will load the content in English locale, www.example.de will load the content in German locale. This way you can use top-level domain names to set the locale for Ruby on Rails application.

The benefits of using this method are:

  1. The locale setting is an obvious part of the URL.
  2. Visitors obviously know in which language the content of the website will be displayed.
  3. It is very easy to implement in Ruby on Rails.
  4. Search engines love the content in different languages served at different domains.

You can use it in your ApplicationController:

We can also set the locale from the subdomain in a very similar way:

You can also use switching menu option in your application like this:

assuming you would set APP_CONFIG[:deutsch_website_url] to some value like http://www.example.de.

As previously mentioned this method has benefits, but what if you do not want to or able to provide different localizations (“language versions”) on different domains. The solution to this will be to add locale code in the URL params (or request path).

Setting the Locale from the URL Params
We can also set the locale by including it in URL params like www.example.com/items?locale=de or www.example.com/de/items.

You can use it in your ApplicationController:

You can get same benefits with this method as with setting the locale from the domain name, but the only little problem is it takes bit more code to implement. Here is the code to implement it by including it in our ApplicationController:

This way we can easily set/override “defaults” for url_for and helper methods dependent on it. So, every url_for dependent helper method will now add the locale in the query string like, http://example.com/?locale=de.

Now, if you want URLs to look like this http://example.com/de/items (which loads the German locale), we need to set up routes with path_prefix option this way:

So, when you call items_path for :de locales, an URL like http://example.com/de/items will be generated with the German locale. But, if you do not want to force the use of a locale in your routes you can set an optional path scope like:

This way when you access http://example.com/items without a locale, you will not get a routing error, which is very useful for setting default locale when one is not mentioned.

Setting the Locale from the Client Supplied Information
Sometimes, you have to set the locale from the information that client had supplied (not from the URL.) i.e. user’s preferred language or by choosing the locale in your application interface and saving it to their profile.

Using Accept-Language HTTP header
Client may set Accept-Language HTTP header in their browser or other client (like curl) and request the url. By supplying the information of the locale. We can implement this scenario with this code in ApplicationController.

You can also use a plugin such as Iain Hecker’s http_accept_language or even Rack middleware such as Ryan Tomayko’s locale to implement this method in production mode.

Using User Profile
In your Ruby on Rails application it is also possible that users want to set the locale for their interface by setting it from database. In user’s profile, user will have to choose the locale from a dropdown list and it will get saved in the database along with other profile data. In this case, you need to override the locale value something like this.

That’s it for Part II. In Part III we will check

  • Using different backends for I18n API
  • Translating Routes

Internationalization (I18n) API in Ruby on Rails 3 (Part I)

Whenever we hear about Internationalization of web application, the first word that comes to our mind is “Translation“. There are three ways to translate software:

  • Do it Yourself
  • Pay Someone for doing Translation
  • Ask your Users

No matter what, making the software ‘Internationalization ready’ is the developer’s job. No static strings, dates in the code and so forth; every text must be put in resource files and the code decides which text to use. Luckily Ruby on Rails provides a simple ruby gem called “I18n” (internationalization) that does exactly that.

Definition of Internationalization & Localization :

First of all let’s check the definition of Internationalization & Localization. According to Wikipedia,

i18n – “Internationalization is the process of designing a software application so that it can be adapted to various languages and regions without engineering changes.” This means, the “Internationalization” functionality translates all string and other locale specific information ( like date and currency formats) from your application in desire language to end user.

l10n – “Localization is the process of adapting software for a specific region or language by adding locale-specific components and translating text.” This means, the “Localization” functionality gives translations in local-specific language and other localized formats for local specific information.

How i18n mechanism works in Rails :

All the languages are different from each other in many ways (For example pluralization rules). It is very difficult for any tools/software to provide a general solution for language translation. To solve this problem I18n API provides two features in Ruby on Rails.

  1. Give support for English and other similar languages out of the box
  2. Provide a user-friendly and adjustable functions for supporting other languages

This means in Ruby on Rails every static string has been internationalized and you can “over-ride” these defaults with “localization” of the application.

There are two parts of Ruby I18n gem which are:

  1. The public I18n API – provides a Ruby module with public methods to characterize the work of the library
  2. A default backend – which executes these public methods

The Public I18n API comes with two most important methods which are:

  1. translate # This public method do translations of the text
  2. localize # This public method localize Date and Time objects to local formats as per settings

These method have aliases #t and #l respectively, so you can use them like this:

Setup/Configuration about the i18n mechanism

You can easily setup ruby I18n gem with few simple steps and use in your application for I18n support. Ruby on Rails application set up reasonable defaults in all .rb and .yml files in config folder. If you want to change the settings, you can easily change them according to your need.

To check the defaults for I18n module you have to check config/locales directory, which is default path for translation.

You will get one file named en.yml which default locale and contains these translation strings:

English is default locale in I18n library. So, if your application is not set in different locale, :en will translate all the static text changes.

So, when you check this on console the output shows like following :

Some other available locales which are supported by rails 3 are:

ar, bg, bn-IN, bs, ca, cy, cz, da, de, de-AT, de-CH, dsb, el, en-GB, en-US, eo, es, es-AR, es-CO, es-MX, es-PE, et, eu, fa, fi, fr, fr-CA, fr-CH, fur, gl-ES, gsw-CH, he, hi-IN, hr, hsb, hu, id, is, it, ja, ko, lo, lt, lv, mk, mn, nb, nl, nn, pl, pt-BR, pt-PT, rm, ro, ru, sk, sl, sr, sr-Latn, sv-SE, sw, th, tr, uk, vi, zh-CN, zh-TW

The easiest method to set your own locales to override the default locale is to change config/application.rb file in Rails 3. (You can apply same settings to config/environment.rb in Rails 2). Add this line to get German language as default locale:

The first line of code tells the location of translation file to the I18n library. The second line of code will set the default locale to :de other than :en.

Using the I18n module

Put these lines in config/locale/de.yml.

Let’s check now whether this settings translate “hello” into “German” locale or not.

Using the I18n with interpolation

Most of the time you want to use various variables with your translations. That’s why I18n API also provide interpolation feature.

Using the I18n with pluralization
In English there are only one singular and one plural form for a given string, e.g. “1 friend” and “2 friends”. The best thing is I18n API also provides a flexible pluralization feature in Rails applications.

We must note that an interpolation variable named :count plays key role in translation and pluralization.

The algorithm for pluralization is:

entry[count == 1 ? 0 : 1]

I.e. here 1 denotes singular and 0 denotes plural. So as specified using conditional operator, if count == 1, it will consider it as singular form otherwise its plural.

That’s it for Part I. In Part II we will check other features of I18n like

  • Localization of Times/Dates
  • Using views for translation
  • Translating ActiveModel errors
  • Organization of resource files
  • Setting the locale (from domain, parameter or client) and of course storing it in the database when the user is logged in

For more details you can check http://ruby-i18n.org/ .