---
title: Better Tests Through Internationalization
teaser:
tags: web,rails,testing
author: Derek Prior
published_on: 2013-10-26
---

Internationalization (i18n) is the process of adapting computer software to
different languages. In Rails, this means extracting all strings from you views
and controllers and placing them (by default) into YAML files that live in
`config/locales`. So why would you want to bother with this if your
site is only available in English?

The oft-cited reason for turning to i18n before there's even a hint of needing
to support additional languages is that it's simply easier to do from the
start. This is absolutely true but there are several other benefits to i18n
that make it a worthwhile pursuit and something we suggest for all applications.
Today we’re going to focus on the effect i18n has on your tests.

## i18n Makes Your Tests More Resilient

Consider the very basic feature spec and view below.

`spec/features/visitor_signs_in_spec.rb`:

```ruby
feature 'user views dashboard' do
  scenario 'sees welcome message' do
    user = create(:user)
    visit dashboard_path(as: user)

    expect(page).to have_content "Welcome, #{user.name}."
  end
end
```

`spec/views/dashboards/show.html.erb`:

```erb
Welcome, <%= user.name %>.
```

One day the client decides that the welcome message is stale and wants to
replace it with, "We're so happy to see you again, #{user.name}." Just this
very simple request in this completely contrived example is going to result in
two code changes in two files. Let's take a look at how this change would have
affected our application if we had been using i18n from the start:

`spec/features/visitor_signs_in_spec.rb`:

```ruby
feature 'user views dashboard' do
  scenario 'sees welcome message' do
    user = create(:user)
    visit dashboard_path(as: user)

    expect(page).to have_content welcome_message_for(user)
  end

  def welcome_message_for(user)
    t('dashboards.show.welcome', user: user)
  end
end
```

`spec/views/dashboards/show.html.erb`:

```erb
<%= t('.welcome', user: user) %>
```

`config/locales/en.yml`:

```yaml
dashboards:
  show:
    welcome: "Welcome, %{user.name}"
```

Now the requested change is localized to a single line in
`config/locales/en.yml`. Our specs no longer depend on copy that can change
regularly but on i18n keys which change far less frequently.  Following the
common Rails convention, the key will only change if the name or path of the
view changes.

## Tips for Getting Started

Familiarize yourself with the [Rails Internationalization API Guide][guide].
Rails has great support for i18n throughout. You can review the
[default locale files][default] to see the keys that power Rails’ validations
and helpers.

[guide]: http://guides.rubyonrails.org/i18n.html
[default]: https://github.com/svenfuchs/rails-i18n/blob/master/rails/locale/en.yml

* If you use Vim, you'll likely find the
  [`vim-i18n`](http://github.com/stefanoverna/vim-i18n) plugin useful when
  extracting translations from your views and controllers.
* As you go through the process of extracting translations at some point you are
  likely to add a key, but not the corresponding translation. Rails will fall
  back to a reasonable string based on the key, but without manual inspection of
  the site, you’ll have no way of knowing the key is missing. You can make
  missing translations fail your tests by adding the following to an
  initializer:

          if Rails.env.development? || Rails.env.test?
            I18n.exception_handler = lambda do |exception, locale, key, options|
              raise "missing translation: #{key}"
            end
          end

* Put some level of effort into keeping your i18n file organized alphabetically.
  The gem [`i18n_yaml_sorter`](https://github.com/redealumni/i18n_yaml_sorter)
  can do this for you automatically. Without any organization it's easy to
  introduce namespace collisions which have the unfortunate effect of clobbering
  pre-existing keys in YAML.
* An engine such as [Tolk](https://github.com/tolk/tolk) or a service like
  [Locale](http://www.localeapp.com/) can move the process of updating text away
  from git and YAML and into a database-backed web application to expose it to
  more people.

If you’re not using i18n in your app today, the process of getting started can
be daunting. Using i18n as a means to improve your specs as you add and refactor
features is a good way to slowly internationalize your application while seeing
immediate benefit. I heartily recommend i18n for even the smallest of
internal-only web applications.

Do you have another interesting use for the Rails i18n framework? Let us know
by tweeting [@thoughtbot][twitter].

[twitter]: https://twitter.com/thoughtbot
