How to Implement Internationalization (i18n) in JavaScript
This article was peer reviewed by Julian Motz, Panayiotis Velisarakos, Vildan Softic and Tim Severien. Thanks to all of SitePoint’s peer reviewers for making SitePoint content the best it can be!
So, you’re a developer working on the next big thing. Your customers love your product and it’s getting rave reviews on all the social networks. Even better, the CEO of the company has just secured $10,000,000 in funding to expand into new markets. But before you can go global, the product must be updated to support different languages, currencies, date formats, and much more. And guess what? You and your team are the ones in charge of making this happen. In technical terms, your software must be internationalized and then localized.
Internationalization (also known as i18n) is the process of creating or transforming products and services so that they can easily be adapted to specific local languages and cultures. Localization (also known as L10n) is the process of adapting internationalized software for a specific region or language. In other words, internationalization is the process of adapting your software to support multiple cultures (currency format, date format, and so on), while localization is the process of implementing one or more culture.
These two processes are usually adopted by companies that have interests in different countries, however they might also come in handy for a single developer working on their own site. For example, as you might know, I’m Italian and I own a website. My website is currently in English but I might decide to internationalize it and then localize it into Italian. This is beneficial for those that are native Italian speakers and aren’t well accustomed to the English language.
In this article I’ll introduce you to Globalize, a JavaScript library for internationalization and localization developed by some members of the jQuery team. All the snippets demonstrated in this article can be found on our GitHub repository.
But before delving into Globalize, I’d like to conclude this brief introduction on i18n with the words of Rafael Xavier de Souza, the lead of the project:
Developers think i18n is about translations to non-English languages. That i18n is only needed for expanding the current application to multiple countries or markets. I always try to explain that i18n is about “talking” in general. Every application, at some point, has to “talk” to its users. To talk with the users, the application may require pluralization support, gender inflection, date formatting, number formatting, and currency formatting. Even in English, it might be tricky to get this done properly.
What is Globalize?
Globalize is a JavaScript library for internationalization and localization that leverages the official Unicode CLDR JSON data.
The library is open source and it’s developed by Rafael Xavier de Souza along with some of the members of the jQuery team.
Globalize is based on the Unicode Consortium’s Common Locale Data Repository (CLDR), the largest and most extensive standard repository of locale data available. So, unlike libraries that embed locale data, if you use Globalize it’s really easy to always be up-to-date with the latest CLDR data.
The library works both for the browser and as a Node.js module. Globalize 1.0 supports all major browsers including IE9+, Chrome, Firefox, Safari 5.1+, and Opera 12.1+.
The main features of the library are:
- Number formatting and parsing
- Date and time formatting and parsing
- Relative time formatting
- Currency formatting
- Message formatting
- Plural support
- Unit support
One of the things I like the most about Globalize is that it has a module for each of its features. A developer may not need the whole library and can thus cherry-pick the module(s) needed. Another interesting feature is that, unlike other libraries, it keeps the code separated from the content by not hosting or embedding any locale data in the library.
But Globalize isn’t the only show in town. If you’re interested in some alternatives, Rafael has a dedicated page. The most notable alternative is i18next.
Globalize and the JavaScript Internationalization API
To some of you this may come to a surprise, but JavaScript has native support for internationalization in the form of the Internationalization API (also known as ECMA-402). The Intl
object is an object available on the window
object which acts as a namespace for the Internationalization API. This API currently provides methods to format numbers and dates, and to compare strings in a specific language.
Now that you know of the existence of the Internationalization API, you could be led into thinking that Globalize uses it behind the scenes. This approach would surely lead to better date and number formatting performance. However, because the support is low and very inconsistent among browsers, the library doesn’t use it.
Before we move on and start the discussion on Globalize, I want to give you a taste of the Internationalization API.
Formatting a Date
The first example I’m going to show uses the Internationalization API to format a date in several locales: IT, US, and GB.
// 30th of June 2016
var date = new Date(2016, 5, 30);
// "30/6/2016"
console.log(new Intl.DateTimeFormat('it-IT').format(date));
// "6/30/2016"
console.log(new Intl.DateTimeFormat('en-US').format(date));
// "30/06/2016"
console.log(new Intl.DateTimeFormat('en-GB').format(date));
In this example, I employ the DateTimeFormat
constructor to create a new date formatter using the specified locale (“it-IT”, “en-US”, and “en-GB”). Then, I invoke the format
method to format the date object.
The code above is also available as a JSBin.
Formatting a Number
As mentioned before, the API also allows you to format number. An example that employs the NumberFormat
constructor is shown below:
var number = 1302.93;
// "1.302,93"
console.log(new Intl.NumberFormat('it-IT').format(number));
// "1,302.93"
console.log(new Intl.NumberFormat('us-US').format(number));
// "1,302.93"
console.log(new Intl.NumberFormat('en-GB').format(number));
By looking at the output of this second snippet, also available as a JSBin, you can notice that in Italy we format numbers differently compared to the USA and the UK.
As I mentioned earlier, the support for this API is low but in case you want to use it, you can employ this polyfill in your application.
Now that I’ve given you a better idea of how internationalization and localization work, let’s discuss Globalize.
Installing and Using Globalize
Globalize can easily be installed via npm:
npm install globalize cldr-data --save
This command also installs the CLDR data which is necessary to load the locale data (e.g. how numbers or dates are formatted in a certain language) that Globalize will use. With these two packages installed, we’re ready to use the library.
Note: the following examples assume Node. If you are interested in using Globalize in the browser, I recommend starting with one of the examples on the project’s homepage. The webpack example makes it especially easy to get up and running fast.
Next, I’ll employ Globalize to rewrite the two snippets listed in the previous section.
Formatting a Date
The first example can be implemented as shown below:
// Include the Globalize library
var Globalize = require('globalize');
// Include the CLDR data
var cldrData = require('cldr-data');
// Loads the supplemental data
Globalize.load(cldrData.entireSupplemental());
// Loads the data of the specified locales
Globalize.load(cldrData.entireMainFor('it', 'en', 'en-GB'));
// 30th of June 2016
var date = new Date(2016, 5, 30);
// "30/6/2016"
console.log(Globalize('it').formatDate(date));
// "6/30/2016"
console.log(Globalize('en').formatDate(date));
// "30/06/2016"
console.log(Globalize('en-GB').formatDate(date));
Despite its simplicity, the code above allows me to cover a few topics. The first thing that felt a bit weird to me the first time I played with Globalize, is that some of the language codes used by the CLDR data use only two letters. For consistency, I expected all the locales to require the full version of the ISO 3166 standard (e.g. “it-IT” and “en-US”) instead of the short version (e.g. “it” and “en”). While assuming Italian of Italy as the default seems legit (Italian originated in Italy after all), it was confusing for English. In fact, “en” represents American English and not British English. If you want to be sure not to make the same mistake I did, I suggest you to take a look at this table.
Another concept worth outlining is the entireSupplemental
method (3rd statement of the code). This loads all the files that contain supplemental information for a country or its locale data. For example the telephone country code (39 for Italy), the population, some well-known abbreviations, how to spell other countries’ currencies, and much more.
The last point I want to cover is the fourth statement where I invoke the entireMainFor
method. This allows to load the locale data for the desired countries (in the above example Italy, USA, and Great Britain).
Formatting a Number
To format a number, Globalize provides the formatNumber
method. The signature of the method is
formatNumber(value[, options])
where value
is the number to format and options
is an object used to customize the return value of the method. Some examples of the options that you can specify are:
round
: defines how the number will be rounded. Its value can be any of the following:ceil
,floor
,round
, ortruncate
useGrouping
: A Boolean indicating whether a grouping separator should be usedminimumIntegerDigits
: a non-negative integer indicating the minimum integer digits to be used.
The complete list of options available can be found in the documentation.
Now that we’ve learned more about the formatNumber
method, let’s see it in action.
// Include the Globalize library
var Globalize = require('globalize');
// Include the CLDR data
var cldrData = require('cldr-data');
// Loads the supplemental data
Globalize.load(cldrData.entireSupplemental());
// Loads the data of the specified locales
Globalize.load(cldrData.entireMainFor('it', 'en', 'en-GB'));
var number = 1302.93;
// "1.302,93"
console.log(Globalize('it').formatNumber(number));
// "1,302.93"
console.log(Globalize('en').formatNumber(number));
// "1,302.93"
console.log(Globalize('en-GB').formatNumber(number));
Formatting Currency Values
The library provides a currencyFormatter
method to help you format currency values. This method supports many options that let you define if you want to round the number, if you want to use the symbol of the currency (e.g. “$”) or its code (e.g. “USD”), and much more.
An example of use of currencyFormatter()
is shown below:
// Include the Globalize library
var Globalize = require('globalize');
// Include the CLDR data
var cldrData = require('cldr-data');
// Loads the supplemental data
Globalize.load(cldrData.entireSupplemental());
// Loads the data of the specified locales
Globalize.load(cldrData.entireMainFor('en'));
var enGlobalize = Globalize('en');
var value = 229.431;
var usdFormatter = enGlobalize.currencyFormatter('USD');
// "$229.43"
console.log(usdFormatter(value));
var eurFormatter = enGlobalize.currencyFormatter('EUR', {
style: 'code',
round: 'ceil'
});
// "229.44 EUR"
console.log(eurFormatter(value));
Parsing Numbers
Parsing numbers can also be a task you have to perform, perhaps when dealing with user inputs. The following examples demonstrates how to do that:
// Include the Globalize library
var Globalize = require('globalize');
// Include the CLDR data
var cldrData = require('cldr-data');
// Loads the supplemental data
Globalize.load(cldrData.entireSupplemental());
// Loads the data of the specified locales
Globalize.load(cldrData.entireMainFor('en'));
// Set default locale
var enGlobalize = Globalize('en');
var numberParser = enGlobalize.numberParser();
// "229.431"
console.log(numberParser('229,431.00'));
var percentParser = enGlobalize.numberParser({style: 'percent'});
// "0.5341"
console.log(percentParser('53.41%'));
Formatting Relative Dates
Another very common feature in modern web apps is to show times and dates in relative terms. For example, instead of showing the full date of a day, you’ll usually find labels such as “yesterday” and “last week”. Achieving this task with Globalize is straightforward thanks to the relativeTimeFormatter
method. An example of use is displayed below:
// Include the Globalize library
var Globalize = require('globalize');
// Include the CLDR data
var cldrData = require('cldr-data');
// Loads the supplemental data
Globalize.load(cldrData.entireSupplemental());
// Loads the data of the specified locales
Globalize.load(cldrData.entireMainFor('en'));
// Set default locale
var enGlobalize = Globalize('en');
var dayFormatter = enGlobalize.relativeTimeFormatter('day');
// "yesterday"
console.log(dayFormatter(-1));
var yearFormatter = enGlobalize.relativeTimeFormatter('year');
// "next year"
console.log(yearFormatter(1));
Globalize offers many other methods that I haven’t presented in this article. However, the topics covered here should have given you enough information to get you up and running. Moreover, the library’s documentation is very detailed.
Conclusion
In this article I discussed what internationalization and localization are and why they are important to expand a product’s market. I briefly introduced you to the Internationalization API by mentioning some supported features and then, I showed some examples of their use.
In the second part of the article I introduced you to Globalize, a JavaScript library for internationalization and localization developed by the jQuery team. The library is very powerful and comes with all the methods you might need to internationalize your project, for example: methods to parse numbers, format dates, and format currency values. If you think the library has saved you time, feel free to give something back by contributing to the project.
Once again, I want to remind you that all the snippets demonstrated in this article can be found on our GitHub repository.