DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

The software you build is only as secure as the code that powers it. Learn how malicious code creeps into your software supply chain.

Apache Cassandra combines the benefits of major NoSQL databases to support data management needs not covered by traditional RDBMS vendors.

Generative AI has transformed nearly every industry. How can you leverage GenAI to improve your productivity and efficiency?

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Related

  • Exploring Intercooler.js: Simplify AJAX With HTML Attributes
  • Mastering React App Configuration With Webpack
  • I Built an App With Remix in 30 Minutes
  • Angular vs. Flutter for Web App Development: Know Which One Is Best?

Trending

  • Driving DevOps With Smart, Scalable Testing
  • Building an AI/ML Data Lake With Apache Iceberg
  • IoT and Cybersecurity: Addressing Data Privacy and Security Challenges
  • Cosmos DB Disaster Recovery: Multi-Region Write Pitfalls and How to Evade Them
  1. DZone
  2. Data Engineering
  3. Databases
  4. A Step-by-Step Guide to JavaScript Localization

A Step-by-Step Guide to JavaScript Localization

Today it's time to talk about front-end. In this article we will discuss how to localize JavaScript applications using the following solutions.

By 
Ilya Bodrov user avatar
Ilya Bodrov
·
Updated Apr. 01, 20 · Tutorial
Likes (13)
Comment
Save
Tweet
Share
36.6K Views

Join the DZone community and get the full member experience.

Join For Free

Internationalization (dubbed as I18n) and localization (dubbed as L10n) are very important (though often hard) steps for any application that is going to be used worldwide. In one of the previous articles we've seen how to implement I18n at the back-end powered by Ruby on Rails, but today it's time to talk about front-end. In this article we will discuss how to localize JavaScript applications using the following solutions:

  • jQuery.I18n by Wikimedia
  • Polyglot by Airbnb
  • Globalize by jQuery team

All of these solutions are quite different and have their own specifics so we'll see them all in action.

The source code is available at GitHub.

Preparations

Before proceeding to the main part let's quickly prepare a basic structure for our simple demo project. Create a separate folder with the index.html file inside. We'll make copies of this file to test various solutions. Then create a nested folder called common and inside place the jquery.js file that can be downloaded from the jquery.com website. You may pick either version of jQuery (1, 2 or 3) depending on what browsers you wish to support - it does not really matter for this demo.

Now populate index.html with some markup:

index.html

    <!DOCTYPE html>
    <html>
    <head>
      <meta charset="utf-8">
        <script src="common/jquery.js"></script>
    </head>
    <body>

    </body>
    </html>

This is enough for us to get started!

jQuery.I18n

Let's start with a jQuery-based internationalization library developed and maintained by Wikimedia. Wikimedia is a global movement which takes care of such well-known projects as Wikipedia, Wikinews, Wikibooks and others. jQuery.I18n, in turn, uses a JSON-based localization file format and supports gender, grammar forms, dynamic change of language, fallback chains and more.

Translations for jQuery.I18n can be separated in multiple files (en.json, de.json etc) or stored all together in one file. Here is an example of en.json:

{
    "@metadata": {
        "authors": [
          "Me"
        ],
        "last-updated": "2016-09-21",
        "locale": "en",
        "message-documentation": "qqq"
    },
    "appname-title": "Example Application",
    "appname-sub-title": "An example application with jquery.i18n"
}

So, as you can see, these files support metadata where you specify authors, date of the latest update, locale, and other information. Next, you provide the actual translations in a key-value format. It is advised to prefix keys with the app's name to make it unique, but that's not mandatory.

For smaller apps, you may provide all translations in a single file. In this case language's name is specified as a parent key:

{
  "@metadata": { ... }
    "en": {
    "appname-title": "Example Application"
    },
    "ru": {
        "appname-title": "Тестовое приложение"
    }
}

What's more, you can even provide path to the translations file:

