Guide to translate your Ruby on Rails applications

This guide explains how to install our gem and configure your project, then shows you how to synchronize your keys and translations, and presents the syntaxes you can use in your code.

— In a nutshell —

  • Our Ruby gem is made to help you localize and translate your Rails applications, with minimum effort.
  • You can use both the official Rails syntax (with YAML files) and the GetText syntax.

Need help or have a question?


  1. Add the gem to your project’s Gemfile:
gem 'translation'
  1. Create a new translation project from the UI.
  2. Copy the initializer into your Rails app (config/initializers/translation.rb)

The initializer looks like this:

TranslationIO.configure do |config|
  config.api_key        = 'abcdefghijklmnopqrstuvwxyz012345'
  config.source_locale  = 'en'
  config.target_locales = ['fr', 'nl', 'de', 'es']
  1. Initialize your project and push existing translations to with:
bundle exec rake translation:init

If you need to add or remove languages in the future, please read our documentation about that.



To send new translatable keys/strings and get new translations from, simply run:

bundle exec rake translation:sync

Sync and Show Purgeable

If you need to find out what are the unused keys/strings from, using the current branch as reference:

bundle exec rake translation:sync_and_show_purgeable

As the name says, this operation will also perform a sync at the same time.

Sync and Purge

If you need to remove unused keys/strings from, using the current branch as reference:

bundle exec rake translation:sync_and_purge

As the name says, this operation will also perform a sync at the same time.

Warning: all keys that are not present in the current local branch will be permanently deleted from

Localization syntaxes

I18n (YAML)

The default Rails Internationalization API.

# Regular

# Pluralization
t('inbox.message', count: n)

