Foolproof I18n Setup in Rails

Gabe Berke-Williams

Let’s make I18n on Rails better, quickly and easily. These tips have helped us here at thoughtbot and caught some easy-to-fix but hard-to-track-down mistakes.

Raise an exception on missing translations

When a translation is missing, Rails will fall back to a default translation. For example the code t(:hello) will output "hello" if there is no provided translation for :hello. This is almost certainly not what you want, especially for more complicated I18n keys like t('users.sign_in').

Most versions of Rails include a way to raise exceptions when a translation is missing. Raising an exception ensures that all of your calls to t() are using your copy, instead of using a default string. I recommend raising exceptions in the test and development environments, so that you can find missing translations by running tests and by browsing around on localhost.

To raise exceptions on missing translations in Rails 4.1.0 and higher:

# config/environments/test.rb
# and
# config/environments/development.rb
Rails.application.configure do |config|
  config.action_view.raise_on_missing_translations = true
end

And in Rails 3:

# config/initializers/i18n.rb
if Rails.env.development? || Rails.env.test?
  I18n.exception_handler = lambda do |exception, locale, key, options|
    raise "Missing translation: #{key}"
  end
end

If you’re using a version of Rails 4 between 4.0.0 and 4.1.0, it requires a monkey patch, from Henrik Nyh:

# config/initializers/i18n.rb
if Rails.env.test? || Rails.env.development?
  module ActionView::Helpers::TranslationHelper
    def t_with_raise(*args)
      value = t_without_raise(*args)

      if value.to_s.match(/title="translation missing: (.+)"/)
        raise "Translation missing: #{$1}"
      else
        value
      end
    end
    alias_method :translate_with_raise, :t_with_raise

    alias_method_chain :t, :raise
    alias_method_chain :translate, :raise
  end
end

Time’s a-Wasting

Using I18n.t in tests is more typing than you really need. Here’s how you can use t() instead of I18n.t() in all of your tests:

RSpec.configure do |config|
  config.include AbstractController::Translation
end

Excellent.

What’s next

Read about better tests through internationalization. You can also use the i18n-tasks gem to find and manage missing and unused translations in your application.