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