Fork me on GitHub

R18n is a tool to internationalize and localize your Rails, Sinatra or desktop Ruby application.

Why R18n?

Ruby-style Syntax

R18n uses hierarchical not English-centrist YAML format for translations by default:

user:
  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 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 translation key has name of Object method you can use another way:

t[:methods] #=> "Methods"

Filters

You can add custom filters for some YAML type or any translated strings. Filters are cascade and can communicate with each other.

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

hi: !!markdown
  **Hi**, people!
greater: !!escape
  1 < 2 is true

t.hi      #=> "<p><strong>Hi</strong>, people!</p>"
t.greater #=> "1 &lt; 2 is true"

Flexibility

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

greeting: "Hi, {{name}}"

R18n::Filters.on(:named_variables)
t.greeting(name: 'John') #=> "Hi, John"

Flexible Locales

Locale can extend Locale class, so locales are very flexible. For example, English locale extend time formatters:

l Date.now, :full #=> "30th of November, 2009"

Or 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 пользователей"

Loaders

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

class DBLoader
  def available
    Translation.find(:all).map(&:locale)
  end
  def load(locale)
    Translation.find(locale).to_hash
  end
end

R18n.set R18n::I18n.new(user_locales, DBLoader.new)

You can also set a list of different translation places or set extension places, which will be used only with application translation (useful for plugins).

Object Translation

You can translate any class, including ORM models:

require 'r18n-core/translated'

class Product < ActiveRecord::Base
  include R18n::Translated
  # Model has two usual property: title_en and title_ru
  translations :title
end

# For English user
product.title #=> "Anthrax"

# For Russian user
product.title #=> "Сибирская язва"

Localization

R18n can localize numbers and time:

l -5000                 #=> "−5,000"
l Time.now              #=> "30/11/2009 14:36"
l Time.now, :full       #=> "30th of November, 2009 14:37"
l Time.now - 60, :human #=> "1 minute ago"

Several User Languages

Lack of translation in user language isn’t exception for R18n (because translation to not primary language done by enthusiasts, it can be out of date). R18n just automatically take next user language (browser send a list of locales) and for cultures with two officially languages (e.g., exUSSR) it take second language (e.g., if translation isn’t available in Kazakh R18n will see in Russian):

i18n = R18n::I18n.new(['kk', 'de'], 'dir/with/translations')

i18n.locales    #=> [Locale kk (Қазақша), Locale de (Deutsch),
                #    Locale ru (Русский), Locale en (English)]

i18n.kazakh  #=> "Қазақша", main user language
i18n.deutsch #=> "Deutsch", not in Kazakh, use next user locale
i18n.russian #=> "Русский", not in kk and de, use Kazakh sublocale
i18n.english #=> "English", not in any user locales, use default

Agnostic

R18n has a agnostic core package and plugins with out-of-box support for Sinatra, Merb and desktop applications.