Developing a good web app involves to implement an efficient internationalization system. You have to provide the same content in multiple languages, so you need a module to translate all your texts.
This article will explain how to build a simple translation system (or i18n module) for all your application texts. I don’t consider other data like dates or phone numbers which are a huge mess but can be implemented in the same way as text.
Build the Singleton
Firstly we need to write a simple Singleton as explained in this article. I take the exact code from the end of the article, which is :
We can now call something like Translator.get('YES')
from anywhere in our code by importing the module.
Analyze logic behind the translation
Before implementing the translation module, we have to think about the features of the module.
Here are some examples of features I want :
Input | English | French |
---|---|---|
('HELLO') | Hello | Bonjour |
('HELLO', 'Florian') | Hello Florian | Bonjour Florian |
('YOU_HAVE_N_MESSAGES', 9) | You have 9 messages | Vous avez 9 messages |
('YOU_HAVE_N_MESSAGES', 1) | You have 1 message | Vous avez 1 message |
So I need a module that can take some parameters to include them in the translated texts. I also need something to configure translations according to the parameter (for example, to create a singular or a plural word).
I’ll use a particular combination of characters to determine where I must replace the texts with parameters. This combination is $-
, and we replace each presence of this combination with the argument of Translator.get
.
Let’s write i18n files for English and French:
We define JSON objects where each key can contain a string or an object with keys :
"null"
for no argument"0"
/"1"
/"2"
/"test"
/ … to define a specific case for the argument"N"
for all cases not defined for argument
Implementation of the get() function
It’s pretty simple to add the argument in the translation. We use a regex within the String.replace
function, and we already finished this part.
Now, we have to get the translation and use the good case according to the argument. First, get the translation and use it if this is a string. If we can’t find any translation, return the text.
If it isn’t a string, we assume it’s an object. So we have to check if there’s an argument. If not, use the null
entry of the translation. We also added a check if we have to set a null
translation, but there’s no null transaction.
Now, there’s an argument. Let’s check if there’s a translation entry with this value and use it if it exists. We cast the argument explicitly to a String because it’s nice to respect typing.
Last but not least, we are in the N
case. So get the N
translation and insert the argument into it.
We also add a setLanguage
to the Translator to … set the language. Yeah, not very original.
That’s it. We have a simple i18n module for all of our texts.
All the Translator code is available in this gist.
I hope you found this short article pleasant. Don’t hesitate to tell me what you have thought of this one on Twitter. I’ll be happy to discuss it with you :)