Of course, this contains an example with Pokémon!
Internationalization of a website's content is always something to have in mind. Although many countries have a second language, not everyone knows how to speak or write it. In my country, Portugal, only the latest couple of generations understand English, at least in a way that they can use it on a daily basis. Some older generations didn’t even know how to read and write in Portuguese; illiteracy in our country was a major concern.
Because of these type of things, a website (in my opinion) should have at least 2 languages a user can select from, or enough languages to reach as many users as it can.
Hard-coded text in an application is somewhat of a bad practice, but in a Rails application we can find inside the configs/locales a file with the name en.yml. This file is a place where we can set strings that may appear in different places, instead of hard coding them. But, they don’t need to be only in English. You can create other translations files for different languages for instance: fr.yml and es.yml (French and Spanish, respectively).
To change which locale you are using, you can set I18n.locale with the language code, or even set the default language with I18n.default_locale.
I18n.locale = :pt
I18n.deafaul\_locale = :es
Okay but, now imagine this case: you are a Pokémon fan like me and you would like to make a website with all the information about them (names, types, moves…). Yes, you could save everything in a database, but that’s not the point, I don’t want you to be that smart.
Start by adding info about the first 151 inside en.yml… It is already a lot of information, so you’ll have a very big file. OK, now create a locales file for Portuguese, pt.yml. Now you have all the info for a Portuguese native to read.
I said to start with the first 151… Now add the ones from the second generation and you get info about 251 Pokémon. Oh, but in generation two, the generation one Pokémon can learn new moves, so you’ll have to update that info going through the whole file.
This is a big edge case but I think you already know where I want to go with this… In the future, changing a locale file could be hard to manage if you don’t structure it right from the beginning.
If only we could organize them a little bitbetter.
Well, you can! Rails lets you organize your translations in a way that’s much more suitable to your project. You can create multiple locales files for the same language and spread them into different folders, in a structure that makes sense to you or the project scope. This example was taken from the Ruby on Rails Guides:
|-defaults
|---es.rb
|---en.rb
|-models
|---book
|-----es.rb
|-----en.rb
|-views
|---defaults
|-----es.rb
|-----en.rb
|---books
|-----es.rb
|-----en.rb
|---users
|-----es.rb
|-----en.rb
|---navigation
|-----es.rb
|-----en.rb
(The file's extension is in ruby but you can use _yml _inside.)
To use a structure like this, you need to let Rails know where to look for the files:
# config/application.rb
config.i18n.load\_path += Dir[Rails.root.join(_'config'_, _'locales'_, _'\*\*'_, _'\*.{rb,yml}'_)]
Going back to our example, we can now think of a possible structure to organize our Pokémon’s information. Here’s an example:
|-pokemons
|--models
|---en.yml
|---pt.yml
|--views
|---en.yml
|---pt.yml
|-moves
|--models
|---en.yml
|---pt.yml
|--...
Or you can do something like this:
|-models
|--pokemons
|---en.yml
|---pt.yml
|--moves
|---en.yml
|---pt.yml
|--...
|-views
|--homepage
|---en.yml
|---pt.yml
|--...
And then, each file will be something like this:
# config/locales/models/en.yml
en:
models:
pokemon:
name: Name
type: Type
moves: Moves
...
# config/locales/models/pt.yml
pt:
models:
pokemon:
name: Nome
type: Tipo
moves: Ataques
...
# config/locales/views/homepage/pt.yml
pt:
views:
homepage:
title: Pokémon e Companhia
main\_section: Vou apanhá-los todos!
...
Always scope the translations by the name of the folder, to avoid overrides.
With a structure like this, it is now easier for you or any developer on your project to add, change or remove a translation for a specific language. This is great if you need to refactor some code and delete some legacy references in the project. Trust me, refactoring a big and unique translations file is not a pleasant task at all.
Pokémon © 2002–2018 Pokémon. © 1995–2018 Nintendo/Creatures Inc./GAME FREAK inc. TM, ® and Pokémon character names are trademarks of Nintendo.
No copyright or trademark infringement is intended in using Pokémon content on this post.
I’m currently a Ruby-on-Rails developer at Runtime Revolution that still likes to play the good old Pokemon games for the GameBoy. Go check our website to know us more. Oh, and we’re hiring!
Top comments (2)
We use Localeapp.com for a very large Rails project. It has CLI and the workflow is pretty simple, but no matter what tool you use it always comes back to bite you. Rails documentation about locales suggests a very bad approach for locales organization and leads to thousands of dollars in duplicated locales. Also, I wonder how this example especially the model scales.
If we have many models with
type
orname
attribute it will be duplicated in the locales. Scoping locales by folders/models/controllers/views is not so good idea in my opinion. For example if you have a dropdown menu in your app that has a "delete" button, you should not put the locales in a scoped view path. You will probably have a delete button in hundred places in your app.Just my few cents on the locales in 7+ years old Rails project with a lot of locales :D
Nice article, João! I used this approach in the past. But my problem was that our non-tech translators couldn't deal with the YAML files, and even if they could, it wasn't an efficient approach. So I've built LocaleData.com - a translation management platform for Rails. The side effect is that I no longer need to touch directly the YAML files. I'm using the web interface to manage the translations and a command line tool to download them into a Rails app. I've successfully used it for all my projects since then :).