The Advanced Guide to jQuery i18n

We are going to thoroughly discuss jQuery I18n's features as well as see it in action by creating a demo JavaScript application.

jQuery i18n

For the past months I have written a bunch of articles about internationalization covering the usage of I18n in Rails apps as well as various solutions for JavaScript. Out today’s guest is the jQuery I18n library that was briefly covered in one of the previous posts. This solution supports gender, grammar forms, dynamic change of language, fallback chains and more.

We are going to thoroughly discuss jQuery I18n’s features as well as see it in action by creating a demo JavaScript application. Here is a quick overview of the themes that we’ll cover:

  • Basic information about jQuery I18n
  • Setting up demo application and loading the necessary files
  • The process of creating translation files
  • Loading translation files and some gotchas to take into consideration
  • Switching locale
  • Persisting locale data by using History API
  • Displaying localized messages based on HTML5 data- attributes
  • Displaying localized messages using custom JavaScript code
  • Employing gender and pluralization info in translations
  • Prettifying the code to avoid repeating ourselves
  • Adding “magic” words
  • Documenting your translations

The source code for this article is available at GitHub.

A Quick Note About the Demo App

To be able to see the final result in action you’ll need to setup a web server. I am going to use Microsoft IIS but there are plenty of other solutions available that can get you up and running in a minute:

About jQuery I18n

Of course you know what Wikipedia is and probably have used this website many times, as it has knowledge about anything starting from physics and chemistry to popular (and less popular) films and computer games. Wikipedia is maintained by a company called Wikimedia Foundation, that also takes care of projects like Wikinews, Wikivoyage and many others. All these projects are worldwide and available in different languages so internationalization is absolutely crucial for Wikimedia.

Therefore this company created a popular jQuery-based solution to internationalize JavaScript applications and called it simply jQuery I18n. Not a very fancy name, but believe me there is more than meets the eye – this library is really convenient and powerful, so let’s not waste our time and discuss it’s features now. To make our journey more interesting and useful, we are going to apply the learned concepts into practice.

Translation Files

jQuery.I18n stores translations in the simple JSON files. If you are, like me, a fan of Rails, you’ll find this process very similar to storing translations in YAML files.

The actual translations are in key-value format, for example

title is, of course, the key, whereas Example Application is its value. It is advised to name keys in lowercase with words separated by -. jQuery.I18n’s docs also suggests to prefix your keys with an application’s name

but that’s not mandatory.

Apart from translations, files may host metadata, like information about authors, last updated date etc. Metadata has its own key starting with @:

Usually translation files are being placed into the i18n directory. Translations for different languages are often stored separately in the files named after the language, for example en.json, de.json, ru.json and so on. However, for a simple application you may put everything in a single file. In this case translations should be placed under the key named after the language:

Moreover, you may provide a path to the file instead of an object with translations:

To start crafting our demo application, create a new folder with a nested js/i18n directory. Place translation files inside for the languages you prefer. I am going to work with English and Russian in this article:

  • js/i18n/en.json
  • js/i18n/ru.json

Populate your files with some basic contents:

js/i18n/en.json

js/i18n/ru.json

You might need to setup a proper MIME type for the .json extension in your server config.

Also in order to prepare for the next steps clone jQuery I18n locally along with its dependencies:

Inside your project’s directory create js/lib/jquery.i18n folder and copy the contents of the src folder from the cloned project inside (without the nested languages directory). Next inside the js/lib create another directory CLDRPluralRuleParser. Copy the libs/CLDRPluralRuleParser/src/CLDRPluralRuleParser.js file from the cloned project there.

Lastly inside the js create a file called global.js where we will put our custom code. As a result, your project should have the following structure:

Now in the root of the project create a simple HTML file with the necessary libraries hooked up in the proper order:

demo.html

I am using jQuery 3 here but you may choose version 1 or 2 depending on the browsers you wish to support (version 3 works only with modern browsers, so no IE7 for you).

So far so good. We can proceed to the next section and load our translation into the application.

Loading Translations

The next important step is of course loading your translations. In the simplest case you would just provide the key-value pairs directly in the code:

Or for a specific locale:

For complex apps, however, that’s not the best choice. Instead I’d recommend specifying a remote URL like this:

Do note that you may load messages in parts, meaning that this code

is totally valid. Just make sure you don’t overwrite any existing keys.

Let’s load translations into the demo app now:

js/global.js

Another important thing is that the load function returns a jQuery Promise making it easy to perform some action only after the translations are loaded successfully, for example:

Now that our translations is loaded, we can introduce a mechanism to switch locales.

Switching Locale

Which locale is going to be set as a default upon the page load? First of all, the default locale can be provided as an option passed to the $.i18n:

Also jQuery.I18n will check the value of the lang attribute set for the html tag. Let’s add it now:

demo.html

If the default locale was not set in any of these two ways, the library will try to get the language setting passed by the browser. Internally the default language is set to English. To avoid any unexpected behaviour it is recommended to set the default locale explicitly.

Of course it is possible to switch the chosen locale later by modifying the locale option:

Let’s add links to switch locale now:

demo.html

Handle the click event by preventing the default action and switching locale based on the data-locale attribute:

js/global.js

Note that I am putting this event handler inside the promise to make sure that the translations are loaded first.

Persisting Locale

Another thing that we are going to do is persist the chosen locale by appending it to the URL in a form of a GET param. So, for example, this URL http://localhost/demo.html?locale=ru is going to request the Russian version of the website. It is very important to do so because when a user shares a link he expects that other people will see the same version of the site.

