Managing Translatable UI Text in JavaScript with RequireJS

Old world map

Internationalizing a web application includes the task of extracting user interface text for translations. When text is separated from the rest of your application business logic, you are able to localize and translate it easier. Although the JavaScript language and browser environment don’t prescribe any particular method for creating externalized text resources, many libraries exist to help this effort. One such library is RequireJS. The library includes an i18n plugin that helps you organize your text resources and load them at runtime depending on the needed language. The goal of this article is to describe how to use the RequireJS i18n plugin on a simple, single page application.

This sample application has a single html file index-before.html that contains text headers and other UI elements. We will extract the text, put it into a separate resource file, translate that file, and use the translatable files at runtime as needed by the customer’s language.

NOTE: This is not a RequireJS tutorial. The article assumes that you have some RequireJS knowledge.

Setting Up Your Environment

Download the js-localization project on Github. The project contains the source code for this article, allowing you to see code both before and after using the i18n plugin.

The project’s base directory is js-localization. Two HTML files are in this directory, index-before.html and index-after.html, which are the before- and after-internationalization files. The scripts subdirectory holds all JavaScript libraries for the project. All 3rd-party libraries, including RequireJS, are in scripts/libs. This application’s primary application file is scripts/main.js. Externalized text bundles are in scripts/nls.

Understanding the Original Index File

The original file looks like this:

    <meta charset="UTF-8">
    <title>Localization with RequireJS</title>
    <link href="styles/quotes.css" rel="stylesheet"/>
    <h1>Famous Quotation</h1>
    I love deadlines. I like the whooshing sound they make as they fly by.
    Douglas Adams

This file contains the following translatable items that we will extract for translation:

  • The title element contents
  • The h1 element contents
  • Two p element contents

Enabling Your HTML File

Include the RequireJS core library in your HTML file with a script element within the head section:

<script src="scripts/libs/require.js" data-main="scripts/main" charset="UTF-8"></script>

This script loads the require JavaScript file and also tells RequireJS to load your main module. The main module is the application’s starting point.

Creating I18n Text Modules

We need to pull out the text and put it into a separate text resource bundle. We will create text resource bundles in the scripts/nls subdirectory. RequireJS will look for resource bundles within the nls sudirectory unless you configure it otherwise. For our needs, we’ll create scripts/nls/text.js, put all exported text there, and provide a key for each string.

The updated HTML file maintains the same core document structure that is in the original file. However, we’ve removed the UI text. The HTML file, found in index-after.html now looks like this without text:

    <meta charset="UTF-8">
    <title id="titleTxt"></title>
    <link href="styles/quotes.css" rel="stylesheet"/>
    <script src="scripts/libs/require.js" data-main="scripts/main" charset="utf-8"></script>
    <h1 id="headerTxt"></h1>
    <p id="quoteTxt"></p>
    <p id="authorTxt"></p>

