Tag Archives: I18n integration with Rails Application

Introduction

The Ruby I18n (shorthand for internationalization) gem which is shipped with Ruby on Rails (starting from Rails 2.2) provides an easy-to-use and extensible framework for translating your application to a single custom language other than English or for providing multi-language support in your application.

The process of “internationalization” usually means to abstract all strings and other locale specific bits (such as date or currency formats) out of your application. The process of “localization” means to provide translations and localized formats for these bits.

So, in the process of internationalizing your Rails application you have to:

  • Ensure you have support for i18n.
  • Tell Rails where to find locale dictionaries.
  • Tell Rails how to set, preserve and switch locales.

In the process of localizing your application you’ll probably want to do the following three things:

  • Replace or supplement Rails’ default locale – e.g. date and time formats, month names, Active Record model names, etc.
  • Abstract strings in your application into keyed dictionaries – e.g. flash messages, static text in your views, etc.
  • Store the resulting dictionaries somewhere.

How I18n in Ruby on Rails Works

Internationalization is a complex problem. Natural languages differ in so many ways (e.g. in pluralization rules) that it is hard to provide tools for solving all problems at once. For that reason the Rails I18n API focuses on:

  • providing support for English and similar languages
  • making it easy to customize and extend everything for other languages

As part of this solution, every static string in the Rails framework – e.g. Active Record validation messages, time and date formats – has been internationalized, so localization of a Rails application means “over-riding” these defaults.

The Overall Architecture of the Library:
The Ruby I18n gem is split into two parts:

  1. The public API of the i18n framework – a Ruby module with public methods that define how the library works
  2. A default backend (which is intentionally named Simple backend) that implements these methods

As a user you should always only access the public methods on the I18n module, but it is useful to know about the capabilities of the backend.

The Public I18n API

The most important methods of the I18n API are:

    translate # Lookup text translations
    localize  # Localize Date and Time objects to local formats
  

These have the aliases #t and #l so you can use them like this:

    I18n.t 'store.title'
    I18n.l Time.now
  

There are also attribute readers and writers for the following attributes:

    load_path         # Announce your custom translation files
    locale            # Get and set the current locale
    default_locale    # Get and set the default locale
    exception_handler # Use a different exception_handler
    backend           # Use a different backend
  

So, let’s internationalize a simple Rails application from the ground up.

Setup the Rails Application for Internationalization

There are just a few simple steps to get up and running with I18n support for your application.

Configure the I18n Module:

Following the convention over configuration philosophy, Rails will set up your application with reasonable defaults. If you need different settings, you can overwrite them easily.Rails adds all .rb and .yml files from the config/locales directory to your translations load path, automatically.

The default en.yml locale in this directory contains a sample pair of translation strings:

   en:
     hello: "Hello world"
   

This means, that in the :en locale, the key hello will map to the Hello world string. Every string inside Rails is internationalized in this way. You can use YAML or standard Ruby Hashes to store translations in the default (Simple) backend.

The I18n library will use English as a default locale, i.e. if you don’t set a different locale, :en will be used for looking up translations.

The translations load path (I18n.load_path) is just a Ruby Array of paths to your translation files that will be loaded automatically and available in your application. You can pick whatever directory and translation file naming scheme makes sense for you.

The default application.rb file has instructions on how to add locales from another directory and how to set a different default locale. Just uncomment and edit the specific lines.

  # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
  # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
  # config.i18n.default_locale = :de
 

Optional: Custom I18n Configuration Setup
For the sake of completeness, let’s mention that if you do not want to use the application.rb file for some reason, you can always wire up things manually, too.

To tell the I18n library where it can find your custom translation files you can specify the load path anywhere in your application – just make sure it gets run before any translations are actually looked up. You might also want to change the default locale. The simplest thing possible is to put the following into an initializer:

   # in config/initializers/locale.rb
   # tell the I18n library where to find your translations
   I18n.load_path += Dir[Rails.root.join('lib', 'locale', '*.{rb,yml}')]
   # set default locale to something other than :en
   I18n.default_locale = :pt
 

Setting and Passing the Locale

If you want to translate your Rails application to a single language other than English (the default locale), you can set I18n.default_locale to your locale in application.rb or an initializer as shown above, and it will persist through the requests.

However, you would probably like to provide support for more locales in your application. In such case, you need to set and pass the locale between requests. We can set locale in three ways as follows:

1.Setting the Locale from the URL Params
2.Setting the Locale from the Domain Name
3.Setting the Locale from the Client Supplied Information

1.Setting the Locale from the URL Params

The setting part is easy. You can set the locale in a before_action in the ApplicationController like this:

   before_action :set_locale
   
   def set_locale
     I18n.locale = params[:locale] || I18n.default_locale
   end
  

This requires you to pass the locale as a URL query parameter as in http://example.com/books?locale=pt. (This is, for example, Google’s approach.) So http://localhost:3000?locale=pt will load the Portuguese localization, whereas http://localhost:3000?locale=de would load the German localization, and so on.