In order to achieve this, however, we’ll need two additional libraries. The first one is History API that supports HTML5 History/State APIs. I am going to take the bundled html4+html5 version for jQuery.

The second is url library that makes working with URLs in JavaScript a breeze. Grab the minified version.

Hook up these two libraries now:

demo.html

Now code a simple function to change the locale:

js/global.js

It should be called once the page is loaded and translations are done. The locale itself should be fetched from the ?locale parameter:

js/global.js

url('?locale') takes the value of the locale GET parameter. Also we need to listen for the statechange event (that happens, for example, when the pushState method was invoked) and change the locale accordingly:

js/global.js

And lastly use pushState once a link to switch the language was clicked. Here is the final version of the script:

js/global.js

Even if the locale parameter is missing, we still have the lang="en" set for the html tag.

Displaying Translations with Data API

The easiest way to display a localized content is by using data- attributes. All you need to do is provide a data-i18n attribute and set the translation’s key as its value. For example, let’s display our site name:

demo.html

app-title, as you recall, was defined inside the en.json file as "app-title": "Example Application" and in ru.json as "app-title": "Тестовое приложение".

To actually display the message use the i18n() method without any arguments. This method should be applied to the exact element or to its parent. We can simply say body:

js/global.js

In order to provide a fallback text that will be displayed while translations are being loaded, simply place it into the tag:

demo.html

Now reload your page and observe result!

Parameters, gender and pluralization in translations

In the previous section we’ve seen the simplest example of displaying localized messages by using data-i18n parameter. But obviously in many cases that’s not enough. To fetch translation for an arbitrary key, you can use the following code

This way we can, for example, display a welcoming message (with a fallback text):

demo.html

Add translations:

en.json

ru.json

and tweak the code:

global.js

Pretty nice, but it would be better to display a user’s name as well. In order to do this, we need to add parameter inside the message. Parameters in jQuery.I18n are being named $1, $2, $3 etc:

en.json

ru.json

To set parameter’s value, simply pass another argument to the $.i18n:

global.js

If you had two parameters, you would of course pass two arguments to the method and the first one will be assigned to the $1 variable, whereas second – to the $2.

Now what about the gender info? Suppose, for example, we want to display a bunch of e-mails from different people with a header “You received a new letter from Someone. He/She says:”. First of all, add a new markup:

demo.html

Now translations:

en.json

ru.json

So GENDER: acts as a switch. It receives parameter and chooses one of two options: the first is picked for the male gender, whereas the second – for the female. To be able to use this message, provide male or female as the second argument:

global.js

Also let’s display how many letters does the user have. For this we’ll need the PLURAL: switch.

demo.html

The messages:

en.json

ru.json

Russian language has more complex pluralization rules so I had to provide additional option for the PLURAL: switch. Now the code:

global.js

All information about gender and pluralization is stored inside the jquery.i18n.language.js file.

Prettifying the Code

Everything is working pretty nice, but things are becoming tedious. For each separate translation I have to write another line of code which is not particularly great. What I want to do is write a cycle that takes each element requiring translation and displays a message inside based on its parameters.

As a first step, modify the markup:

demo.html

Now for each element with the .translate class we need to take the value of the data-args, split it into an array and pass to the $.i18n method. The only problem is that we don’t know how many arguments are going to be passed. That’s not a problem however with the apply method:

apply accepts two arguments: the first is the value of this inside the called function and the second is an array representing all the arguments to pass.

Now you may reload the page and observe the result. Of course this solution is not ideal but you can extend it further as needed.

Magic Words

PLURAL: and GENDER: are both “magic” words the library supports. You can, however, introduce new magic words as needed by extending the $.i18n.parser.emitter:

global.js

So, the sitename is the magic word’s name and the corresponding functions returns its value. To fetch this value, use template-like syntax (similar to what Handlebars use): {{SITENAME}} or {{sitename}}. : is not needed because sitename does not accept any arguments.

Now you may use this magic word in your translations:

en.json

ru.json

The markup:

demo.html

The magic words can be more complex. This one, for example, is going to construct a link with the specified title and URL:

global.js

nodes is an array of arguments. Use this magic words just like gender: or plural:

en.json

ru.json

Note that magic words can be nested, like shown in this example.

Documenting Your Translations

Localizing application is hard. In many cases simply translating messages is not enough as you need to know their context. Therefore jQuery I18n’s translations can be documented. Usually it is done inside a file called qqq.json. Inside you provide translations keys and their descriptions. For example:

i18n/qqq.json

Having such file in place is really useful.

PhraseApp and Translation Files

Working with translation files is hard, especially when the app is big and supports many languages. You might easily miss some translations for a specific language which will lead to user’s confusion. And so PhraseApp can make your life easier!

Grab your 14-days trial. PhraseApp supports many different languages and frameworks, including JavaScript of course. It allows to easily import and export translations data. What’s cool, you can quickly understand which translation keys are missing because it’s easy to lose track when working with many languages in big applications. On top of that, you can collaborate with translators as it’s much better to have professionally done localization for your website.

Conclusion

In this article we’ve seen jQuery i18n library by Wikimedia Foundation in action. We discussed all of its main features and hopefully by now you feel more confident about using it. Of course, there are other similar (and not so similar) solutions available so you might want to try them as well.

I thank you for staying with me and see you soon!


Also published on Medium.

Comments