ruby rails i18n gem

CEO and founder of Lingohub. Envisioning a multilingual digital world. Email me if you have questions about how Lingohub can help you take your products global.

Helmut Development 4 Comments

SHARE

Internationalization for Ruby with the r18n gem

The previous two articles on internationalization for Ruby have covered the usage of the i18n gem in detail. This article will introduce an alternative: R18n including usage of the r18n gem.

ruby r18n gemR18n is an internationalization tool that can be used in the process of creating multilingual Ruby applications. It contains a core gem and out-of-box wrapper plugins for frameworks or environments (Rails, Sinatra, desktop). Some of its features are Ruby style syntax, filters, model (or Ruby object) translation, auto detection of user locales, flexible locales and flexibility in general.

Just like the i18n gem that we described previously, the default resource file format for r18n is YAML. The file names should correspond to the locale used. Here is a sample en.yml file that the test will be run on:

Installation and setup of the r18n gem

gem install r18n-core

After installing the gem, lets start the console in the directory where the sample YAML file is located, require the library and set the default resource files’ location and a locale that will be used:

2.0.0-p247 :001 > require 'r18n-core'
  => true
2.0.0-p247 :002 > R18n.default_places = './'
  => "./"  
2.0.0-p247 :003 > R18n.set('en') 
  => #<R18n::I18n:0x00000001f114a8 ...

Translation lookup with r18n

To access the translation lookup functionality in r18n, method t of the R18n module can be used, or the helper method t if the R18n::Helpers is included. The method returns a Translation object on which other methods that correspond to the phrase key can be chained:

2.0.0-p247 :004 > R18n.t.greetings.hello.world 
  => "Hello World!"

2.0.0-p247 :005 > include R18n::Helpers
  => Object
2.0.0-p247 :006 > t.greetings.hello.world 
  => "Hello World!"

2.0.0-p247 :007 > t.class
 => R18n::Translation 
2.0.0-p247 :008 > t.greetings.class
 => R18n::Translation 
2.0.0-p247 :009 > t.greetings.hello.world.class
 => R18n::TranslatedString

If a translation that is missing is looked up, the Untranslated object is returned. To check if the translation exists, the translated? method can be used. Providing a fall-back value for a missing translation is also easy:

2.0.0-p247 :010 > t.greetings.hello.everybody
  => #<R18n::Untranslated:0x00000001d75158 @translated_path="greetings.hello.", 
@untranslated_path="everybody", @locale=Locale en (English), 
@filters=#<R18n::GlobalFilterList:0x000000014bfae0>> 

2.0.0-p247 :011 > t.greetings.hello.everybody.class
 => R18n::Untranslated 

2.0.0-p247 :012 > t.greetings.hello.everybody.translated?
  => false

2.0.0-p247 :013 > t.greetings.hello.everybody | 'default'
  => 'default'

To interpolate a variable in the string, the values should be passed as arguments.

2.0.0-p247 :014 > t.greetings.hello.user 'stranger', 'our site'
 => "Hello stranger, welcome to our site"

Not providing the value for variables wont raise any exceptions, it will just leave place-holders blank.

2.0.0-p247 :015 > t.greetings.hello.user
 => "Hello , welcome to "

Filters for translations processing in r18n

Filters can be used to process translations in various ways. In the resource file filtered content needs to be marked by YAML type. R18n has built in filters for HTML escaping, Textile, Markdown and lambdas. Prior to using Textile , RedCloth gem needs to be installed.

hi: !!markdown
  Hi, **people**!
greater: !!escape
  1 < 2 is true
greater_no_filter:
  1 < 2 is true
alarm: !!textile
  It will delete _all_ users!
2.0.0-p247 :019 > t.greater
 => "1 &lt; 2 is true" 
2.0.0-p247 :020 > t.greater_no_filter
 => "1 < 2 is true" 
2.0.0-p247 :021 > t.hi
 => "<p>Hi, <strong>people</strong>!</p>\n"
2.0.0-p247 :022 > t.alarm
 => "<p>It will delete <em>all</em> users!</p>"

Lambdas can also be used in the translations:

sum: !!proc |x, y| x + y
2.0.0-p247 :023 > t.sum
 => 3

Any unwanted filter can be switched off;

R18n::Filters.off(:procedure)

Custom filters can be defined in filters.rb.

Pluralization in the .yml file

The keys of the phrases containing the plural forms should be marked with the pl filter in the YAML file, and its children keys should correspond to the cardinal numbers, or “n”:

inbox:
  messages: !!pl
    0: Your inbox is empty.
    1: You have one message in your inbox.
    n: You have %1 messages in your inbox.

