There is a good chance that the application that you work on supports multiple languages. One of the things this includes is language-sensitive date handling. The most popular library that helps in achieving that is Moment.js. One of the things it handles is internationalization. Its source code contains localization for many different languages. Currently, this might not be the best approach, because we have the ECMAScript Internationalization API.
The Internationalization API aims to provide language-sensitive functionalities that many applications require. It helps you with tasks that need to take the language into consideration. Let’s look into various use cases in which you might find it useful. The browser keeps all of the above features in the Intl global object to avoid any name collisions.
Handling dates with DateTimeFormat
The Intl.DateTimeFormat is a constructor that lets us make use of language-sensitive date processing.
1 2 3 4 5 |
const date = Date.now(); console.log(new Intl.DateTimeFormat('en-US').format(date)); // 8/30/2019 console.log(new Intl.DateTimeFormat('da-DK').format(date)); // 30.8.2019 |
It contains a lot more than just rules for formatting numbers in dates. By supplying it with additional options, we can entrust it with translating strings such as week and month days.
1 2 3 4 5 6 7 8 9 |
new Intl.DateTimeFormat( 'en-US', { weekday: 'short', month: 'long', day: '2-digit' } ) .format(date) |
Fri, August 30
By choosing what we pass to the DateTimeFormat constructor, we can shape the result as we see fit. We can set properties like the weekday, era, and month to be long, short, or narrow. Numerical values, such as year and day, can be set to numeric (e.g., 1) or 2-digit (e.g., 01). The month can be represented either as a number or as a string.
We can also specify whether we want to use the 12-hour clock. The default setting here is dependent on the locale.
1 2 3 4 5 6 7 8 9 10 11 12 |
new Intl.DateTimeFormat( 'en-US', { day: '2-digit', month: 'long', year: 'numeric', hour: '2-digit', minute: '2-digit', hour12: true } ) .format(date) |
August 31, 2019, 11:04 AM
There are some additional options that you can use. For a full list, go to the MDN.
MDN Docs also mention dateStyle and timeStyle. Those properties are currently in the stage-3
Relative time format
With the ECMAScript Internationalization API, we can also handle relative time formatting depending on the language provided.
1 2 |
const formatter = new Intl.RelativeTimeFormat('en'); formatter.format(-1, 'day'); // 1 day ago |
By setting numeric to auto, we can also use string values, if available.
1 2 |
const formatter = new Intl.RelativeTimeFormat('en', { numeric: 'auto' }); formatter.format(-1, 'day'); // yesterday |
By setting the style to long, short, or narrow, you can configure the length of the message.
1 2 |
const formatter = new Intl.RelativeTimeFormat('en', { style: 'long' }); formatter.format(5, 'year'); // in 5 years |
1 2 |
const formatter = new Intl.RelativeTimeFormat('en', { style: 'short' }); formatter.format(5, 'year'); // in 5 yr. |
The narrow style could be similar to the short style for some locales.
Replacing Moment.js with Luxon
You might not be willing to replace many useful features of the Moment.js library for the native Date API just yet. An interesting alternative is Luxon. This project was started by one of the Moment.js maintainers that wanted to provide a bit different API but didn’t want to break anything in Moment.js. By writing a new library from scratch, he was able to change some significant things. The most important in the context of this article is the fact that Luxon uses the Intl API under the hood. Thanks to that, it does not have to ship internalization files as Moment does.
We also need to consider the browser support. Sine Luxon focuses on using native API, not everything is supported in every browser. If you are concerned about it, you might consider using polyfills. Check the support matrix for more information.
Other cool features of Internationalization API
The ECMAScript Internationalization API provides other useful functionalities. One of them is the ability to format lists.
Formatting lists with Intl.ListFormat
1 2 |
const list = ['Cat', 'Dog', 'Rat']; new Intl.ListFormat('en-GB', { style: 'long', type: 'conjunction' }).format(list); |
Cat, Dog and Rat
1 2 |
const list = ['Cat', 'Dog', 'Rat']; new Intl.ListFormat('en-US', { style: 'long', type: 'conjunction' }).format(list); |
Cat, Dog, and Rat
Notice that this API is very precise and takes into account even rather subtle differences like the usage of the serial comma before the “and” word in British and American English.
You can use either conjunction or the disjunction type to choose between Cat, Dog, and Rat and Cat, Dog, or Rat.
Language-sensitive string comparison
Another feature that might come in handy is the collator function. It comes in handy when comparing strings that might contain some language-specific characters. The “ä” letter makes a good example because it appears in both German and Swedish and the alphabetical order might differ.
1 |
new Intl.Collator('de').compare('ä', 'z'); // -1 |
1 |
new Intl.Collator('sv').compare('ä', 'z'); // 1 |
There are quite a few additional options that you might pass to the collator function. You can find a list in the MDN docs.
Plural rules
With the usage of Intl.PluralRules we can make use of plural sensitive formatting.
You can use it, for example, to figure out what plural form should you use in a specified language.
1 |
new Intl.PluralRules('en-US').select(1); // one |
Function resulted with “one”, so the proper form would be “one dog”.
1 |
new Intl.PluralRules('en-US').select(41); // other |
Function resulted with “other”, so the proper form would be “fourty one dogs”.
Another one of its use-cases is figuring out the ordinal numbers.
1 |
new Intl.PluralRules('en-US', { type: 'ordinal' }).select(41); // one |
Since the function resulted with “one”, the ordinal is: 41st
1 |
new Intl.PluralRules('en-US', { type: 'ordinal' }).select(32); // two |
Because the result is “two”, the ordinal is: 32nd
Formatting numbers
The rules in formatting numbers differ between languages and countries. Using the Intl.NumberFormat you can use proper formatting for a given country.
1 2 3 4 5 |
const number = 1025.15; new Intl.NumberFormat('en-US').format(number); // 1,025.15 new Intl.NumberFormat('ar-EG').format(number); // ١٬٠٢٥٫١٥ |
There are many different options that you can use.
1 2 3 4 |
const number = 125.5678; new Intl.NumberFormat('ar-EG', { style: 'currency', currency: 'DZD' }).format(number); //١٢٥٫٥٧ د.ج. new Intl.NumberFormat('en-GB', { style: 'currency', currency: 'GBP' }).format(number); // £125.57 |
Summary
In this article, we’ve gone through the basics features of the ECMAScript Internationalization API. It might prove to be useful to you on different occasions. I believe it is useful to know what if offers because it might often stop us from looking for an external library that might aim to do the same thing. Some of the above functionalities might not be supported in all browsers. It is worth checking it out beforehand and applying polyfills if necessary.