Software localization

What Every Developer Ought to Know About jQuery I18n

Learn how to implement jQuery i18n step by step and prepare your application for localization for global markets.
Software localization blog category featured image | Phrase

The internationalization of web applications is an important yet complex task. Each region and country around the world have different expectations when it comes to how text, messages, numbers, and dates should appear. Each user of a particular application expects that all text and messages are displayed in a familiar format. Luckily, there are a bunch of solutions available for JavaScript that enable you to internationalize. Some of them provide only a couple of helper functions, whereas others are full-fledged libraries with dozens of features. One such solution is jQuery i18n, which is used for localizing MediaWiki and many other international websites. In this article, we will learn lots of aspects of the jQuery i18n library that you may not have known and discuss some examples. Happy reading!

Features of jQuery i18n

  • Keeps code separate from i18n content. This feature keeps the code modular and allows developers to load the i18n functionalities they need.
  • Uses the JSON format.
  • It allows a change in a language without refreshing the web page.
  • Handles the plural form without using additional messages. Rule handling is done using the Unicode Consortium’s Common Locale Data Repository (CLDR).
  • Corrects sentences according to gender by passing the gender value.
  • Supports grammar forms.

Powered by Wikimedia

As already mentioned above, jQuery.i18n library is maintained by the Wikimedia Foundation team. Wikimedia, in turn, is the company behind Wikipedia, Wikinews, and others. What’s interesting, jQuery.I18n was initially created to be used in Wikimedia’s own projects, for example in Universal Language Selector which is currently available in Wikipedia, Wikibooks, Wikiquote, and other websites. The Language team takes care of the library. They launched Milkshake in 2012, a project aiming to create general I18n tools in jQuery. Members of the team decided to make jQuery.I18n framework (yeah, they call it “framework”, not “library”) open-source, and now it has hundreds of users all over the world. As long as Wikimedia is a solid company, you can be sure that this library will be maintained for a long period.

jQuery i18n Directory

Now let's talk about organizing your I18n files. Usually, an app powered by jQuery.i18n will have a folder i18n with a handful of .json files named after the language. This is a common practice met in many popular frameworks.

jQuery i18n Translation Files

Translation Files Format

All translations (or messages, as they are called in the library’s terms) are stored inside simple JSON files, which can be edited even using a simple Notepad. JSON is a data storage format and the abbreviation means “JavaScript object notation”. This is a simple key-value format that is very popular nowadays: for the past years, it has nearly superseded XML. Initially, it was derived from JavaScript, but all in all, it is language-independent. Here is a small example of how JSON looks:

{

  "key": "value",

  "nested": {

    "key_inside": 10

  }

}

JSON files are advantageous because:

  1. They allow an easy way for translators to access text to be translated. This is very useful if the JSON files need to be sent out for translation services.
  2. They prevent direct access to the database.

Storing Translations in a Single File

In a JSON file for internationalization, the key-message pairs contain the names and values for all language pairs. Every key is in lowercase letters with “-“ separating the words and is associated with a value in the chosen language. The JSON file can include meta information. Meta information can be defined as “information about information”. In relation to translations, it usually contains authors’ names, the date of the last modification, and a locale’s name (if you store translations inside separate files). Any information that describes a translation file can be put under the @metadata key (note the @ symbol). For example:

{

  "@metadata": {

    "authors": [ "John", "Kate", "Someone Else" ],

    "last-updated": "2017-03-09",

    "locale": "en",

    "additional-info": "some text goes here"

  }

}

A separate JSON file is often created for each language type; however, all of the translations can also be put into a single file. The advantage of separate JSON files is that the files are less complex. However, a single JSON file can be used for small apps with a few dozens of messages. Here is an exemplary JSON file with a single language and @metadata:

{

   "@metadata": {

       "author": "Colleen",

       "description": "An example JSON file",

       "last-updated": "2016-09-21",

       "message-documentation": "qqq"

   },

   "greeting": "Hello",

   "bye": "Goodbye"

}

Next, here is an example JSON file with multiple languages and @metadata:

{

  "@metadata": {

  "author": "Fred",

  "description": "An example JSON file",

  "last-updated": "2016-09-21",

  "message-documentation": "qqq"

  },

  "en": {

    "greeting": "Hello",

    "bye": "Goodbye"

  },

  "fr": {

    "greeting": "Bonjour",

    "bye": "Au revoir"

  }

}