{
  "@metadata": { ... }
    "en": {
    "appname-title": "Example Application"
    },
    "ru": "ru.yml"
}

Now, of course, you are wondering how to load these translations into your app. There are a couple of ways and the first is probably the easiest one. Instead of creating a separate JSON file you can place all translations directly into the script by passing them to the load function:

$.i18n().load({
  'en': {
    'appname-title': 'Example Application'
  },
  'ru' : {
    'appname-title': 'Тестовое приложение'
  }
});

I can't say that this is a recommended practice but for really small projects it is fine.

Another solution is to load messages from an external URL

$.i18n().load({
    en: 'i18n/en.json'
})

When using this approach the load will return a promise, so you can place further actions inside the done function:

$.i18n().load({
    en: 'i18n/en.json'
}).done( function() { console.log('done!') } )

You can set locale upon library's initialization using either locale option

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

or by providing it inside the lang attribute for the html tag:

<html lang="en" dir="ltr">

Of course, locale can be switched later by redefining the locale option:

$.i18n({
  locale: 'de' 
});
// or
$.i18n().locale = 'de';

To translate a message you would provide a key to the $.i18n function

$.i18n('message-key1');

or just employ a data- attribute (no additional JavaScript is needed). The initial content is a fallback text to display in case something goes wrong and the translation cannot be found:

<li data-i18n="message-key">Fallback text</li>

Note that messages support interpolation by taking parameters. These are written as $1, $2 etc:

var message = "Good day, $1";
$.i18n(message, 'Joe');

Plurals and genders are handled with the following syntax:

var message = "Found $1 {{PLURAL:$1|result|results}}";
$.i18n(message, 1);

var message = "$1 changed {{GENDER:$2|his|her}} profile picture";
$.i18n(message, 'Emma', 'female');

In Practice

To start off, copy our base index.html file as the jquery_i18n.html. Create a new jquery_i18n directory and place the main-jquery_i18n.js file inside. Next clone jQuery.I18n somewhere on your PC along with sub-modules:

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

We will require all files from the src directory (without the languages folder) and also CLDRPluralRuleParser.js from the libs\CLDRPluralRuleParser\src. Copy all of these into the jquery_i18n folder and then include them in the proper order:

jquery_i18n.html

    [...]
  <script src="jquery_i18n/CLDRPluralRuleParser.js"></script>
  <script src="jquery_i18n/jquery.i18n.js"></script>
  <script src="jquery_i18n/jquery.i18n.messagestore.js"></script>
  <script src="jquery_i18n/jquery.i18n.fallbacks.js"></script>
  <script src="jquery_i18n/jquery.i18n.language.js"></script>
  <script src="jquery_i18n/jquery.i18n.parser.js"></script>
  <script src="jquery_i18n/jquery.i18n.emitter.js"></script>
  <script src="jquery_i18n/jquery.i18n.emitter.bidi.js"></script>
  <script src="jquery_i18n/main-jquery_i18n.js"></script>
    [...]

Let's also provide the initial locale via the lang attribute:

jquery_i18n.html

    [...]
    <html lang="en" dir="ltr">
    [...]

Lastly, add links to switch the language and a couple of empty tags that are going to host our translated content. I'll work with English and Russian but, of course, you may choose any other languages - it does not really matter, but for real-world apps make sure your texts are properly translated, preferably by a human.

jquery_i18n.html

    [...]
    <body>
        <a href="#" class="lang-switch" data-locale="en">English</a> |
        <a href="#" class="lang-switch" data-locale="ru">Русский</a>
        <h1 data-i18n="welcome"></h1>
        <p id="messages"></p>
    </body>
    [...]

Now proceed to the script. We'll need to load translations as soon as the document is ready. For simplicity let's store all our messages in the script:

main-jquery_i18n.js

    [...]
    jQuery(document).ready(function() {
        $.i18n().load({
            'en': {
            'welcome': 'Welcome!',
            }
            'ru': {
            'welcome': 'Добро пожаловать!',
            }
        });
    });
    [...]

