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
:
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
:
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
:
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
:
<%= t('.welcome', user: user) %>
config/locales/en.yml
:
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. Rails has great support for i18n throughout. You can review the default locale files to see the keys that power Rails’ validations and helpers.
- If you use Vim, you’ll likely find the
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
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 or a service like Locale 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.