Basically, you only need to provide a locale’s name and then put translations inside. It is also a common practice to prefix the keys with the application’s name, for example myapp-mykey.

Storing Translations in Multiple Files

For larger applications having all translations reside in a single file is usually not the best idea. It is easy to get overwhelmed by all the keys and values. Also, having a single file with all content is not very convenient when working with a version control system like Git. Therefore, you may divide messages into separate JSON files named after the locale: en.json, ru.json, de.json, etc. Usually, these files are stored inside the i18n directory. Here, for example, you may see how Wikimedia itself stores messages for its project. On top of that, you may extract messages for one or multiple locales in a separate file and then simply provide a path to it:

{

  "en": {

    "appname-title": "Example Application"

  },

  "ru": "my_dir/i18n/ru.json"

}

So, it is up to you to decide how translations will be stored!

Documenting Translations

Usually localizing an application (or anything else really) does not mean simply translating it. Translators may require some context, additional explanations to understand how to localize a phrase better. This is a common problem when translating video games or films: if a translator does not understand the background behind a character, he may incorrectly localize some joke and its point will be lost. Therefore, jQuery.i18n allows you to localize your translations easily inside a strangely named qqq.json file. Creating such a file is a really great idea that may come in handy for the translators. Inside the file, you simply list all the message keys and some relevant information. For example, you may explain in what tone should the message sound (more or less formal), what is the point of some joke or catchphrase, etc. Here is a small example of a qqq.json file:

{

  "appname-title": "Application's title. Do not translate, only transliterate",

  "appname-slogan": "Should sound energetic and friendly",

  "appname-notification": "Notification about some even. Should sound informal"

}

Loading Translation Files

Translations for jQuery.I18n can be loaded with a simple load() function like this:

$.i18n().load( {

  'en': './i18n/en.json'

});

However, how do you know when the files are actually loaded so that you can perform additional operations? Suppose, for example, you want to bind a click event to a link; when it happens the locale should be changed to another one. Of course, this can be done after the page is loaded using a document-ready event, but at this point, translations most likely won’t be ready. What to do? Meet promises. Promises simply mean that some operation will be carried out only after another process has finished its work with some result. This concept is pretty simple yet powerful and in many cases is preferred over callbacks. To add a promise, simply chain a done() method after the load():

$.i18n().load( {

  'en': './i18n/en.json'

}).done(function() {

  $('.change_locale).click(function() {

    // change locale here...

  });

});

So, the anonymous function inside the done() method will only be run after the load() succeeds. You may also chain a fail() method after the done() to instruct what to do if the loading was unsuccessful:

$.i18n().load( {

  'en': './i18n/en.json'

}).done(function() {

  $('.change_locale).click(function() {

    // change locale here...

  });

}).fail(function() {

  console.error('Cannot load translations!');

});

Usage

Usage Area Description
Switching locale The locale for the web page can be set using the locale option: $.i18n({locale: 'fr'});. In this example locale is French. To switch to another locale after plugin is already initialized: $.i18n ().locale = ‘ml’;
Message Loading Messages can be loaded for a specific locale or more than one locale: $.i18n.load({ });
Data API Localized messages can be displayed without JavaScript. Syntax: <li data-i18n=”message-key”></li>
Message Format – Placeholders Placeholders are represented with the $1, $2, $3 variables in the messages. They are replaced during runtime.
Message Format – Plurals In English, there are only two plural forms, but in many other languages, there are more than two plural forms. Syntax: {{PLURAL:$1|plural form 1|plural form 2}}.
Message Format – Gender The syntax is: {{GENDER:$1|He|She}}.

Data API

One interesting feature jQuery.i18n has is the support for HTML5 data- attributes. With this feature, you can start localizing your application in virtually 5 seconds. All you need to do is create a translation file, for example:

{

  "en": {

    "appname-title": "Example Application"

  }

}

Then, add some HTML tag on the page with the data-i18n attribute. This attribute should have a value equal to the translation key you want to employ:

<h1 data-i18n="appname-title">Fallback text</h1>

Inside the tag, you may put fallback text to display if the translation fails to load or some other error occurs.