Note the welcome key - the same name is used in the data-i18n attribute for the h1 tag. This way a proper translation will be used automatically - all we have to do is run this process by calling the i18n() function. I'll extract it inside the update_texts:

main-jquery_i18n.js

    [...]
    jQuery(document).ready(function() {
      var update_texts = function() {
            $('body').i18n();
        };

        $.i18n().load({...});

        update_texts();
    });
    [...]

Now let's take care of language switching. This is simple - just listen to the click event, extract value of the data-locale attribute and then set locale accordingly:

main-jquery_i18n.js

    [...]
  $('.lang-switch').click(function(e) {
    e.preventDefault();
    $.i18n().locale = $(this).data('locale');
    update_texts();
  });
    [...]

Lastly, we are going to add a translation for the #messages section. This is going a be a bit more complex:

main-jquery_i18n.js

    [...]
  $.i18n().load({
    'en': {
      'welcome': 'Welcome!',
      'message_from': '$1 has send you $2 {{plural:$2|message|messages}}. {{gender:$3|He|She}} is waiting for your response!'
    },
    'ru': {
      'welcome': 'Добро пожаловать!',
      'message_from': '$1 {{gender:$3|отправил|отправила}} вам $2 {{plural:$2|сообщение|сообщений|сообщения}}. {{gender:$3|Он|Она}} ждёт ответа!'
    }
  });
    [...]

Here pluralization and gender info are used at the same time. For Russian language I had to add more options because pluralization rules are more complex. In order for this to work tweak the update_texts() function:

main-jquery_i18n.js

    [...]
  var update_texts = function() {
    $('body').i18n();
    $('#messages').text($.i18n('message_from', 'Ann', 2, 'female'));
  };
    [...]

Now open the page and try to switch between languages - everything should be working just great!

Polyglot.js

Polyglot.js is a small solution created by Airbnb (an engineering company) that works in-browser and in Common.js environments. It supports interpolation and pluralization while having zero dependencies. Grab production version here.

To start working with Polyglot, instantiate it:

var polyglot = new Polyglot();

It's a class-based, therefore you may work with a different set of locales at the same time.

Next provide a list of phrases:

polyglot.extend({
  "hello": "Hello"
});
// or
var polyglot = new Polyglot({phrases: {"hello": "Hello"}});

As a common pattern, documentation suggests to prepare a hash of phrases on the back-end and then output them in the script tag. Note that Polyglot won't do translation - it's your job to give proper phrases based on user's locale.

Phrases can be replaced or removed completely (for example, to free up memory) by using replace or clear methods respectively.

Note that nesting is supported as well:

polyglot.extend({
  "nav": {
    "sidebar": {
      "welcome": "Welcome"
    }
  }
});

If you've come from the Rails world, interpolation should look familiar to you:

polyglot.extend({
  "hello_name": "Hello, %{name}."
});

To perform the actual translation, utilize the t method:

polyglot.t("hello_name", {name: "John"});

If your key is nested, then use dot . as a delimiter:

polyglot.t("nav.sidebar.welcome");

Note that language's name is not provided anywhere - currently it is used only when working with pluralization. To set it, employ the locale function

polyglot.locale("de")

or set it upon instantiating a new object by passing a locale option.

Messages with pluralization should be delimited with four pipes (||||):

polyglot.extend({
  "num_cars": "%{smart_count} car |||| %{smart_count} cars",
});

The correct message will be picked based on the smart_count parameter.

In Practice

Now let's quickly see this solution in action. Copy index.html file and name it polyglot.html. Then create a polyglot folder and place the production version of the script inside. Also create the main-polyglot.js file there and hook everything up:

polyglot.html

    [...]
    <script src="polyglot/polyglot.js"></script>
    <script src="polyglot/main-polyglot.js"></script>
    [...]

