I’ve written before about Working with Locales and Time Zones in Rails, but I often feel the i18n library (short for internationalisation) is underused (appreciated?). Perhaps it is even avoided because of the perception it is more effort to develop with and harder to maintain.
This article will, I hope, open your mind to the idea that you will be better off using i18n in your application (even for a single language) and that it can be maintainable with some simple organisational pointers.
The number of i8n keys that your application accumulates can become
overwhelming as your application develops. In my experience, the biggest pain
point has been finding the key(s) you’re looking to update if everything is in
one huge file such as the default
Thankfully, you are not restricted to using a single file. We can adjust the
load_path in Rails and break up our translation files into more
logically grouped files.
Rails doesn’t care or place special significance on this structure, all it cares about is the key hierarchy it eventually stores after parsing and merging the resulting data structure.
Now we organise our locale files to mirror the structure of our
directory. So for every view we can have a corresponding locale file, for
1 2 3 4 5 6 7 8 9 10 11
This pairing applies even to partials; in fact I prefix them with an underscore as well. Now I can easily find the translations for any view.
Tip: When you add a new locale file you will need to restart your Rails server as it will not be loaded automatically.
The “Lazy” lookup
To compliment our new directory structure we can make a hierarchy in our locale file the same as the path to the view and use a more convenient way to to look up locales.
This removes the need for you to think up a hierarchy yourself and instead take advantage of a convention everyone can understand/lookup in documentation.
1 2 3 4 5
The same pattern works for partials too. For example:
1 2 3 4 5
What if you have some translations that need to be “global” to your application and don’t fit in a particular view or class, perhaps they are changeable and appear in many locations so having them repeated would be inconvenient.
The idea can be carefully applied at a global level too when it’s needed.
1 2 3
If you’ve only got a few keys then a single file is fine, if you start finding
the file hard to read then break it up into smaller files inside a
directory or whatever makes sense for your domain.
Using i18n outside of views
This approach is not limited to views, I find it really useful to use for validation messages in form object and models as well. For example:
1 2 3 4 5 6 7
Given a simple form object with basic presence validation as an example, the locale file below shows how you might customise the validation message. This makes use of Rails “lazy” lookup in a similar way to views and will affect this form only.
Global error messages can be set as well, check out the rails-i18n gem for an exhaustive list of the defaults you can change in Rails.
1 2 3 4 5 6 7 8 9
Taking care to create our locale file in a corresponding location to our class makes it easy to find these translations in the future.
Naming things is hard. So don’t over think it, with our file structure this key is already confined to a single view and locale file. If it turns out to be a poor choice you can confidently go and change it.
My recommendation for naming your keys is to choose a name after the purpose of the key and not just use an underscored version of your translation. One exception to this would be things like models attribute labels where it makes sense to just use the translation as the key too.
1 2 3 4 5 6 7
Hopefully in the example above you can see the point I am trying to make. This isn’t a rule; more of a guide to help you make a more meaningful choice early on.
Remove text from HAML and Slim templates
1 2 3 4 5 6 7 8 9 10
While the example above does not look like chaos it should be seen as a code “smell” in my opinion and one that’s easily solved with i18n:
1 2 3 4 5 6 7
That’s better! You might notice I call
#each on the
this is so I can quickly mention namespace lookups: if you call a
key with children it will return a
Hash with the translation as the value so
it can used to create the list.
.body_html is considered a HTML safe translation so
Rails won’t escape the
In my opinion this keeps your focus on good structure without the noise and distraction of interpolated strings. This can be used for ERb templates too.
Bonus level: Pluralization
I just wanted to share one of my favourite uses for i18n, handling pluralization. Not simply changing a singular form to a plural, I’m talking about adapting the entire sentence based on the count. For example, given the following locale file:
1 2 3 4 5 6
You can probably see what’s going on just from that example; depending on the
count you pass to the translation it will select the appropriate response.
It should be noted that you don’t need to include all those options. You might not need to include a special case for
zero in which case it will fallback to
This can help reduce a lot of unnecessary code in your view, not to mention the benefit of providing users with a better message.
I suggest taking a look at the documentation for i18n in Rails to learn more about its features and creative uses. This article is more about how to better manage your locales and really only scratches the surface of what you can do with i18n.