When I wrote a Phoenix app for the first time, I quickly asked myself how to organize my translation files. By default, all translations goes into priv/gettext/[LOCALE]/LC_MESSAGES/default.po and it can easily become a big mess. So I quickly started to search for a way to split my translations by domains. It’s not a thing that is explained in the (really good) Phoenix guides.

Under the hood there’s gettext

It took me some time to figure out how to do it the right way and that’s mainly because I was searching through the Phoenix documentation when the real answer was available in the underlying lib that handle translations gettext and its Elixir bridge.

Gettext is used for translation since forever in free software world. It’s a battle tested library that has everything you need when it comes to translations. It can do simple translations, handle plural translations and domain-based translations.

Coming from Ruby and Rails world I’m really used to using ecosystem specific solution (a.k.a i18n gem and YAML files) to handle translation.

To be honest, when I started using Rails, I was wondering why the community wasn’t using gettext since it was the most standard i18n library I was aware of. It was used everywhere.

When I saw that Phoenix was using gettext I got a mixed feeling of “OMG back to this old lib” and “Yeah this good old gettext!”.

After using it a little bit I really quickly told to myself “why do we reinvent the wheel when there’s something that good out there”.

The format is easy to learn, there’s a bunch of tools out there to translate strings.

Basic usage of gettext in Phoenix

gettext "Title" searches for a key (msgid) named Title in the default namespace. If you don’t specify anything it will use the default namespace.

So in your .eex file you’ll get something like:

<tr>
  <th colspan="2"><%= gettext "Status" %></th>
  <th><%= gettext "Title" %></th>
  <th><%= gettext "Brand" %></th>
  <th><%= gettext "Description" %></th>
</tr>

where gettext will search in the default namespace (default.po) for msgid Status, Title, Brand, and Description. If no translation is found for the current language then the string will be used as is.

common translation file priv/gettext/default.pot then a file per locale such as priv/gettext/fr/LC_MESSAGES/default.po

Custom domain

I’m pretty ashamed of having searched so much to find out how to use custom domains / files for translations when the answer was pretty much in the dgettext function.

 Gettext.dgettext(Api.Gettext, "additionals", ad.status)

Final words

IRB, and by extension Rails console, is a real gem and can help you a lot everyday. You really should try to take the best out of it. Maybe my next blog post will be the same king of tips but for Iex which is the same kind of tool but for Elixir

Leave a Comment