To access the plural, the count should be passed as an argument.

2.0.0-p247 :016 > t.inbox.messages 0
 => "Your inbox is empty." 
2.0.0-p247 :017 > t.inbox.messages 1
 => "You have one message in your inbox." 
2.0.0-p247 :018 > t.inbox.messages 39
 => "You have 39 messages in your inbox."

 Localization with r18n

For localizing the time, date, numbers and currency, R18n::l method can be used, or just l, if the R18n::Helpers was included.

Numbers and floats can be formatted according to the rules of the current locale. The real typographic minus and non-breakable spaces will be used for formatting (if required by the rules).

l -12000.5 #=> "−12,000.5"

Months, week day names, Time, Date and DateTime can be translated by the strftime method:

l Time.now, '%B'  #=> "September"

R18n has some built-in time formats for locales: :human, :full and :standard (the default):

l Time.now, :human #=> "now"
l Time.now, :full  #=> "August 9th, 2009 21:47"
l Time.now         #=> "08/09/2009 21:41"
l Time.now.to_date #=> "08/09/2009"

Translating ActiveRecord models and plain Ruby objects

With r18n it is possible to add localized fields to the model (or any other Ruby object) that would respond to the common methods and the fields would be accessed depending on the current locale. For example, to have this feature for the title and text fields of the model Post and English and Russian locales, this should be added to the migration:

t.string :title_en
t.string :title_ru

t.string :text_en
t.string :text_ru

Next, R18n::Translated should be included in the model and the translation method called:

class Post < ActiveRecord::Base
  include R18n::Translated

  translations :title, :text
end

Now, the model will have virtual methods title, text, title= and text=, which will call title_ru or title_en, based on current user locale.

> post = Post.first
> R18n.set('en')
> post.title
 => "English title"
> R18n.set('ru')
> R18n.title
 => "Russian title"

Locale settings

Settings for all the locales that r18n supports are located at locales directory of the r18n gem. The settings include the locale name, sub-locales, date/time and number localization and pluralization rules.

To get information about a locale, an instance of R18n::Locale should be created:

locale = R18n.locale('en')
locale.title #=> "English"
locale.code  #=> "en"

# left to right setting:
locale.ltr? #=> true
locale.week_start #=> :sunday

R18n wrappers for Rails and Sinatra

r18n-rails gem adds out of box support for Rails internationalization. It is a wrapper for r18n-rails-api and r18n-core libraries. The main differences to the core gem are that

  • it comes with some defaults set for Rails (for example, translations are stored in app/i18n/locale.yml),
  • r18n helper methods are automatically available in the views and controllers
  • and both i18n and r18n syntax can be used interchangeably

To install the gem, it should be added to the Gemfile:

gem 'r18n-rails'

Both i18n and r18n syntax can be used interchangeably:

t 'user.name',  :name => 'John'
t 'user.count', :count => 5
t.user.name(:name => 'John') # for Rails I18n named variables
t.user.name('John') # for R18n variables 
t.user.count(5)

r18n-sinatra is a wrapper of r18n-core that provides the support for Sinatra applications.

To add r18n to Sinatra application:

1) ./i18n/ directory should be created

2) yaml files should be placed at ./i18n/

3) r18n should be added to Sinatra application:

require 'sinatra/r18n'

#the defaults can be overridden:
#R18n::I18n.default = 'ru'
#R18n.default_places { './translations' }

If the application inherits from Sinatra::Base, this should also be added:

class YourApp < Sinatra::Base
  register Sinatra::R18n
  set :root, File.dirname(__FILE__)

4) locales can be added to urls, for example:

get '/:locale/posts/:id' do
  @post = Post.find(params[:id])
  haml :post
end

or saved in a session:

before do
  session[:locale] = params[:locale] if params[:locale]
end

5) r18n helper methods for translation and localization can be used in views.

> t.hello('Ela')
# => "Hello Ela"

r18n vs i18n

In the end, here is a quick comparison of translation, interpolation and pluralisation with these two gems:

library i18n r18n
translation
en:
  hello: Hello, %{username)
> t :hello, :username('Ela')
# => "Hello Ela"
#
hello: Hello, %1
> t.hello('Ela')
# => "Hello Ela"
pluralisation
 ru:
  robots:
    one: %{count} робот
    few: %{count} робота
    many: %{count} роботов
> t('robots', :count => 1)
# => 1 робот
#
robots: !!pl
  1: %1 робот
  2: %1 робота
  n: %1 роботов
> t.robots(1)
# => 1 робот

 Further reading