For this demo we will utilize Underscore.js' template engine that will be used to render content (though you may stick with Handlebars or some other solution). If you haven't worked with such templates before, the idea is pretty simple: you have a markup with some parameters. This template is then being "compiled" (meaning that the parameters receive their values) and rendered on the page like any other HTML.

Place the production version of Underscore into the common folder and require it:

polyglot.html

    [...]
  <script src="common/jquery.js"></script>
  <script src="common/underscore.js"></script>
    [...]

I am going to place our template directly onto the page, at the very bottom. Note that it has to be wrapped with a script tag with a special type (to prevent browsers from trying to process it as a JavaScript code):

polyglot.html

    <script type="text/template" id="main-content">
      <p><%= hello %></p>
      <small><%= unread %></small>
    </script>

Now inside the script let's wait until the document is ready and then instantiate the Polyglot class while providing two messages:

main-polyglot.js

    jQuery(document).ready(function() {
      var polyglot = new Polyglot({
        phrases: {
          "hello": "Hello, %{name}!",
          "unread": "You have %{smart_count} unread message |||| You have %{smart_count} unread messages"
        }
      });
    });

Here we utilized interpolation and pluralization (note the parameter's name smart_count - when using another name the pluralization seems to stop working). Now let's grab the template

    var main_content_temp = _.template($('#main-content').html());

and then provide values to the parameters inside it

  $('body').prepend(main_content_temp({
    hello: polyglot.t('hello', {name: 'John'}),
    unread: polyglot.t('unread', {smart_count: 2})
  }));

Here is the resulting code:

main-polyglot.js

    jQuery(document).ready(function() {
      var polyglot = new Polyglot({
        phrases: {
          "hello": "Hello, %{name}!",
          "unread": "You have %{smart_count} unread message |||| You have %{smart_count} unread messages"
        }
      });

      var main_content_temp = _.template($('#main-content').html());
      $('body').prepend(main_content_temp({
        hello: polyglot.t('hello', {name: 'John'}),
        unread: polyglot.t('unread', {smart_count: 2})
      }));
    });

Load the HTML document and test it out!

Globalize

Globalize is a pretty big library for internationalization developed by the members of jQuery core team. It works in-browser (supporting all modern browsers and IE starting from version 9) and with Node.js, providing many useful features including number, date and time parsing, pluralization, interpolation, unit support and much more. It uses that provides key building blocks for software to support the world's languages, with the largest and most extensive standard repository of locale data available. What's more, Globalize is modular and does not contain any I18n data - you are free to load it yourself.

There are three main API functions:

  • Globalize.load() that loads CLDR locale data in JSON format (such as date and time formats, names of the month etc)
  • Globalize.locale() - getter and setter for the locale
  • [new] Globalize - instantiates a new Globalize object

Also there are variety of different functions for each module that you can find here.

In Practice

Let's see Globalize in action right away. Copy index.html and name it globalize.html. Also create the globalize folder with the globalize-main.js file inside. As long as Globalize is modular, dependencies has to be loaded in the proper order (there is even an online tool available helping you to find out which dependencies are needed).

Therefore, you'll need to download the latest version of Globalize as well as CLDR. Here is the list of Globalize's files to grab (place them inside the globalize folder of your project):

  • dist/globalize.js
  • dist/globalize/date.js
  • dist/globalize/number.js
  • dist/globalize/currency.js
  • dist/globalize/message.js
  • dist/globalize/plural.js

Unfortunately, that's not all. Here are the required CLDR files that has to be placed inside cldr folder (create it now):

  • dist/cldr.js
  • dist/cldr/event.js
  • dist/cldr/supplemental.js

Phew. The last part is providing some common locale data for CLDR - download it here and place under the cldr/cldr_data.js name. Finally, hook all those files in the proper order:

globalize.html

    [...]
  <script src="common/jquery.js"></script>

  <script src="cldr/cldr.js"></script>
  <script src="cldr/event.js"></script>
  <script src="cldr/supplemental.js"></script>

  <script src="globalize/globalize.js"></script>
  <script src="globalize/message.js"></script>
  <script src="globalize/number.js"></script>
  <script src="globalize/plural.js"></script>
  <script src="globalize/currency.js"></script>
  <script src="globalize/date.js"></script>

  <script src="cldr/cldr_data.js"></script>
  <script src="globalize/globalize-main.js"></script>
    [...]

Also add a couple of placeholders for our content:

globalize.html

    [...]
    <body>
        <h1 id="welcome"></h1>
        <p id="earnings"></p>
    </body>
    [...]

Now let's load a welcome message:

globalize-main.js

    jQuery(document).ready(function() {
      Globalize.loadMessages({
        "en": {
          'welcome': 'Welcome, {name}!'
            }
        });
    });

Here we are using the message-formatter module. Next, instantiate the Globalize class

    var globalize = new Globalize("en");

and populate the #welcome section:

    $('#welcome').text( globalize.messageFormatter('welcome')({name: 'John'}) );

Note that messageFormatter returns a function that we then call and pass the object containing a name. This can be re-written as

  var welcome_message = globalize.messageFormatter('welcome');
  $('#welcome').text( welcome_message({name: 'John'}) );

Actually, message's parameters do not need to be named - you may say 0, 1, 2 etc instead:

    'welcome': 'Welcome, {0}!'

In this case pass an array to the formatter:

    $('#welcome').text(globalize.messageFormatter('welcome')(['John']));

Next provide another message containing the today's day and total earnings:

    "en": {
      'welcome': 'Welcome, {0}!',
      'earned': 'Today is {date} and you\'ve earned {amount}!'
    }

In this example we'll use date and currency formatters:

  $('#earnings').text(
      globalize.messageFormatter('earned')({
        amount: globalize.formatCurrency(500.5, 'USD'),
        date: globalize.formatDate( new Date(), {
          datetime: "medium"
        })
      })
  )

When formatting the currency, we pass USD as the second argument. This argument is used to display a proper symbol when rendering the result. The symbol itself was defined inside the clrd_data.js file:

    "currencies": {
      "USD": {
        "symbol": "$"
      }
    }

medium is the name of the datetime format - it was also defined inside the clrd_data.js file as

    "dateTimeFormats": {
      "medium": "{1}, {0}"
    }

Here 1 is the date and 0 is time. Date and time, in turn, are formatted using the following masks:

    "dateFormats": {
      "medium": "MMM d, y"
    },
    "timeFormats": {
      "medium": "h:mm:ss a"
    }

Now that everything is ready you can observe the result!

PhraseApp Saves the Day Once Again

Managing translations for multiple languages can be tedious indeed. With PhraseApp, however, the whole process becomes much simpler.

PhraseApp supports a great range of various formats from simple to nested JSON (and specific formats for AngularJS or React). Next just add as many locales as you need and upload existing JSON files with translations if you have any.

Having done that you will be able to quickly understand which translations are missing, manage your keys and messages as well as download updated translations with one click. What's even cooler, you can easily request support from professional translators (which is probably better as localization is not only about translation). Therefore, I really encourage you to give PhraseApp a try!

Conclusion

So in this article, we've taken a look at various solutions helping you to localize your app: jQuery.I18n, Globalize and Polyglot. Polyglot appeared to be the smallest and simplest libraries, whereas Globalize and jQuery.I18n are quite big and complex - it's up to you which one pick!

Hopefully, you found this article useful and interesting. Thanks for staying with me and happy coding!

JavaScript Translation Locale (computer hardware) app Polyglot (computing) Template Attribute (computing)

Published at DZone with permission of Ilya Bodrov. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • Exploring Intercooler.js: Simplify AJAX With HTML Attributes
  • Mastering React App Configuration With Webpack
  • I Built an App With Remix in 30 Minutes
  • Angular vs. Flutter for Web App Development: Know Which One Is Best?

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!