
Table of content
In July we published a series of articles on internationalization for PHP. This month we will focus on tutorials about internationalization for Ruby, starting out with a comprehensive tutorial on getting started with the i18n gem and various frameworks.
If you missed any of the prior articles, you can have a look here:
Localization and Internationalization are recurring themes in our blog posts. If you are not 100% sure what they refer to, you can find an explanation in this article.

For August: Internationalization in the Ruby world
For August, we have prepared a series of articles, and to start out we will demonstrate how internationalization can be achieved in the Ruby world.
Ruby is a dynamic, reflective, general-purpose object-oriented programming language that combines syntax inspired by Perl with Smalltalk-like features. It was also influenced by Eiffel and Lisp. Ruby was first designed and developed in the mid-1990s by Yukihiro “Matz” Matsumoto in Japan. [1]
Topics covered in this article
Since the emergence of web application frameworks like Ruby on Rails and DSLs like Sinatra, Ruby has been used to reach a wide international public. Early in August 2013, there were nearly 200,000 websites worldwide using Ruby on Rails [2] alone.
There is no better way of reaching a wide international audience than in their own individual languages, and this series of articles will show you how. Let’s start with the basics and then move on to the details.
Internationalization for Ruby: The Ruby i18n gem
One of the most popular Ruby gems for internationalization is the Ruby I18n gem. It allows translation and localization, interpolation of values in translations, pluralization, customizable transliteration to ASCII, flexible defaults, bulk lookup, lambdas as translation data, custom key/scope separators, and custom exception handlers.
The gem is split into two parts:
the public API
a default backend (Simple backend)
Other backends can be used, such as Chain, ActiveRecord, KeyValue, or custom implementations.
YAML (.yml) or plain Ruby (.rb) files are used for storing translations in the Simple backend, but YAML is the preferred option among Ruby developers.
Internationalization and the YAML resource file format
YAML is a human-readable data serialization format. Its syntax is designed to map easily to common data structures such as lists, associative arrays, and scalars. Unlike some other formats, YAML has a well-defined standard (specification).
Key features of YAML resource files
Information is stored in key-value pairs separated by
:Keys can be nested (scoped)
The root key corresponds to the locale (e.g.,
en-US,de)Leaf keys must have values
Values can be escaped
Correct indentation is required for hierarchy
Lines starting with
#are commentsPlaceholder syntax:
%{name}UTF-8 encoding is typically used
Before demonstrating i18n methods, let’s create an example YAML file we will load and test:
👉 Gist
Installation and setup
After installing the gem, change directory to where the sample YAML file is saved and start the IRB (Interactive Ruby Shell).
First, require the library:
require 'i18n'
Check the current locale (default is English):
I18n.locale
# => :en
Change the locale:
I18n.locale = :de
# => :de
Translation lookup
Translation lookup is done via I18n.translate (alias: I18n.t).
Example:
I18n.translate :world, scope: 'greetings.hello'
# => "translation missing: en.hello.world"
Load translation files:
I18n.load_path = Dir['./*.yml', './*.rb']
# => ["./en.yml"]
Retry lookup:
I18n.translate :world, scope: 'greetings.hello'
# => "Hello world!"
Explicit locale:
I18n.translate :world, scope: 'greetings.hello', locale: :en
# => "Hello world!"
Equivalent calls:
I18n.translate 'greetings.hello.world'
I18n.translate 'hello.world', scope: :greetings
I18n.translate 'hello.world', scope: 'greetings'
I18n.translate :world, scope: 'greetings.hello'
I18n.translate :world, scope: [:greetings, :hello]
Defaults:
I18n.translate :missing, default: [:also_missing, 'Not here']
# => "Not here"
Interpolation:
I18n.translate :user, scope: [:greetings, :hello], user: 'Ela'
# => "Hello Ela!"
Multiple keys:
I18n.translate [:world, :friend], scope: [:greetings, :hello]
# => ["Hello World!", "Hello Friend!"]
Nested hash result:
I18n.translate :hello, scope: [:greetings]
# => {:world=>"Hello World!", :user=>"Hello %{user}", :friend=>"Hello Friend!"}
Pluralization options in Ruby i18n
English uses singular and plural forms (e.g., “1 message” vs “2 messages”). Other languages have more complex rules.
The :count variable is used for interpolation and plural selection:
I18n.translate :messages, scope: :inbox, count: 1
# => "You have one message in your inbox."
I18n.translate :messages, scope: :inbox, count: 39
# => "You have 39 messages in your inbox."
Pluralization rule (English):
entry[count == 1 ? 0 : 1]
one→ singularother→ plural
Setting up date and time localization
Localize time using:
I18n.localize Time.now
# => "Wed, 14 Aug 2013 13:34:49 +0200"
Custom format:
I18n.localize Time.now, format: :short
# => "14 Aug 13:34"
Alias:
I18n.l Time.now
i18n – default internationalization solution for Ruby on Rails
I18n in Ruby on Rails
I18n is the default internationalization solution for Ruby on Rails and is localized with the use of the rails-i18n gem.
In accordance with the Rails philosophy of convention over configuration, Rails applications come with some reasonable defaults already set.
Default Rails I18n setup
Instead of doing this manually:
require 'i18n'
I18n.locale = :en
I18n.default_locale = :en
I18n.load_path = Dir['./*.yml']
Rails automatically:
Loads all
.rband.ymlfiles fromconfig/localesSets English (
:en) as the default locale
Locale file organization in Rails
By default, Rails expects all locale files in:
config/locales
However, it is often better to organize files into subdirectories per locale, or even separate translations by domain (models vs views).
Customizing I18n settings in Rails
You can override defaults in application.rb:
config.i18n.load_path += Dir[Rails.root.join('config/locales/**/*.{rb,yml}').to_s]
config.i18n.default_locale = :de
config.i18n.available_locales = [:en, :de, :fr]
Available locales in Rails
Rails itself is localized into many languages via the rails-i18n project, including translations for built-in framework text.
Managing locale selection in Rails applications
Multilingual applications need to allow users to switch locales and persist that choice.
However, storing locale in sessions or cookies is not recommended. Instead, locale should be part of the URL to ensure:
Shareable URLs preserve language
SEO-friendly structure
RESTful design
Ways to pass locale in Rails
Query parameter
http://example.com/?locale=srURL path
http://example.com/sr/Domain
http://example.srSubdomain
http://sr.example.comClient information (headers, profile, etc.)
Using locale from query parameters
In ApplicationController:
before_action :set_locale
def set_locale
I18n.locale = params[:locale] || I18n.default_locale
end
Keeping locale in URLs
Override default_url_options:
def default_url_options(options = {})
{ locale: I18n.locale }
end
This ensures all generated URLs include the locale automatically.
Locale in URL path (recommended approach)
Example routing:
scope "(:locale)", locale: /en|sr/ do
resources :books
end
This allows:
/en/books/sr/books/books(default locale fallback)
Locale from domain or subdomain
before_action :set_locale
def set_locale
I18n.locale = extract_locale_from_tld || I18n.default_locale
# or:
# I18n.locale = extract_locale_from_subdomain || I18n.default_locale
end
def extract_locale_from_tld
parsed_locale = request.host.split('.').last
I18n.available_locales.include?(parsed_locale.to_sym) ? parsed_locale : nil
end
def extract_locale_from_subdomain
parsed_locale = request.subdomains.first
I18n.available_locales.include?(parsed_locale.to_sym) ? parsed_locale : nil
end
Locale from client-supplied data
From user profile:
I18n.locale = current_user.locale
From browser headers:
def set_locale
I18n.locale = extract_locale_from_accept_language_header
end
def extract_locale_from_accept_language_header
request.env['HTTP_ACCEPT_LANGUAGE'].scan(/^[a-z]{2}/).first
end
For production systems, consider:
http_accept_language gem: https://github.com/iain/http_accept_language
rack middleware: https://github.com/rack/rack-contrib/blob/master/lib/rack/contrib/locale.rb
Rails I18n helpers
Rails provides shorthand helpers:
t→ translatel→ localize
Instead of:
I18n.t :hello
I18n.l Time.now
You can write:
t :hello
l Time.now
Missing translations are automatically wrapped in helpful error output.
Inflection rules in Rails
Rails allows locale-specific inflection rules (pluralization, singularization, etc.) in:
config/initializers/inflections.rb
Localized views in Rails
Rails supports localized templates:
index.html.erb
index.de.html.erb
Rails will automatically select the correct version based on locale.
Safe HTML translations
Keys ending in _html or named html are treated as HTML-safe.
Example locale file:
en:
welcome: "<b>welcome!</b>"
hello_html: "<b>hello!</b>"
title:
html: "<b>title!</b>"
Example view:
<div><%= t('welcome') %></div>
<div><%= raw t('welcome') %></div>
<div><%= t('hello_html') %></div>
<div><%= t('title.html') %></div>
ActiveRecord translations
Rails supports model and attribute translations:
en:
activerecord:
models:
user: Dude
attributes:
user:
login: "Handle"
Usage:
User.model_name.human
# => "Dude"
User.human_attribute_name("login")
# => "Handle"
Error message translation lookup
Rails searches multiple namespaces for validation messages:
activerecord.errors.models.[model].attributes.[attribute]activerecord.errors.models.[model]activerecord.errors.messageserrors.attributes.[attribute]errors.messages
Error message interpolation
Rails supports interpolation variables like:
"Please fill in your %{attribute}"
Action Mailer translations
If no subject is provided, Rails looks it up:
class UserMailer < ActionMailer::Base
def welcome(user)
end
end
Locale file:
en:
user_mailer:
welcome:
subject: "Welcome to LingoHub!"
Internationalization for Sinatra with the i18n gem
Internationalization in Sinatra with the i18n gem
SINATRA can be easily set up to use the i18n gem for internationalization.
Basic setup
require 'i18n'
require 'i18n/backend/fallbacks'
configure do
I18n::Backend::Simple.send(:include, I18n::Backend::Fallbacks)
I18n.load_path = Dir[File.join(settings.root, 'locales', '*.yml')]
I18n.backend.load_translations
end
Passing the locale
As previously described for Ruby on Rails, there are several ways to pass locale information.
1. Locale via URL path
before '/:locale/*' do
I18n.locale = params[:locale]
request.path_info = '/' + params[:splat][0]
end
2. Locale via subdomain
before do
if (locale = request.host.split('.')[0]) != 'www'
I18n.locale = locale
end
end
3. Locale via browser preference (Rack middleware)
Requires rack-contrib:
use Rack::Locale
Using translation helpers
Once the i18n gem is configured and locale is set, you can use:
I18n.tfor translationsI18n.lfor localization
To avoid repeating I18n. everywhere, define helpers:
helpers do
def t(*args)
I18n.t(*args)
end
def l(*args)
I18n.l(*args)
end
end
Localized template rendering in Sinatra
To support localized views, override find_template so templates match the user’s locale.
Templates are typically named like:
index.en.erbindex.de.erb
helpers do
def find_template(views, name, engine, &block)
I18n.fallbacks[I18n.locale].each do |locale|
super(views, "#{name}.#{locale}", engine, &block)
end
super(views, name, engine, &block)
end
end
Internationalization for Padrino with i18n gem
i18n in Padrino
The i18n gem is used as the default internationalization solution for the Padrino framework. By default, Padrino will search for all .yml or .rb files located in app/locale.
Localization is fully supported in:
padrino-core (date formats, time formats, etc.)
padrino-admin (admin language, ORM fields, ORM errors, etc.)
padrino-helpers (currency, percentage, precision, duration, etc.)
Supported languages in Padrino
Padrino has been localized into the following languages:
Czech, Danish, German, English, Spanish, French, Italian, Dutch, Norwegian, Russian, Polish, Brazilian Portuguese, Turkish, Ukrainian, Traditional Chinese, Simplified Chinese, Japanese.
Translation approach
Translation and localization in Padrino work similarly to the approaches described in previous sections.
Setting the default locale
You can set the default locale in config/boot.rb:
Padrino.before_load do
I18n.locale = :en
end
Summary
The i18n gem is simple to use yet very powerful, and it provides most functionality developers need. Because of this, it has become the most widely used internationalization solution in the Ruby ecosystem.
In most cases, i18n works practically “out of the box”. It is easy to set up and use, and once configured, the main remaining requirement is a reliable localization service.
Conclusion
In this article, we covered the basic usage of the i18n gem. The next article in the Ruby internationalization series will explore more advanced use cases.
Sources
Further reading
Related articles

i18n gem advanced features - Ruby on Rails internationalization
Find inside this article the highlighting of some i18n gem advanced features in Ruby on Rails internationalization.
Internationalization for Ruby with the r18n gem
In this article, we'll explore using the r18n gem as an alternative to i18n for internationalization in Ruby. Want to get more information? Click and find it in our blog.
Ruby and Encoding
In this article, we'll discuss how Ruby 1.9 manages encoding internally and the tools it provides to handle encoding issues. Looking to get more info? Visit LingoHub blog
