At some point when you build applications with a wide user base, you find that you need to support various combinations of languages and locales. This could be in the form of having to support a locale from the browser, or maybe a user has defined their preferred locale in their profile, so you need to display data in a different locale from the browser default. Taking a step back from translating text, which most likely involves you defining files in multiple languages, you can handle the formatting of dates and numbers in your applications. There are multiple libraries that have been able to help you do this over the years, going all the way back to Dojo Toolkit to modern libraries like luxon. But you can accomplish most of your formatting needs with the native Intl Web API.

Number Formatting

Did you know that numbers can have different formatting? Take, for example, the number 88888.88. In English US formatting it is “88,888.88”, but in French European it would be “88 888,88”. As a front-end developer, you might be furrowing your brow at the prospect of trying to manage this. Traditionally internationalization, or i18n, isn’t an easy task. Assume this particular workflow:

  1. A user logs in
  2. They view information on how many cats are roaming the globe
  3. They want to see cat numbers formatted to their locale
  4. That’s a lot of cats!

As a developer, how do you determine the user’s locale? You can use navigator.language, which will return the browser’s preferred language. This is something the user can change in their browser based on their preference. If your application allows users to set their preferred locale in their own application-level settings, you could use that option, then fall back to the browser locale if none is set.

Suppose we have some raw data that we want to format for the user that has a locale of fr-FR. We can use the Intl.NumberFormat to create a formatter for our values.

const values = [
	101010.10,
	484739.23,
	239945.88,
	310098.44
];
const formatter = new Intl.NumberFormat(navigator.language);
const formattedValues = values.map((value) => formatter.format(value));
// ['101 010,1', '484 739,23', '239 945,88', '310 098,44']

You can now use these formatted values to display in your application. There are a number of options you can specify if you need to. You can set the formatting for currency, percent, unit of measurement, or decimals. Now, if I had a nickel for every cat in my report, I can actually figure out how much money I would have. Exciting!

const values = [101010.1, 484739.23, 239945.88, 310098.44];
const formatter = new Intl.NumberFormat(navigator.language, {
	style: "currency",
	currency: "USD",
	minimumFractionDigits: 2
});
const formattedValues = values.map(
	(value) => formatter.format(value * 0.05)
);
// ['$5,050.51', '$24,236.96', '$11,997.29', '$15,504.92']

Those nickels add up. As you can see, you can customize your formatting options as needed for your users.

Time for Dates

We’ve already seen the power of being able to format numbers with Intl. The Intl API also provides a DateTimeFormat you can use to keep dates consistent with locales in your application too. When viewing our reports of cat sightings, we might want to display the dates of those sightings as well.

Let’s try this out with the default options Intl.DateTimeFormat in the en-US locale.

const dateStrings = [
	"January 1, 1996, 2:30 PM",
	"September 2, 2003, 10:45 PM",
	"August 20, 2011, 11:15 AM",
	"March 8, 2020, 6:30 AM"
];
const dates = dateStrings.map(value => new Date(value));
const formatter = new Intl.DateTimeFormat("en-US");
const formattedValues = dates.map(
	(value) => formatter.format(value)
);
// ['1/1/1996', '9/2/2003', '8/20/2011', '3/8/2020']

If you are not from the United States, you probably recognize right away that these date formats make no sense to you. If we were using the fr-FR locale, our dates would look like this.

['01/01/1996', '02/09/2003', '20/08/2011', '08/03/2020']

You can now let out a sigh of relief.

There are also various options for formatting the date and time to your liking.

const formatter = new Intl.DateTimeFormat("en-US", {
	year: "numeric",
	month: "numeric",
	day: "numeric",
	hour: "numeric",
	minute: "numeric",
	hour12: false
});
// ['1/1/1996, 14:30', '9/2/2003, 22:45', '8/20/2011, 11:15', '3/8/2020, 06:30']

As with NumberFormat, you can adjust the display of dates as needed.

We can expand on some of these options, and maybe assume we are using a fr-FR locale.

const formatter = new Intl.DateTimeFormat("fr-FR", {
	weekday: "long",
	year: "numeric",
	month: "long",
	day: "numeric",
	hour: "numeric",
	minute: "numeric",
	dayPeriod: "short"
});
// ["lundi 1 janvier 1996 à 14:30","mardi 2 septembre 2003 à 22:45","samedi 20 août 2011 à 11:15","dimanche 8 mars 2020 à 06:30"]

You might even allow your users to configure these localization options in their own user settings! That would make your cat tracking application the talk of the cat tracking community.

Related to formatting dates and times is the ability to format relative times. This is one of those features that you don’t realize how useful it is until you need it.

const formatter = new Intl.RelativeTimeFormat(navigator.language, {
  localeMatcher: "best fit",
  numeric: "auto",
  style: "long",
});

formatter.format(1, “day”);
// tomorrow
formatter.format(1, “week”);
// next week

This is a front-end developer’s dream come true. No more date math and less than or greater than checks to display relative time in sentence format. You could even ask the formatter to always use a  numeric format.

const formatter = new Intl.RelativeTimeFormat("en-US", {
  localeMatcher: "best fit",
  numeric: "always",
  style: "long",
});

formatter.format(1, “day”);
// in 1 day
formatter.format(1, “week”);
// in 1 week

There are numerous options available for relative time formatting to meet your specific needs. This gives you the flexibility to provide your users with the best user experience possible.

Summary

There are a number of other methods available with Intl, we just covered a handful of the ones you might use on a regular basis. Dates and numbers are some very basic objects in JavaScript, however, displaying them for an international user base can provide some challenges. We used to have to lean on third-party libraries and toolkits for these tasks or perform lots of date math to determine time in days, weeks, or months. Today, with modern web development, we can leverage native Web APIs like Intl for internationalization. It’s well worth your time to learn the basics of Intl to help provide your users with a better experience.