I18n tool to translate your Ruby application.
Pull request Compare This branch is 308 commits behind ai:master.
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Failed to load latest commit information.



R18n is an i18n tool to translate your Ruby application into several languages. It contains a core gem and out-of-box wrapper plugins for frameworks or environments (Rails, Sinatra, Merb, desktop).

Quick Demo


  edit: Edit user
  name: User name is %1
  count: !!pl
    1: There is 1 user
    n: There are %1 users


# Setup R18n (or just use out-of-box `r18n-rails` or `sinatra-r18n` gem)
R18n.default_places = './i18n/'

include R18n::Helpers

# Use R18n
t.user.edit         #=> "Edit user"
t.user.name('John') #=> "User name is John"
t.user.count(5)     #=> "There are 5 users"

t.not.exists | 'default' #=> "default"
t.not.exists.translated? #=> false

l Time.now         #=> "03/01/2010 18:54"
l Time.now, :human #=> "now"
l Time.now, :full  #=> "3rd of January, 2010 18:54"

R18n Features

  • Nice Ruby-style syntax.
  • Filters.
  • Model Translation (or any Ruby object).
  • Autodetect user locales.
  • Flexible locales.
  • Total flexibility.

Ruby-style Syntax

R18n use compact, explicit and ruby-style syntax.

Translations store in YAML with types:

  edit: Edit user
  name: User name is %1
  count: !!pl
    1: There is 1 user
    n: There are %1 users

To access translation you can call methods with the same names:

t.user.edit         #=> "Edit user"
t.user.name('John') #=> "User name is John"
t.user.count(5)     #=> "There are 5 users"

t.not.exists | 'default' #=> "default"
t.not.exists.translated? #=> false

If the translation key is the name of an Object method you can access it via hash index or use Rails I18n API in r18n-rails:

t[:methods] #=> "Methods"

If you use r18n-rails plugin, you will have full compatibility with Rails I18n syntax.


You can add filters for some YAML type. For example, we add custom filter to return different translation depend on user gender (it’s actual for some languages).

  signup: !!gender
    male: Он зарегистрировался
    female: Она зарегистрировалась
R18n::Filters.add(:gender) do |translation, config, user|


R18n already has filters for HTML escaping, Textile, Markdown and lambdas:

hi: !!markdown
  Hi, **people**!
greater: !!escape
  1 < 2 is true
i18n.hi      #=> "<p>Hi, <strong>people!</strong></p>"
i18n.greater #=> "1 &lt; 2 is true"

You can also add filters for all returned translations or to issues, when translation can’t be founded.

Model Translation

You can translate any class, including ORM models (ActiveRecord, Mongoid, MongoMapper, DataMapper or others):

class Product < ActiveRecord::Base
  include R18n::Translated
  # Model has two normal properties: title_en and title_ru
  translations :title

R18n.set('en') # English
product.title #=> "Anthrax"

R18n.set('ru') # Russian
product.title #=> "Сибирская язва"

Autodetect User Locales

R18n has an agnostic core package and plugins with standard support for Sinatra and desktop applications. So for common cases you can use out-of-box gems with user locale autodetect. For special cases you can use core gem and hack everything, that you want (see “Total flexibility” section).

R18n automatically generate fallbacks for current user, based on user locales list from HTTP_ACCEPT_LANGUAGE, locale info (in some countries people know several languages), and default locale. For example, if user know Kazakh and German, R18n will try find translations in: Kazakh → German → Russian (second language in Kazakhstan) → English (default locale).

Flexible Locales

R18n store separated business translations and locale information. So all locales (pluralization rules, time and number localization, some base translations) ship with core gem and can be used out-of-box:

For example, Russian has built-in different pluralization without any lambdas in YAML:

t.user.count(1) #=> "1 пользователь"
t.user.count(2) #=> "2 пользователя"
t.user.count(5) #=> "5 пользователей"

Locales are simple Ruby classes, so they are very flexible. For example, R18n ship with very perfection full time formatter:

R18n.set('en') # English
l Time.now, :full         #=> "1st of December, 2011 12:00"
l Time.now + 1.day, :full #=> "2nd of December, 2011 12:00"

R18n.set('fr') # French
l Time.now, :full         #=> "1er décembre 2011 12:00"
l Time.now + 1.day, :full #=> "2 décembre 2011 12:00"

Years in Thailand are counted in the Buddhist Era. It’s very easy for R18n:

R18n.l Time.now, :full #=> "1 พฤศจิกายน, 2554 12:00"

Total Flexibility

R18n is very flexible and agnostic. For example, Rails I18n compatibility level is just few filters and custom loader.

Translation variables and pluralization (“1 comment”, “5 comments”) are filters too, so you can disable, replace or cascade them. For example, you can use the “named variables filter” from the r18n-rails-api gem:

greeting: "Hi, %{name}"
t.greeting(name: 'John') #=> "Hi, John"

Or you can add cascade filter on all untranslated messages to collect issues:

R18n::Filters.add(::R18n::Untranslated) do |v, c, transl, untransl, path|
  File.open('untranslated.list', 'w') { |io| io.puts(path) }

R18n can load translations from anywhere, not just from YAML files. You just need to create loader object with 2 methods: available and load:

class DBLoader
  def available
  def load(locale)

R18n.default_places = DBLoader.new
R18n.set('en') # Load English messages from DB

Also you can use several I18n object in program. For example, to communicate with user and log system journal for admins:

puts I18n.t :error # Show error in French

admin_i18n = R18n::I18n.new(ADMIN_LOCALES)