Lastly, apply the i18n() method to the body:

$('body').i18n();

That's it! The library will automatically search for all data-i18n attributes and replace the tag’s contents with the corresponding value.

Translation

There are several ways to translate a jQuery.i18n application:

  1. Editing the JSON files directly. This is suitable for small applications with a limited number of languages.
  2. Have a translation interface with the application. This option works for proprietary or private applications with many translators.

Magic Words

Most developers love magic, especially when it can be easily understood and used. jQuery.i18n enables us to cast some magic too by introducing “magic words”. Actually, these “magic words” behave like templates supporting, for example, gender and plural forms. Suppose you want to display the phrase “You have 1 new message,” but what about 2 or more messages? Of course, you may simply say “You have 2 new message(s),” but that’s not much pretty. Let’s take advantage of jQuery.i18n’s “magic word” called plural:

var message = "You have $1 new {{PLURAL:$1|message|messages}}";

$1 is a placeholder that will be set with some integer in a moment. PLURAL:, in turn, is our “magic word”. It will automatically decide which word to display based on the value of $1. Out of the box, jQuery.i18n knows how to work with plural forms in many languages, including Russian, Finnish, Armenian, and others.

Now, all you need to do is localize this message with a simple line of code:

$.i18n(message, 2)

Here we take the message and assign the value of 2 to our $1 placeholder. Simple, isn’t it? Gender is also supported by the library, but you must explicitly say which gender you wish to use – unfortunately, jQuery.i18n cannot guess it by the user’s name, for example. Here is our sample message:

var message = "$1 has written you a message! {{GENDER:$2|He|She}} says:";

In this example, we have not one, but two placeholders: the first will store the name and the second one will host the gender itself.

Now, localize the message:

$.i18n(message, 'Helen', 'female');

Lastly, note that “magic words” are case insensitive, so you may say both GENDER: and gender:.

Parser Is Extendable

Apart from working with the existing “magic words”, you may extend jQuery.I18n’s parser to introduce custom ones. Suppose, you are tired of writing a long application’s name everywhere and want to insert it with an APPNAME “magic word”. Well, this is really easy to do:

$.extend( $.i18n.parser.emitter, {

  appname: function () {

    return 'My Super Duper Cool Application Version 3';

  }

}

When a trick’s secret is revealed, it appears to be so simple, eh? Under the hoods, the “magic words” are functions that return some content. In this case, for example, we return a string with the app’s name. What’s more, “magic words” may depend on each other.

For instance, let’s create a link template:

$.extend( $.i18n.parser.emitter, {

  appname: function () {

    return 'My Super Duper Cool Application Version 3';

  },

  link: function ( nodes ) {

    return '<a href="' + nodes[1] + '">' + nodes[0] + '</a>';

  }

}

The nodes local variable is going to contain an array of all passed arguments that you may easily reference. Now, let’s use this template to link to the site’s homepage while interpolating its name:

$.i18n('{{link:{{appname}}|http://example.com}}');

Fallbacks

What happens if some key cannot be found for a given locale? Well, this is not quite optimal, but jQuery.i18n takes care of such a scenario with the help of fallbacks as well. "Fallback" simply explains which locale to employ if a key is not found in the one currently set. You can include jquery.fallback.js file from the library’s source code into your app. It already sets some sane default fallbacks. For example, if a key was not found inside the Ukrainian locale, the library will try to search inside Russian, as these languages are somewhat similar:

{ "uk": [ 'ru' ] }

Of course, you may provide multiple elements inside the array constructing fallback chains:

{ 'zh-mo': [ 'zh-hk', 'zh-hant', 'zh-hans' ] }

jQuery.i18n.properties

jQuery.i18n.properties is a jQuery plugin for internationalization. Similar to Java, jQuery.i18n may use resource bundles (.properties files). Resource bundles are used to store locale-specific information such as text messages. They allow an easy way of accessing locale-specific information and adding locales easily by adding additional resource bundles. The .properties files contain locale-specific key-value pairs and interpret these files based upon language and country codes.

Using jQuery.i18n.properties

The jquery.i18n.properties.js plugin should be included in the <head> section of your HTML page:

<head>

  <script src="js/jquery.js"></script>

  <script src="js/jquery.i18n.properties.js"></script>

</head>

Features of jQuery.i18n.properties

  • Works like Java i18n. Uses resource bundles (‘.properties’ files) for translations. Uses ISO-639 for language codes and ISO-3166 for country codes.
  • If no language is specified, use the default browser language. The default language in the resource bundles is always used first. The user-specified language will be loaded next.
  • The resource bundle strings allow placeholder substitution, and there is support for namespaces in the keys.

Language Control

To make your code more efficient with fewer 404 errors, a languages.json file should be used. It defines the languages and the properties files that can be used. This file should be placed into the same directory as the language properties files. An example of a languages.json file is as follows:

{

  "languages": [

    "en_GB",

    "es_ES",

    "pt_BR",

    "sv_SE"

  ]

}

Example of using jQuery.i18n.properties

To create an HTML page using jquery.i18n.properties.js, the first step is to create a directory with the desired folders for the JavaScript files and the properties files. Next, the HTML code will be created. The HTML contains a dropdown that allows the user to select a language. The messages below the dropdown are localized based on the language chosen.

<HTML>

<HEAD>

  <script src="js/jquery.js" ></script>

  <script type="text/JavaScript" src="js/jquery.i18n.properties.js"></script>

</HEAD>

<BODY>

  <h2>Internationalization Example Using jQuery.i18n.properties</h2>

  <div id="langBox">

     Language:

     <select id="lang">

       <option value="en" selected>English</option>

       <option value="tr">Turkish</option>

       <option value="fr">French</option>

     </select>

  </div><br>

  <div id="lWelcome">Thank you for reading this example</div><br>

  <div id="lSelLang">Your Selected Language is: en </div>

</BODY>

</HTML>

Define .properties files

The jquery.i18n.properties.js plugin uses .properties files for the translated text. There are three properties files used in this example:

  1. Messages.properties
  2. Messages_fr.properties
  3. Messages_tr.properties

The text in each properties files is shown below:

Messages.properties

lWelcome = Thank you for reading this example

lSelLang = Your Selected Language is: {0}

Messages_fr.properties

lWelcome = Merci d'avoir lu cet exemple

lSelLang = Votre langue sélectionnée est : {0}

Messages_tr.properties

lWelcome = Bu örnek okumak için teşekkür ederiz

lSelLang = Sizin Seçili Dil geçerli: {0}

Loading localized strings from .properties

To load the messages from the properties files, save the jquery.i18n.properties.js file in the js folder. The code below is a simple example of how the properties files are loaded:

$(function() {

 $.i18n.properties({

   name: 'Messages',

   path: 'bundle/',

   mode: 'both',

   language: lang,

   callback: function() {

     $("#lWelcome").text($.i18n.prop('lWelcome'));

     $("#lSelLang").text($.i18n.prop('lSelLang', lang));

   }

 });

});

Options

Optional String or String[] If not specified, the default language reported by the browser will be used.

Option Description Notes
name File name or portion of the file name that represents a resource bundle.
language The ISO-639 Language code (‘en’, ‘fr’) and, optionally, ISO-3166 country code (‘en_US’, ‘pt_BR’).
path Path to the directory that contains ‘.properties‘ files to load. Optional String
mode Option to have resource bundle keys available as JavaScript vars/functions OR as a map. Optional String
cache Bundles are cached by the browser or forcibly reloaded. The default is to forcibly re-load. Optional boolean
encoding Type of encoding for bundles. Property file resource bundles are specified in ISO-8859-1 format. Defaults to UTF-8 format. Optional String
callback A callback function is called when script execution is completed. Optional function()

Phrase Can Make Your Life Easier

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 users' confusion. And so Phrase can make your life easier! Grab your 14-day trial. Phrase supports many different languages and frameworks, including JavaScript. It allows you to easily import and export translation 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 tutorial, we discussed some facts about jQuery.i18n library by Wikimedia Foundation. Hopefully, you found this article useful and interesting! If you are looking for a detailed tutorial explaining how to use this tool in your application, check out our Advanced Guide to jQuery i18n. Also, jQuery.I18n’s homepage provides very nice documentation, so we recommend browsing it as well. Lastly, we also recommend exploring a couple of other solutions for JavaScript to enable I18n support in our Step-by-Step Guide to JavaScript Localization.