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 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
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.
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 quickly told to myself “why do we reinvent the wheel when there’s something that good out there?”.
The format is easy to learn and there’s a bunch of tools available to translate strings.
Basic usage of gettext in Phoenix
gettext "Title" searches for a key (msgid) named
Title in the
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
Description. If no translation
is found for the current language then the string will be used as is.
To handle translation you have files with two extensions. The first
priv/gettext/default.pot), one per domain, which
is generated by scanning the app code and lists all keys. You then
.po file per locale / domain (e.g.
priv/gettext/fr/LC_MESSAGES/default.po), this is where you’ll
actually put the translations.
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
Gettext.dgettext(Api.Gettext, "additionals", "In progress")
The first argument is the backend used for translation. In a typical
Phoenix app it’ll be
The second argument is the domain is which gettext will search. In our
example it means that the translations will be in
priv/gettext/[locale]/LC_MESSAGES/additionals.po files. Yes the
domain is determined by the file name.
The third and last argument is the
msgid or in other words the key
and untranslated text.
I think that if your app becomes big enough, it’s a good strategy to divide your translations in multiple contextualized files to avoid collisions and ease translation process.
Hope you’ll find this useful.