Engineering May 6, 2019 4 min read

Organize your translations using multiple locales files

Photo by Kyle Glenn on Unsplash

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.

Code
I18n.locale = :ptI18n.default_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 bit better.

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:

Code
|-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:

Code
# config/application.rbconfig.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:

Code
|-pokemons
|--models
|---en.yml
|---pt.yml
|--views
|---en.yml
|---pt.yml
|-moves
|--models
|---en.yml
|---pt.yml
|--...

Or you can do something like this:

Code
|-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:

Code
# config/locales/models/en.ymlen:
 models:
  pokemon:
   name: Name
   type: Type
   moves: Moves
...# config/locales/models/pt.ymlpt:
 models:
  pokemon:
   name: Nome
   type: Tipo
   moves: Ataques
...# config/locales/views/homepage/pt.ymlpt:
 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!