JavaScript and jQuery logos

jQuery.i18n: Localization & internationalization tutorial with examples

Recently we have done an overview of the most popular internationalization libraries for JavaScript covering solutions like I18next, Globalize and others. In this article, however, we are going to focus on a single solution and talk about localizing JavaScript apps with jQuery.I18n. This is a feature-rich library developed and supported by Wikimedia team. It is easy to get started and which is suitable for both simple and complex websites.

In this tutorial we’re going to cover the following topics:

  • Overview of the library.
  • Downloading and loading all the necessary modules.
  • Providing translations in JSON files.
  • Documenting your translations.
  • Performing translations and using placeholders.
  • Utilizing gender information and employing pluralization.
  • Using “magic words”.
  • Switching locale.

Note that if you wish to follow this tutorial with the examples, you’ll require a web server like MAMP, WAMP, or IIS.

    Overview

    So, jQuery.I18n is an internationalization library developed by Wikimedia language engineering team. Wikimedia, in turn, is a company behind Wikipedia, a popular free online encyclopedia. What’s important, Wikimedia team utilizes jQuery.I18n internally for their resources which are accessed by people from all over the world. These projects, of course, require proper localization. It means that the library is being quite actively maintained and well-documented which is very important for us, developers.

    Here are the main features of jQuery.I18n:

    On the other hand, jQuery.i18n, does have some downsides:

    • As the name implies, it relies on the jQuery library. 8-10 years ago jQuery used to be a de-facto standard for web, and nearly everyone was using it. Now, however, things have changed and many devs tend to get rid of jQuery preferring pure JavaScript. Still, many sites rely on jQuery, therefore reliance of this library is probably not a big downside. Also, there is a jQuery-independent port of the library with a funny name banana-i18n.
    • Translations are not updated dynamically once the current locale changes. This may or may not be a big problem for you, but sill, this is quite an annoying thing. Some other popular solutions (like I18next) can update your texts nearly instantly as soon as the user has switched the current language.
    • jQuery.I18n team is not actively participating in discussions on GitHub. There are a handful of open issues, and some of them are quite old. Still, jQuery.I18n does not have any serious bugs, and it is production-ready.

    Creating a Demo App

    For the purposes of this demo, I’ll create a single index.html file inside the root folder of my local web server. Next, we have to download the latest version of jQuery.i18n, therefore run the following commands (you will need Git installed on your PC):


    git clone https://github.com/wikimedia/jquery.i18n.git
    cd jquery.i18n
    git submodule update –init

    view raw

    gistfile1.txt

    hosted with ❤ by GitHub

    These commands will clone the library’s code into the jquery.i18n folder and also initialize a sub-module with CLDR rule parser. Copy the contents of the jquery.i18n/src to the js folder of your demo project. src folder contains the library’s modules and you can choose which ones to load in your app. There’s also a nested languages folder with basic support for some common locales.

    Also, navigate to jquery.i18n\libs\CLDRPluralRuleParser\src and copy CLDRPluralRuleParser.js into the js folder. This file will enable support for pluralization.

    At this point we may define a skeleton for our HTML page inside the index.html file:


    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="utf-8">
    </head>
    <body>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
    <script src="js/CLDRPluralRuleParser.js"></script>
    <script src="js/jquery.i18n.js"></script>
    <script src="js/jquery.i18n.messagestore.js"></script>
    <script src="js/jquery.i18n.fallbacks.js"></script>
    <script src="js/jquery.i18n.language.js"></script>
    <script src="js/jquery.i18n.parser.js"></script>
    <script src="js/jquery.i18n.emitter.js"></script>
    <script src="js/jquery.i18n.emitter.bidi.js"></script>
    <script src="js/languages/ru.js"></script>
    </body>
    </html>

    view raw

    index.html

    hosted with ❤ by GitHub

    First of all I’m loading jQuery 3 which our I18n library relies on, then CLDR rule parser, all the modules, and ru.js file that enables support for the Russian language. Of course, you may further tweak this code and pick only the necessary modules.

    Translation Files

    Translations for jQuery.i18n are stored inside simple JSON files or can be loaded directly inside your code. I would recommend sticking to the first option (especially for larger sites), therefore create an i18n folder inside your project.

    It possible to store translations for all languages in a single file or separate them into different files. If you prefer to utilize a single file, next your translation keys under the locale codes:


    {
    "en": {
    "welcome": "Welcome!"
    },
    "ru": {
    "welcome": "Добро пожаловать!"
    }
    }

    view raw

    demo.json

    hosted with ❤ by GitHub

    In this example we have translations for English and Russian locale stored under the welcome key.

    Interestingly, it is possible to provide path to the language file instead of listing translations:


    {
    "en": {
    "welcome": "Welcome!"
    },
    "ru": "some/path/ru.json"
    }

    view raw

    demo.json

    hosted with ❤ by GitHub

    As your website grows you will have more and more translation keys resulting in a very long JSON file. Therefore, let’s divide translations into separate files. I’ll create the following directory structure:

    • i18n
      • ru.json
      • en.json

    Here is the contents for the en.json file:


    {
    "@metadata": {
    "authors": [
    "Alex"
    ],
    "last-updated": "2019-05-06",
    "locale": "en",
    "message-documentation": "qqq"
    },
    "welcome": "Welcome!"
    }

    view raw

    en.json

    hosted with ❤ by GitHub

    Note that I am also providing metadata under the @metadata key. Here you may list the authors of this translation, specify locale, and provide message documentation (which we’ll discuss in a moment).

    Here is the contents for the ru.json file:


    {
    "@metadata": {
    "authors": [
    "Alex"
    ],
    "last-updated": "2019-05-06",
    "locale": "ru",
    "message-documentation": "qqq"
    },
    "welcome": "Добро пожаловать!"
    }

    view raw

    ru.json

    hosted with ❤ by GitHub

    Now we can load our translation files!

    Message Documentation

    In the previous section we have specified a message-documentation meta key with a bizarre-looking qqq value. Basically, qqq is a special locale which contains description for every translation key of your application. These descriptions may include information on where exactly this key is being used, what is the translation context (this info is especially important for translators), what tone the translator should use (formal, informal, friendly) etc. For example, here is a qqq.json file used in a real Wikimedia project.

    So, now let’s create our very own message documentation inside i18n/qqq.json:


    {
    "@metadata": {
    "authors": [
    "Alex"
    ]
    },
    "welcome": "A welcoming message displayed on the main page of the site. Should be friendly, informal, and, ummm, welcoming."
    }

    view raw

    qqq.json

    hosted with ❤ by GitHub

    Now our future translators will have all the necessary info on the welcome key.

    Loading Translations

    As long as our translation files are ready, we may load them inside the demo app. Create a new file js/global.js with the following contents:


    jQuery(function($) {
    $.i18n().load({
    'en': 'http://localhost/demo/i18n/en.json',
    'ru': 'http://localhost/demo/i18n/ru.json'
    });
    });

    view raw

    global.js

    hosted with ❤ by GitHub

    $.i18n().load() is a function that accepts either paths to your translation files or JSON with translation data. It means, that you may also load translations in the following way:


    $.i18n().load({
    "en": { "welcome": "Welcome!" },
    "ru": { "welcome": "Добро пожаловать!" }
    });

    view raw

    demo.js

    hosted with ❤ by GitHub

    Now include global.js file on the main page of the site:


    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="utf-8">
    </head>
    <body>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
    <script src="js/CLDRPluralRuleParser.js"></script>
    <script src="js/jquery.i18n.js"></script>
    <script src="js/jquery.i18n.messagestore.js"></script>
    <script src="js/jquery.i18n.fallbacks.js"></script>
    <script src="js/jquery.i18n.language.js"></script>
    <script src="js/jquery.i18n.parser.js"></script>
    <script src="js/jquery.i18n.emitter.js"></script>
    <script src="js/jquery.i18n.emitter.bidi.js"></script>
    <script src="js/languages/ru.js"></script>
    <script src="js/global.js"></script> <!– add this –>
    </body>
    </html>

    view raw

    index.html

    hosted with ❤ by GitHub

    Performing Translations

    In order to fetch translation by its key, you may utilize $.i18n() function. Remember, however, that we are loading our translation files asynchronously, and therefore have to wait until they are ready. Luckily, the load() function returns a promise, so we may chain done() function like this:


    jQuery(function($) {
    $.i18n().load({
    'en': 'http://localhost/lok/jqi/i18n/en.json',
    'ru': 'http://localhost/lok/jqi/i18n/ru.json'
    }).done(function() {
    $('#welcome').text($.i18n('welcome')); // <—
    });
    });

    view raw

    global.js

    hosted with ❤ by GitHub

    welcome here is a translation key.

    Now add a #welcome element to the index.html:


    <body>
    <h1 id="welcome"></h1>
    <!– … –>
    </body>

    view raw

    index.html

    hosted with ❤ by GitHub

    Now you may navigate to the main page of the site and make sure that the “Welcome!” phrase is being displayed in a h1 tag. It means that you have configured jQuery.i18n properly!

    Data Interpolation

    In some cases you may want to dynamically provide data for your translations. For example, our welcoming message may greet a currently logged in user by his or her name. Of course, we won’t code authentication system in this article, and rather store user’s information in a plain object:


    var current_user = {
    name: "Alex"
    };

    view raw

    global.js

    hosted with ❤ by GitHub

    Now pass the current user’s name to the $.i18n() function as the second argument:


    // …
    .done(function() {
    $('#welcome').text(
    $.i18n('welcome', current_user.name)
    );
    })

    view raw

    global.js

    hosted with ❤ by GitHub

    Now all you need to do is fetch this data inside your translations. Tweak i18n/en.json file (I’m skipping metadata for brevity):


    {
    "welcome": "Welcome, $1!"
    }

    view raw

    en.json

    hosted with ❤ by GitHub

    $1 is a placeholder that will be dynamically replaced with the second argument passed to the $.i18n() function (which is “Alex” in our case).

    Now, ru.json:


    {
    "welcome": "Добро пожаловать, $1!"
    }

    view raw

    ru.json

    hosted with ❤ by GitHub

    You may define as many placeholders as needed in your translations, for example $1 says "$2" to $3 which will result in something like “Alex says “Hi” to Ann”. Note, however, that if a placeholder doesn’t have any value, it will be displayed as-is: Welcome, $1!.

    Gender Information

    One very common task when performing internationalization is displaying slightly different messages based on the given gender. Suppose we would like to show some consulting information on the page, and the consultant may be either male or female. Based on the consultant’s gender we would like to display a message like “Your today’s consultant is SOME NAME. He (She) says: …”. How do we achieve that?

    First of all, add a new consulting_info translation key to the en.json file:


    { "consulting_info": "Your today's consultant is $1. {{GENDER:$2|He|She}} says: $3" }

    view raw

    en.json

    hosted with ❤ by GitHub

    GENDER is a special switch that displays one of the given options (“He” or “She”) based on the given argument represented as a placeholder.

    Now let’s add a Russian translation:


    { "consulting_info": "Сегодня вас консультирует $1. {{GENDER:$2|Он|Она}} говорит: $3" }

    view raw

    ru.json

    hosted with ❤ by GitHub

    Now simply define a new consultant object and pass interpolation data to the $.i18n() function as we already did in the previous section:


    jQuery(function($) {
    var current_user = {
    name: "Alex"
    };
    var consultant = { // <—
    name: "Ann",
    gender: "female",
    message: "How may I help you?"
    };
    $.i18n().load({
    'en': 'http://localhost/lok/jqi/i18n/en.json',
    'ru': 'http://localhost/lok/jqi/i18n/ru.json'
    }).done(function() {
    $('#welcome').text(
    $.i18n('welcome', current_user.name)
    );
    $('#consulting_chat').text( // <—
    $.i18n('consulting_info', consultant.name, consultant.gender, consultant.message)
    );
    });
    });

    view raw

    global.js

    hosted with ❤ by GitHub

    Lastly add a new tag to the index.html file:


    <body>
    <!– –>
    <div id="consulting_chat"></div>
    </body>

    view raw

    index.html

    hosted with ❤ by GitHub

    Reload the page and observe the result!

    Pluralization

    Another common task is displaying pluralized messages based on the given count. For instance, we may want to say how many unread messages the current user has. To achieve that, we will use a PLURAL switch. Tweak en.json file:


    { "unread_messages": "You have $1 unread {{PLURAL:$1|message|messages}}" }

    view raw

    en.json

    hosted with ❤ by GitHub

    As you see, it is very similar to what we did with the GENDER switch a minute ago. For Russian language, however, more options has to be provided:


    { "unread_messages": "У вас $1 {{PLURAL:$1|непрочитанное сообщение|непрочитанных сообщения|непрочитанных сообщений}}" }

    view raw

    ru.json

    hosted with ❤ by GitHub

    Various languages have different pluralization rules but luckily you don’t need to bother about it thanks to Unicode CLDR pluralization info that jQuery.I18n relies on. All you need to do is open the following page and use the given table to determine how many options should be provided for the language you wish to support.

    Now tweak your JS file again:


    // …
    $('#unread_messages').text(
    $.i18n('unread_messages', 5)
    );

    view raw

    global.js

    hosted with ❤ by GitHub

    And add yet another tag to the HTML page:


    <body>
    <!– –>
    <div id="unread_messages"></div>
    </body>

    view raw

    index.html

    hosted with ❤ by GitHub

    Reload the page once again and make sure the text has proper pluralization!

    It’s Pure Magic!

    We have discussed two switches: GENDER and PLURAL but it appears they are actually so-called “magic words” that jQuery.I18n supports. A “magic word” is just a special construct that the parser processes and replaces with some content. You may further extend the parser and provide your own constructs as needed. For instance, let’s construct a magic word to display abbreviations:


    $.extend( $.i18n.parser.emitter, {
    abbr: function (nodes) {
    return '<abbr title="' + nodes[1] + '">' + nodes[0] + '</abbr>';
    }
    } );

    view raw

    global.js

    hosted with ❤ by GitHub

    abbr is the name of our “magic word”. The anonymous function may optionally accept an array of nodes which are simply arguments passed to the magic word. This function then returns the content that you wish to display.

    Use this magic word in the following way:


    console.log($.i18n('{{abbr:www|world wide web}}')); // => <abbr title="world wide web">www</abbr>

    view raw

    demo.js

    hosted with ❤ by GitHub

    Note that magic words may be nested as shown in this example.

    Translating With HTML5 Data Attributes

    Another interesting feature that jQuery.I18n provides is the support for HTML5 data-* attributes that may be used to provide translations right inside your markup. To see it in action, add yet another tag into your markup with data-i18n attribute:


    <body>
    <h2 data-i18n="glad_to_see">We're glad to see you!</h2>
    <!– … –>
    </body>

    view raw

    index.html

    hosted with ❤ by GitHub

    glad_to_see is just a translation key. We're glad to see you! basically acts as a fallback text that will be shown if translation for the specified key cannot be found.

    Next add English translation for this key:


    { "glad_to_see": "We are glad to see you!" }

    view raw

    en.json

    hosted with ❤ by GitHub

    Also add Russian translation:


    { "glad_to_see": "Мы рады вас видеть!" }

    view raw

    ru.json

    hosted with ❤ by GitHub

    And lastly tweak your JS code:


    .done(function() {
    $('h2').i18n();
    // …
    }

    view raw

    global.js

    hosted with ❤ by GitHub

    This will search for a translation under the key provided in the data-i18n attribute. If the translation is found, it will be placed inside the h2 tag.

    What’s more, the i18n() function may be applied to the whole HTML page:


    $('html').i18n();

    view raw

    demo.js

    hosted with ❤ by GitHub

    Now every tag containing the data-i18n attribute will be properly translated!

    Switching Language

    Our next task is providing functionality to switch the currently set language. Let’s provide a language switcher component on our HTML page:


    <body>
    <ul class="locale-switcher">
    <li><a href="#" data-locale="en">English</a></li>
    <li><a href="#" data-locale="ru">Русский</a></li>
    </ul>
    <!– … –>
    </body>

    view raw

    index.html

    hosted with ❤ by GitHub

    When a user clicks on one of these links, we should change the language accordingly. Bind a click event and provide an event handler inside the global.js:


    .done(function() {
    $('.switch-locale').on('click', 'a', function(e) {
    e.preventDefault();
    $.i18n().locale = $(this).data('locale');
    });
    );

    view raw

    global.js

    hosted with ❤ by GitHub

    I’ve added this event handler inside the done() function because, once again, we should wait until the translation files are loaded.

    There is one problem however: translation messages won’t update automatically once the user has changed locale. To fix that, I’ll extract all translation-related logic to a separate do_translate() function, and call it once the click event fires. Here is the complete version of the code:


    jQuery(function($) {
    var current_user = {
    name: "Alex"
    };
    var consultant = {
    name: "Ann",
    gender: "female",
    message: "How may I help you?"
    };
    $.extend( $.i18n.parser.emitter, {
    abbr: function (nodes) {
    return '<abbr title="' + nodes[1] + '">' + nodes[0] + '</abbr>';
    }
    } );
    var do_translate = function() {
    $('h2').i18n();
    console.log($.i18n('{{abbr:www|world wide web}}'));
    $('#welcome').text(
    $.i18n('welcome', current_user.name)
    );
    $('#consulting_chat').text(
    $.i18n('consulting_info', consultant.name, consultant.gender, consultant.message)
    );
    $('#unread_messages').text(
    $.i18n('unread_messages', 101)
    );
    }
    $.i18n().load({
    'en': 'http://localhost/lok/jqi/i18n/en.json',
    'ru': 'http://localhost/lok/jqi/i18n/ru.json'
    }).done(function() {
    $('.locale-switcher').on('click', 'a', function(e) {
    e.preventDefault();
    console.log($(this).data('locale'));
    $.i18n().locale = $(this).data('locale');
    do_translate();
    });
    do_translate();
    });
    });

    view raw

    global.js

    hosted with ❤ by GitHub

    do_translate() fires once translation files are loaded, as well as after the click event happens.

    Default Locale

    In order to provide a default locale, you may use two possible options:

    • Provide it using the lang attribute of the html tag
    • Set it using the $.i18n() function

    Let me demonstrate you these two approaches. lang attribute is defined in the following way:


    <html lang="en">
    <!– –>
    </html>

    view raw

    index.html

    hosted with ❤ by GitHub

    This way we are saying that the language of this page is English. jQuery.I18n will read it and assign the default locale properly.

    To set a default locale programmatically, use the following code:


    $.i18n( {
    locale: 'ru'
    } );

    view raw

    demo.js

    hosted with ❤ by GitHub

    These two options are equal, and you are free to choose any of them.

    Make Your Life Easier With Lokalise

    Supporting multiple languages on a big website may become a serious pain. You must make sure that all the keys have translations for each and every locale. Luckily, there is a solution to this problem: the Lokalise platform that makes working with the localization files much simpler. Let me guide you through the initial setup which is nothing complex really.

    • To get started, grab your free trial
    • Create a new project, give it some name, and set English as a base language
    • Click “Upload Language Files”
    • Upload translation files for all your languages
    • Proceed to the project, and edit your translations as needed
    • You may also contact a professional translator to do the job for you
    • Next simply download your files back
    • Profit!

    If you wish to upload QQQ files with message documentation, that’s possible as well. Perform the following steps:

    1. Open your project and press the “Add Language” button on the top
    2. Choose any language from the dropdown
    3. Press on the new language’s flag and select “Language Settings”
    4. Set “Custom Language Code” to “On” and enter qqq
    5. Set “Custom Language Name” to “On” and enter something like “Message documentation”
    6. Save the changes and proceed to the “Upload” page
    7. Choose your .qqq file. Your custom locale should be detected automatically

    Here is the result:

    Lokalise has many more features including support for dozens of platforms and formats, and even the possibility to upload screenshots in order to read texts from them. So, stick with Lokalise and make your life easier!

    Conclusion

    In this article we have discussed jQuery.I18n: an internationalization library for JavaScript applications created by Wikimedia. We have seen how to set it up, create and load language files, perform translations, use magic words, extend parser, and switch locale. Basically, we have covered all the main information, and you may start using this solution in real-world projects.

    Don’t forget to check our article on other I18n libraries for JavaScript to learn about potential alternatives. Also, don’t hesitate to contact us if you have further questions, and stay with Lokalise!

    Talk to one of our localization specialists

    Book a call with one of our localization specialists and get a tailored consultation that can guide you on your localization path.

    Get a demo

    Related posts

    Learn something new every two weeks

    Get the latest in localization delivered straight to your inbox.

    Related articles
    Localization made easy. Why wait?
    The preferred localization tool of 3000+ companies