stalno u pokretu

< IT & tech

17. 09. 2014. back end * i18n&l10n * ruby

Internationalization for Ruby with the r18n gem

I originally published this article on Lingohub blog in 2013, while working for the company. Lingohub specializes in providing app localization for developers and translators.</a>

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.

R18n 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 > 
  => "Hello World!"

2.0.0-p247 :005 > include R18n::Helpers
  => Object
2.0.0-p247 :006 > 
  => "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 >
 => 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), 

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
  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;

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”:

  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, '%B'  #=> "September"

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

l, :human #=> "now"
l, :full  #=> "August 9th, 2009 21:47"
l         #=> "08/09/2009 21:41"
l #=> "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

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 '',  :name => 'John'
t 'user.count', :count => 5 => 'John') # for Rails I18n named variables'John') # for R18n variables 

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__)
  1. locales can be added to urls, for example:
get '/:locale/posts/:id' do
  @post = Post.find(params[:id])
  haml :post

or saved in a session:

before do
  session[:locale] = params[:locale] if params[:locale]
  1. 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**
  hello: Hello, %{username)
> t :hello, :username('Ela')
# => "Hello Ela"
hello: Hello, %1
> t.hello('Ela')
# => "Hello Ela"
    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

Related posts

17. 09. 2014. back end * i18n&l10n * ruby
i18n gem advanced features – Ruby on Rails internationalization

Using Different Backends for Ruby i18n gem, chaining backends, caching is an i18n gem advanced feature, fallbacks, Translation Metadata, cascading lookups, translation symlinks, using custom exception handlers
[ reading time: ~ 15 min. ]

17. 09. 2014. back end * i18n&l10n * padrino * ruby * sinatra
Internationalization for Ruby – i18n gem

i18n for plain Ruby & the i18n gem, i18n for Ruby on Rails, i18n for Sinatra, i18n for Padrino
[ reading time: ~ 20 min. ]

17. 09. 2014. back end * gettext * i18n&l10n * ruby
Ruby gettext internationalization tutorial on using the fast_gettext gem

Setup, translation lookup, managing translations (MO/PO, db, yaml), rails plugin, pluralization, defaults, multiple repositories, logging...
[ reading time: ~ 9 min. ]

15. 08. 2014. api * back end * ruby
API - design, implementation, testing

notes and resources
[ reading time: ~ 3 min. ]

01. 09. 2014. api * back end * bdd / tdd * cucumber * ruby * ruby gem
Developing rails REST API with Cucumber

json_spec, writing the definitions, checking the http headers, testing authentication...
[ reading time: ~ 3 min. ]

03. 09. 2014. api * back end * ruby * ruby gem * security
Devise as authentication solution for rails API

Authentication token, the use of sessions, intsallation and setup, configuring the model, the routes, application controller, custom devise controllers, testing the controllers
[ reading time: ~ 9 min. ]

07. 09. 2014. api * back end * ruby
Building a JSON API in rails

[ reading time: ~ 1 min. ]

12. 09. 2014. back end * ruby
ActiveRecord notes

Uniqueness on a combination of fields, inclusion in array, getting all model associations, overriding model getters and setters, serialization...
[ reading time: ~ 2 min. ]

17. 09. 2014. back end * cakephp * codeigniter * i18n&l10n * php * symfony * yii * zend
Internationalization How To for the 5 most popular PHP frameworks

In this how-To you will read about using CodeIgniter, CakePHP, Zend, Yii and Symfony
[ reading time: ~ 8 min. ]

17. 09. 2014. back end * fuelphp * i18n&l10n * laravel * php
PHP internationalization frameworks: Laravel and FuelPHP

The article covers two more PHP frameworks that are frequently used, Laravel and FuelPHP
[ reading time: ~ 6 min. ]

17. 09. 2014. back end * i18n&l10n * php
PHP internationalization – i18n mechanisms tutorial

Static web and internationalization, dynamic web applications, localizing strings directly in the code, storing the strings in a relational database, message catalogues (string arrays), JSON, use of resource files
[ reading time: ~ 10 min. ]

17. 09. 2014. back end * gettext * i18n&l10n * php
PHP internationalization with gettext tutorial

Installation, portable object template files, plurals, an example of a PO file, directory structure, machine object files, gettext caching problems, setting up PHP for internationalization with gettext...
[ reading time: ~ 18 min. ]

17. 09. 2014. back end * i18n&l10n
Resource file formats

ini files, properties files, iOS .strings files, YAML files, RESX and RESW files, RESJSON files
[ reading time: ~ 4 min. ]

22. 11. 2014. back end * deployment * ruby
rails deployment

Setting up Debian 7 server on VPS, nginx, php-fpm, ruby, unicorn/phusion passanger, essentials, ssh keys, capistrano...
[ reading time: ~ 2 min. ]

10. 08. 2014. api * back end
Representational state transfer (REST)

[ reading time: ~ 3 min. ]

21. 08. 2014. database * ruby
Adding UUID to rails

[ reading time: ~ 1 min. ]

04. 09. 2014. api * back end
Design principles of the REST-ful JSON API (HATEOAS)

REST, JSON, API, HATEOAS, media types, top level resource representations, url templates, compound documents, urls, responses, errors, JSend response types, HTTP Status codes
[ reading time: ~ 12 min. ]

24. 11. 2014. back end * dev_tool
nginx notes

[ reading time: ~ 1 min. ]

10. 12. 2014. back end * ruby gem

[ reading time: ~ 1 min. ]

Ovaj sajt ne sadrži first party kukije i druge mehanizme za aktivno praćenje poseta/ponašanja (facebook, google analytics, itd...). Na nekim stranicama se nalaze embedovani youtube video klipovi i google mape koji učitavaju svoje kukije.