# Interpolation
t('inbox.hello', name:

With the source YAML file:

    title:   'Title to be translated'
      zero:  'no messages'
      one:   'one message'
      other: '%{count} messages'
    hello:   'Hello %{name}'

You can keep your source YAML file automatically updated using i18n-tasks.


This gem adds the GetText support to Rails. We strongly suggest that you use GetText to translate your application since it allows an easier and more complete syntax.

Moreover, you won’t need to create and manage any YAML file since your code will be automatically scanned for any string to translate.

# Regular
_("Text to be translated")

# Pluralization
n_("Singular text", "Plural text", number)

# Regular with context
p_("context", "Text to be translated")

# Pluralization with context
np_("context", "Singular text", "Plural text", number)

# Interpolations
_('%{city1} is bigger than %{city2}') % { city1: "NYC", city2: "BXL" }

More information about GetText syntax here.

Manage Languages

Add or Remove Language

You can add or remove a language by updating config.target_locales = [] in your config/initializers/translation.rb file, and executing rake translation:sync.

If you want to add a new language with existing translations (ex. if you already have a translated YAML file in your project), you will need to create a new project on and run rake translation:init for them to appear.

Edit Language

To edit existing languages while keeping their translations (e.g. changing from en to en-US).

  1. Create a new project on with the correct languages.
  2. Adapt config/initializers/translation.rb (new API key and languages)
  3. Adapt language names and root keys of the YAML files in your project (optional: adapt GetText directories and .po headers)
  4. Execute rake translation:init and check that everything went fine.
  5. Invite your collaborators in the new project.
  6. Remove the old project.

Since you created a new project, the translation history and tags will unfortunately be lost.

Custom Languages

Custom languages are convenient if you want to customize translations for a specific customer or another instance of your application.

A custom language is always be derived from an existing language. Its structure should be like:


where custom_text can only contain alphabetic characters and -.

Examples: en-microsoft or fr-BE-custom.


If a translation is missing for a regional (fr-BE) or custom (fr-microsoft) language, then it will fallback to the main language (fr).

Locale fallbacks will work as expected with both I18n (YAML) and GetText syntaxes.

A good way to leverage this feature is to ignore sentences from a regional language that would have the same translation as the main language (usually most of them). It’s way easier to maintain the project over time if only 10% of the regional sentences need to be adapted.

Note that fallbacks are chained, so fr-BE-custom will fallback to fr-BE that will fallback to fr.

Just make sure to add config.i18n.fallbacks = true to your config/application.rb file. You can find more information about this here.

Change the current locale


The easiest way to change the current locale is with set_locale.

class ApplicationController < ActionController::Base
  before_action :set_locale


First time the user will connect, it will automatically set the locale extracted from the user’s browser HTTP_ACCEPT_LANGUAGE value, and keep it in the session between requests.

Update the current locale by redirecting the user to or even if you scoped your routes like this:

scope "/:locale", :constraints => { locale: /[a-z]{2}/ } do
  resources :pages

The set_locale code is here, feel free to override it with your own locale management.

Don’t forget to define your available locales with I18n.available_locales.

More examples here:


This command will change the locale for both I18n (YAML) and GetText:

I18n.locale = 'fr'

You can call it several times in the same page if you want to switch between languages.

More examples here:

Frontend Localization

Using this Gem

This gem is also able to cover frontend localization (React, Vue, …).

There are several ways to pass the translation strings from the backend to the frontend: JavaScript serialization, data- HTML attributes, JSON files etc.

The easiest strategy when dealing with React/Vue would be to pass the corresponding translations as props when mounting the components.

Assuming that you use reactjs/react-rails, it would look like this if you want to use I18n (YAML) syntax:

react_component("MyComponent", {
  :user_id =>,
  :i18n    => YAML.load_file("config/locales/#{I18n.locale}.yml")[I18n.locale.to_s]["my_component"]

Your en.yml should look like this:

    your_name: Your name
    title: Title

You can also directly use the GetText syntax:

react_component("MyComponent", {
  :user_id =>,
  :i18n => {
    :your_name => _('Your name'),
    :title     => _('Title')

In both case, in your React component, you can simply call this.props.i18n.yourName and your text will be localized with the current locale.


  • You can also structure the i18n props with multiple levels of depth and pass the subtree as props to each of your sub-components.
  • It also works great with server-side rendering of your components (:prerender => true).

Using our official React & JavaScript package

As is directly integrated in the great Lingui internationalization framework, you can also consider frontend localization as a completely different localization project.

Please read more about this on:

Continuous Integration

If you want fresh translations in your Continuous Integration workflow, you may find yourself calling bundle exec rake translation:sync very frequently.

Since this task can’t be concurrently executed (we have a mutex strategy with a queue but it returns an error under heavy load), we implemented this threadsafe readonly task:

bundle exec rake translation:sync_readonly

This task will prevent your CI from failing and still provide new translations. But be aware that it won’t send new keys from your code to so you still need to call bundle exec rake translation:sync at some point during development.

Advanced Configuration Options

The TranslationIO.configure block in config/initializers/translation.rb can take several optional configuration options.

Some options are described below but for an exhaustive list, please refer to config.rb.

Disable GetText or YAML

If you want to only use YAML files and totally ignore GetText syntax, use:

TranslationIO.configure do |config|
  config.disable_gettext = true

In contrast, if you only want to synchronize GetText files and leave the YAML files unchanged, use:

TranslationIO.configure do |config|
  config.disable_yaml = true

Ignored YAML keys

Sometimes you would like to ignore some YAML keys coming from gems or so.

You can use the ignored_key_prefixes array with a mix or strings and regular expressions.

For example:

TranslationIO.configure do |config|
  config.ignored_key_prefixes = [

Custom localization key prefixes

Rails YAML files contain not only translation strings but also localization values (integers, arrays, booleans) in the same place and that’s bad. For example: date formats, number separators, default currency or measure units, etc.

A translator is supposed to translate, not localize. That’s not his role to choose how you want your dates or numbers to be displayed, right? Moreover, this special keys often contain special constructions (e.g., with percent signs or spaces) that he might break.

We think localization is part of the configuration of the app and it should not reach the translator UI at all. That’s why these localization keys are detected and separated on a dedicated YAML file with

We automatically treat known localization keys, but if you would like to add some more, use the localization_key_prefixes option.

For example:

TranslationIO.configure do |config|
  config.localization_key_prefixes = ['']

Source file formats (for GetText)

If you are using GetText and you want to manage other file formats than:

  • rb, ruby and rabl for Ruby.
  • erb and inky for Ruby templating.
  • haml and mjmlhaml for HAML.
  • slim and mjmlslim for SLIM.

Just add them in your configuration file like this:

TranslationIO.configure do |config|
  config.source_formats      << 'rb2'
  config.erb_source_formats  << 'erb2'
  config.haml_source_formats << 'haml2'
  config.slim_source_formats << 'slim2'

Gems with GetText strings

Public gems usually don’t make use of GetText strings, but if you created and localized your own gems with the GetText syntax, you’ll want to be able to synchronize them:

TranslationIO.configure do |config|
  config.parsed_gems = ['your_gem_name']

You can specify where your GetText and YAML files are on disk:

TranslationIO.configure do |config|
  config.locales_path      = 'some/path' # defaults to config/locales/gettext
  config.yaml_locales_path = 'some/path' # defaults to config/locales

GetText Object Class Monkey-Patching

GetText methods (_(''), etc.) are available everywhere in your application. This is made by extending the global Object class.

You can disable the built-in Object monkey-patching if you prefer a more granular approach:

TranslationIO.configure do |config|
  config.gettext_object_delegate = false

Don’t forget to manually include the GetText methods where needed:

class Contact < ApplicationRecord
  extend TranslationIO::Proxy

Pure Ruby (without Rails)

This gem was created specifically for Rails, but you can also use it in a pure Ruby project by making some arrangements:

  require 'rubygems'
  require 'active_support/all'
  require 'yaml'

  class FakeConfig
    def after_initialize
    def development?

  module Rails
    class Railtie
      def self.rake_tasks

      def self.initializer(*args)

      def self.config

    def self.env

  task :environment do

  require 'translation'

  I18n.load_path += Dir[File.join('i18n', '**', '*.{yml,yaml}')]

  # Put your configuration here:
  TranslationIO.configure do |config|
    config.yaml_locales_path = 'i18n'
    config.api_key           = ''
    config.source_locale     = 'en'
    config.target_locales    = ['nl', 'de']
    config.metadata_path     = 'i18n/.translation_io'

(Thanks @kubaw for this snippet!)

Note: the documentation for this gem is also available on

Need help or have a question?