2.Setting the Locale from the Domain Name

One option you have is to set the locale from the domain name where your application runs. For example, we want www.example.com to load the English (or default) locale, and www.example.es to load the Spanish locale. Thus the top-level domain name is used for locale setting. This has several advantages:

  1. The locale is an obvious part of the URL.
  2. People intuitively grasp in which language the content will be displayed.
  3. It is very trivial to implement in Rails.
  4. Search engines seem to like that content in different languages lives at different, inter-linked domains.

You can implement it like this in your ApplicationController:

   before_action :set_locale
 
   def set_locale
     I18n.locale = extract_locale_from_tld || I18n.default_locale
   end
 
   # Get locale from top-level domain or return nil if such locale is not available
   # You have to put something like:
   #   127.0.0.1 application.com
   #   127.0.0.1 application.it
   #   127.0.0.1 application.pl
   # in your /etc/hosts file to try this out locally
   def extract_locale_from_tld
     parsed_locale = request.host.split('.').last
     I18n.available_locales.map(&:to_s).include?(parsed_locale) ? parsed_locale : nil
   end
 

3.Setting the Locale from the Client Supplied Information

In specific cases, it would make sense to set the locale from client-supplied information, i.e. not from the URL. This information may come for example from the users’ preferred language (set in their browser), can be based on the users’ geographical location inferred from their IP, or users can provide it simply by choosing the locale in your application interface and saving it to their profile. This approach is more suitable for web-based applications or services, not for websites.

3.1 Using Accept-Language

One source of client supplied information would be an Accept-Language HTTP header. People may set this in their browser or other clients (such as curl).

A trivial implementation of using an Accept-Language header would be:

   
    def set_locale
      logger.debug "* Accept-Language: #{request.env['HTTP_ACCEPT_LANGUAGE']}"
      I18n.locale = extract_locale_from_accept_language_header
      logger.debug "* Locale set to '#{I18n.locale}'"
    end
 
    private
      def extract_locale_from_accept_language_header
        request.env['HTTP_ACCEPT_LANGUAGE'].scan(/^[a-z]{2}/).first
      end
  

Internationalizing your Application

OK! Now you’ve initialized I18n support for your Ruby on Rails application and told it which locale to use and how to preserve it between requests. With that in place, you’re now ready for the really interesting stuff.

Let’s internationalize our application, i.e. abstract every locale-specific parts, and then localize it, i.e. provide necessary translations for these abstracts.

You most probably have something like this in one of your applications:

   # config/routes.rb
   Rails.application.routes.draw do
     root to: "home#index"
   end
  
    # app/controllers/application_controller.rb
    class ApplicationController < ActionController::Base
      before_action :set_locale
 
      def set_locale
        I18n.locale = params[:locale] || I18n.default_locale
      end
    end
  
    # app/controllers/home_controller.rb
    class HomeController < ApplicationController
      def index
        flash[:notice] = "Hello Flash"
      end
    end
  
    # app/views/home/index.html.erb

<h1>Hello World</h1>



<%= flash[:notice] %>

  

i18n_screen1

Adding Translations

Obviously there are two strings that are localized to English. In order to internationalize this code, replace these strings with calls to Rails’ #t helper with a key that makes sense for the translation:

  # app/controllers/home_controller.rb
  class HomeController < ApplicationController
    def index
      flash[:notice] = t(:hello_flash)
    end
  end
 
   # app/views/home/index.html.erb

<h1><%=t :hello_world %></h1>



<%= flash[:notice] %>

 

When you now render this view, it will show an error message which tells you that the translations for the keys :hello_world and :hello_flash are missing.

I18n_screen2

So let’s add the missing translations into the dictionary files (i.e. do the “localization” part):

  # config/locales/en.yml
  en:
    hello_world: Hello world!
    hello_flash: Hello flash!
 
  # config/locales/pirate.yml
  pirate:
        hello_world: Ahoy World
        hello_flash: Ahoy Flash
 

There you go. Because you haven’t changed the default_locale, I18n will use English. Your application now shows:

I18n_screen3

And when you change the URL to pass the pirate locale (http://localhost:3000?locale=pirate), you’ll get:

I18n_screen4

Note:

  • You need to restart the server when you add new locale files.
  • You may use YAML (.yml) or plain Ruby (.rb) files for storing your translations in SimpleStore.
  • YAML is the preferred option among Rails developers. However, it has one big disadvantage.
  • YAML is very sensitive to whitespace and special characters, so the application may not load your dictionary properly.
  • Ruby files will crash your application on first request, so you may easily find what’s wrong. (If you encounter any “weird issues” with YAML dictionaries, try putting the relevant portion of your dictionary into a Ruby file.)

Conclusion
I18n gem has some limitations & may not fulfill your requirements. We can pass variables to translation files and also pass different parameters for translation method(#t). For more information about I18n gem visit click on this Link

Hope it was useful! Thanks for reading this article.