Where is the text? It’s in scripts/nls/text.js:

    "root": {
        "titleTxt": "Localization with RequireJS",
        "headerTxt": "Famous Quotations",
        "quoteTxt": "I love deadlines. I like the whooshing sound they make as they fly by.",
        "authorTxt": "Douglas Adams"

In general, each string in the original HTML file should be extracted into one or more nls resource bundles, which are really just JavaScript files. The sole purpose of a resource bundle is to define localizable resources. The files that define your primary set of language key-values are called root bundles. As part of a root bundle, a JavaScript file should define a root object that contains content for your application’s base language. This application’s root language is used when no target language can be found that matches the requested language.

Every piece of localizable text should have a key. The key is used to extract the original and translated text from the bundles. For example, headerTxt contains the label for “Famous Quotations”.

Adding Translated Text

Now that you’ve separated text into one or more resource bundles, you can send those files away for translation. For each target language, you will create a subdirectory in the nls directory. In this example, I used Google Translate to translate the text.js content into Japanese and Spanish. The nls subdirectories that contain translations must be named using standard BCP-47 language tags. The sub-directories for Japanese and Spanish are nls/js and nls/es respectively. Because there is only one source file, there will be only one file in each translation subdirectory.

Informing the Library

You must inform the RequireJS library about the available translations. In each source file that contains translations, you must add a language tag that matches the translation subdirectory name. We then have to update the nls/text.js file to notify the library for both Japanese and Spanish like this:

    "root": {
        "titleTxt": "Localization with RequireJS",
        "headerTxt": "Famous Quotations",
        "quoteTxt": "I love deadlines. I like the whooshing sound they make as they fly by.",
        "authorTxt": "Douglas Adams"
    "es": true,
    "ja": true

For each translation, you should include an additional language tag key in the root bundle. Since our root bundle nls/text.js has been translated into both Japanese and Spanish, we include those language tags and set their value to true, indicating that the translation exists.

Configuring RequireJS

RequireJS determines your customer’s language in one of two ways:

1. It uses your browser's language setting via the `navigator.language` or `navigator.languages` object.
2. It uses your customized, explicit configuration.

The navigator.language object is generally available across all browsers and represents the preferred language that is typically configurable in your browser’s language settings. This may be a reasonable default language, but I don’t recommend that a professional, consumer-facing application rely on this setting.

Instead, you should explicitly configure RequireJS to use a language that you select for the customer, with navigator.language as a backup perhaps. Your RequireJS configuration should be in the primary application JavaScript file. In our case, the scripts/main.js file contains this configuration as well as our application code.

If you set the i18n.locale configuration option for the i18n plugin, RequireJS will use that setting as your application’s language. By setting the value of this field, you control what language RequireJS will attempt to use. Set the language/locale option in the main.js file like this:

    config: {
        i18n: {
            locale: "en"

In an actual application, you will not hard-code this locale setting. Instead, you will determine your customer’s language another way, perhaps using navigator.language as a default.

Accessing the Resource Bundle

Once things are configured, using the resource bundle is easy. You just have to include it as a module in your main.js file. Then, you access each key using the name you give the module.

define(["jquery", "i18n!nls/text"],  function($, text) {

    // pull the text from the bundle and insert it into the document


In the above case, I’ve required two modules: jquery and “i18n!nls/text”. When you require a module using a plugin, you must append a “!” to the plugin name. After the plugin name, append the root resource bundle path. In this case, even though we have three languages, we point RequireJS to the root bundle. The i18n plugin will read the root bundle and discover the additional supported translations.

Since the code uses text as the module name, we can retrieve the text values by simply referencing the keys in the bundle. For example, if we want the quoteTxt, we reference it with text.quoteTxt in our code. The above code uses this technique to populate all the UI text in our simple HTML file.

Demonstrating the Plugin

We’ve setup the environment, configured the plugin, translated a file, and have modified our application so that it pulls translated text from the bundles. Now let’s see this work. You shouldn’t need any additional files or tools. Just point your browser to the index-after.html file on your local drive. If you’ve not changed anything, you should see the following English content:

Quote en

Now if you update the main.js file and change the i18n.locale setting to ja, you will see the next image. Remember, this is not a professional translation and is only used for an example.

Quote ja


Although JavaScript has no predefined framework for providing translatable resources, it is reasonably easy to use a library like the RequireJS i18n plugin to help manage UI text strings. Interestingly, the Dojo libraries work similarly to RequireJS. So, if you’re using Dojo, you will manage translations in your application in much the same way.

One of the most interesting parts of JavaScript internationalization is the question of how to determine the user’s preferred language. I’ve written about this before, so instead of handling that question here, I’ll refer you to Language Signals on the Web.

Good luck in your internationalization efforts. Like anything else, the hardest part is just getting started. Hopefully this article makes that first step easier.

JavaScript Internationalization Libraries

7564199138 f613b0fc16 m
Creating a browser-based web application requires a combination of HTML, CSS, JavaScript, and backend services. Creating an internationalized application requires additional techniques and libraries to provide and manage localized data formats and resources. This document focuses just on front-end JavaScript needs.

Depending on your application, your internationalization needs will be different. However, common concerns include these issues:

  • date, time, number, and currency formatting
  • text localization
  • pluralization and complex messages
  • sorting and collation

More demanding use-cases may even require these:

  • phone number formatting
  • alternative calendars

Finally, considering that EcmaScript now includes an internationalization API that is already supported in Chrome and Firefox, I think the following is important:

In my opinion, using a single library that supports all of the above is preferable to using multiple libraries. However, one solution doesn’t always work for every situation and a single solution doesn’t even seem to exist. You may have to use two or more different libraries to get all the functionality you need. For example, libraries that provide general number formatting support typically don’t provide phone number support.

Considering all the features above, you might consider these JavaScript internationalization libraries:

EcmaScript Intl Library

EcmaScript, the language specification for JavaScript, has defined a new Internationalization API. This API is already implemented in Chrome and Firefox browsers, and may be partially implemented in other browsers as well. The Internationalization API defines a new namespace Intl that provides objects for the following:

  • collation
  • number formatting
  • date/time formatting

The new Intl library is extremely important because it means that you get some internationalization support without loading an external library. Additionally, implementations support the CLDR data, which is the best-practice for format patterns.

Unfortunately, this API is not available in all browser-supplied JavaScript implementations, Safari and Internet Explorer being notable holdouts. Additionally, many mobile browsers do not yet provide the APIs.

If you need to use a separate library, you should favor those that provide a polyfill for this API when possible. This will make future transitions smoother.


The Intl.js library is a polyfill for the EcmaScript Intl library. It provides the number and date/time formatting APIs. However, it does not provide the collation API. Because it uses CLDR data, you can feel comfortable that the actual formats created by this library will be the same as–or very similar to–the EcmaScript Intl library. However, because collation isn’t available, you obviously have to look elsewhere if that is needed. If you don’t need collation, this may be a good option for basic data formatting.

JQuery Globalize

TheJQuery Globalize library now provides CLDR support. It also provides the number, date, and time formatting. However, it gives you a couple additional features above Intl.js that make it worth considering:

  • message translation API
  • pluralization

Message translation is a localization API for common message strings. The library defines a file format for translatable message strings and gives you API for retrieving those strings after translation.

Pluralization is a feature that lets you accommodate the differences in word choice that are needed when word forms depend on their count. For example, the pluralization library let’s you conveniently handle word choice for 0, 1, or n number of mouse or mice. Both Polish and Russian, for example, have several plural forms for 2, 3, 4, 5, or even more instances of a particular noun.


The Format.js library was recently released by Yahoo. It builds upon the Intl.js library and adds support for the following:

  • support for template libraries like Handlebars, React, and Dust
  • cache support for Intl format objects
  • ICU message syntax for pluralization, gender, and other types of message variability
  • relative time (5 min ago, 2 hours ago, etc)

The set of APIs is modular. If you only need the relative time support, you can load just that library instead of other items.

Dojo I18n

If you are already using Dojo, Dojo’s own internationalization libraries are an obvious choice. With support for string resource bundles, date and time formatting, and number and currency formatting, Dojo’s library has a lot to offer. This library’s additional benefit is its support for the CLDR patterns.

Phone Number Library

The libphonenumber library solves a very specific need for phone number parsing and formatting. If your application needs this, this well-supported library from Google should handle your common use-cases. In addition to JavaScript, the library exists for Java, C++, and other languages. The library helps you parse, format, and validate phone numbers for many countries of the world. Surprisingly, the library also helps you determine the type of a phone number, for example, fixed-line, mobile, toll-free, etc.


No single JavaScript library exists for all internationalization needs. However, you should be able to use one of either Intl.js, Globalize.js, Format.js, or Dojo for basic data formatting. For phone numbers, the libphonenumber library seems to be the only real choice for now, but it seems to be well supported and adequate for many use cases. Unfortunately, I haven’t found a widespread collation solution yet. However, given the typical size and download-time constraints for JavaScript applications, especially in mobile settings, sorting on the server side before transmitting sortable data may be a better solution for now.

Good luck in your own internationalization work in JavaScript. If you haven’t already adopted a set of support libraries, consider some of the ones mentioned above.

Providing a Language Selection List

Lang listThe questions pop up often enough in internationalization circles so I’ll address them here:

1. how should I localize a language selection list?
2. how should I sort it?

Localized Language Lists

Customers use a language selection list to change languages. You must assume that the currently selected language is inappropriate for some reason. One possible reason is that the user cannot read the page content. This means that a localized language list that displays all target language options in the language of my current page probably won’t be understood. For example, let’s pretend that I speak English but am on a Japanese web site. Presenting me with language options that include ” 英語” will not help me if I don’t read Japanese. I can’t be expected to know that those characters mean “English” to a Japanese person. This option is unhelpful. What is helpful?

The right way to represent any language selection list is to display languages in their own language and script. For example, English should be English, Spanish should be Español, Japanese is 日本語, etc. You don’t need to localize this list into every language. One list using the target language’s own language and script for each language choice is both sufficient and optimal. This guarantees that I’ll be able to read and select my target language regardless of the current page’s language setting. This is the most universal option you have, and I consider this a best practice for creating language selection lists.

Sort Order

I don’t know how better to prepare you for my answer…so here it is. The actual sort order is less important than consistency. Two points make this obvious to me:

1. if your customer wants to choose a different language, they probably don’t speak the current one, and the current language’s sort rules won’t be particularly useful anyway.
2. you can’t accurately guess what language rules you should use because you don’t know which language the user will select.

With these points in mind, I don’t think the sort order matters. Correct linguistic sorts for this list are not critical, and anything you choose will be inconvenient to someone. For this reason, I think you simply have to choose a sort order and be consistent every time you show the language list. My suggestion is that you simply order the list in U.S. English order if you consider U.S. English to be the base language of your product. If you consider your base language to be something else, use that. My point is that it doesn’t matter. Sure, you’ll be tempted to provide this list in the sorted order of the language of the current page or host OS setting. Sure that’s an option, but it’ll be incorrect more often than correct when it’s needed. Save your sanity. Choose an order. Be consistent. Don’t worry about localizing this order.

So this sort issue is bothering you still? You just can’t accept it? Ok, that’s fine, but consider this. The solution I’ve described is already used by some pretty big players. Since I just finished evaluating Facebook, let’s use it as an example. Regardless of which localized site I visit, regardless of my browser language preferences, Facebook shows the same list of languages in the same sort order. They don’t even use a US English sort. Their choice is something different, something almost like a US English sort, but maybe using the Romanized version of the target language? Here’s an example — Japanese is sorted with other languages that start with an “N” sound. The Japanese pronunciation is romanized as “nihongo”. So, “nihongo” starts with an n and sorts with other languages that start with n? I can’t quite figure the sort rules out BUT that’s my point…it doesn’t matter. Its consistent every time I go to the page, and it works. Here’s a shot of that Facebook page:

Fb lang selection


Providing a language selection list for your multilingual product is a great idea. It lets customers conveniently change the UI language of the product. Don’t over-think this problem. You can provide this feature without spending countless hours of debate. Follow my suggestions:

  1. Provide a single language selection list in which each entry is translated into the target language and script.
  2. Choose a sort order, any order. Be consistent in displaying this order.

Have a suggestion or comment? I’d enjoy hearing from you.




Job Post: Localization Engineer in San Jose, CA

Please comment on this post or email me if you are interested in this localization engineer position. I’ll put you in contact with the recruiter.

Direct from the recruiter:

The requirement is for a Localization Engineer in San Jose working for one of the leading Web Commerce companies.  They are looking for more than the typical Localization Engineer as they need someone who has done some programming, not just scripting.   I have attached a description and also listed below some things the manager said they have been missing in the candidates so far.

What I’ve been missing so far in candidates is

–        Experience with web localization (more a focus on i18n engineering, but then without real coding skills), and enterprise localization tools (e.g. WorldServer)

–        Significant modern coding skills (going beyond a simple VBA macro or Perl script), e.g. ability to write some Java application, understand/fix/enhance existing code, or a simple plug-in against a documented SDK (more than just passive knowledge of Java, etc)

–        Ability to clearly articulate concepts or thoughts, describe processes


If this sounds like a good fit, I can email you more details. Good luck!



Offensive Hand Gestures of the World

Recently I told some colleagues that I wanted to write a book about offensive hand gestures around the world. In my business, it’s common knowledge that icons of hand gestures just don’t localize well. We’re told to avoid using icons of hands and fingers because we might offend someone in a different culture. I’ve always known that anything can be offensive to someone somewhere, so of course I’ve accepted this as truth. Seems reasonable to avoid hand gestures when I can.

However, in all my years working in globalization and localization, I’ve never once been provided a comprehensive list of gestures that should be avoided. And not surprisingly, I’ve always wanted to see that list! In fact, I’ve wanted that list of offensive or rude gestures so much that I promised I’d write a book on the topic.

Oddly enough, it turns out that someone else has already beat me to the task. And I’ve discovered this not a moment too soon — I was already interviewing hand models. :) So, now that I know that someone has already beaten me to the job, I suppose I can now take that book off my todo list and just get a copy of it.

In my extensive research on the subject, I’ve found 3 books. THREE! On one of my late-night runs to Barnes and Noble, I saw the first one on my list — it was in the old discounted book pile. What a pity! Now pull out your credit card and take a look at these gems. I know I’ll order at least one of these:

  1. Rude Hand Gestures of the World
  2. Gestures: The Do’s and Taboos of Body Language Around the World
  3. 70 Japanese Gestures: No Language Communication

It’s really too bad too…I could really feel myself being pulled into this work. Maybe I can hope that the original authors do a 2nd edition and allow me to help? Man that would have been a fun book to research and write! But alas, I’m too late! But finally I’ve found the book I’ve wondered about for so long!


Twitter begins crowd-sourced translations

Recently I became aware of Twitter’s new translation initiative that enlists the help of its user base. I think it’s a unique and possibly worthwhile gamble. After all, the translations are free, and twitter pays it users by giving them a …. “special badge on their public Twitter page displaying their status as an official Twitter translator.”

The idea is simple enough. An invited twitterer/translator logs into their site. Having been identified as a translator, the user sees additional buttons on their twitter web UI…buttons that allow you to translate the page labels and text. Cool.

Twitter wants to translate into all the world’s language eventually. For now, however, they’ll settle on these in the short term:

  • French
  • German
  • Indonesian
  • Italian
  • Japanese
  • Korean
  • Spanish

Hey, great idea, Twitter. I wonder if the “special badge” of honor will be enough to enlist talented, real help?