DEV Community πŸ‘©β€πŸ’»πŸ‘¨β€πŸ’»

Milanin
Milanin

Posted on

Formatting data like a pro in JavaScript

Formatting data in JavaScript might occasionally be challenging. I see improperly formatted data far too often. Did you know that JavaScript has a standardized built-in object designed just for that? I didn't either!
Intl is an object namespace for Internalization API, currently supported in >95% of all browsers!
So what can we do with it? I am going to show you how to use Intl to format units, plurals, and relative time. Aside from that, Intl supports date formatting, but we have long-established libraries for that, as well as formatting lists, comparing the order of letters, and segmenting sentences, words, and graphemes.

In this post I will cover:

  • Formatting units
  • Plural-sensitive formatting
  • Formatting relative time

What locales are supported?

All Intl functionality is heavily reliant on locale, but how can we determine which locales are supported? Intl has got this covered with its Intl.getCanonicalLocales(locales) function.
It accepts either a string containing a locale code or an array of strings and returns an array of accurate names for supported locales.

console.log(Intl.getCanonicalLocales(['EN', 'CS-CZ']));
Enter fullscreen mode Exit fullscreen mode

If we don't know the exact code name and don't know if it's supported at all, we can pass it into the function, capitalized. And it will return an array of supported locales with correct capitalization. So the output for this code looks like this:

["en","cs-CZ"]
Enter fullscreen mode Exit fullscreen mode

Formatting numbers

Intl contains a NumberFormat class which lets you format… numbers (duh). To use NumberFormat we must first create it, with the locale that we need, and options.

new Intl.NumberFormat(locale, options)
Enter fullscreen mode Exit fullscreen mode

More info about Intl.NumberFormat() and its options here.

Formatting units

The most important options for formatting units are style, unit, and unitDisplay. What do they do?
style manages how we want to format our numbers, and it accepts "decimal," "currency", "percent," or "unit." Since we want to format units, we’re going to set style to "unit."
unit means what kind of unit we want. All of the daily life units are supported by NumberFormat. You can find all of them here. Now let's go format some data.
unitDisplay tells if it should use the whole unit name or a short notation, e.g., kilogram for long, kg for short.

So let’s format kilogram units as an example. First, we create options:

const kilogramOptions = {
  style: 'unit',
  unit: 'kilogram',
  unitDisplay: 'short'
}; 
Enter fullscreen mode Exit fullscreen mode

And create the NumberFormat instance:

const kilogramFmt = new Intl.NumberFormat('en-UK', kilogramOptions);
Enter fullscreen mode Exit fullscreen mode

And finally you can format the number that you need with the format function.

const result = kilogramFmt.format(input);
Enter fullscreen mode Exit fullscreen mode

I’m passing en-UK into locale, but if you try passing ru, you will see that it uses Cyrillic instead of the alphabet. If you try passing in long to unitDisplay, and de as locale, it will capitalize the K in Kilogram; if you change locale to cs, you will see it in Czech. So that’s perfect! It localizes the units in all languages.
Now, how about we try plural-sensitive formatting next?

Plural-sensitive formatting

Intl provides yet another class to solve this problem, PluralRules. It lets us format cardinal and ordinal numbers. Quick explanation: Cardinal means: 1-one; 2-two; 3-three; and 4-four, and ordinal means: 1-first; 2-second; 3-third; and 4-fourth.
This is exactly what we are going to be passing into options for the PluralRules constructor: type: cardinal or ordinal, which looks exactly the same as NumberFormat:

new Intl.PluralRules(locales, options)
Enter fullscreen mode Exit fullscreen mode

Before we create our PluralRules formatter, here is an explaination on how it works.

  • When we pass a number into PluralRules with the cardinal type, it does not return "one", "two", "three", or "four", as I've mentioned before. It will return "zero", "one", "two", "few", "many", or "other", This is because instead of returning the exact value that we just passed in, it returns grammatical category of that number.

This depends on the locale that we use. With en-UK, you will see only one or other. You can experiment with different numbers and locales to see what results you get. Then with one … other you can format your data the way you need. e.g., with locale set to en-UK, if we get one, leave the word singular; if we get other make the word plural.

Now here comes the confusing part.

  • When we pass a number into PluralRules with the ordinal type... it returns the exact same values. Except this time it returns it as ordinal categories instead of cardinal. While with en-UK & ordinal we were getting only one or other, here we get "one", "two", "few" or "other".

We can use these values now to format -st, -nd, -rd and -th. Which I very often see done incorrectly, and this is why I find this class, PluralRules, helpful.

First we create our ordinal formatter:

const pluralFmt = new Intl.PluralRules('en-US', {
  type: 'ordinal'
});
Enter fullscreen mode Exit fullscreen mode

And now we can create our map for suffixes, and you will very quickly see how this class can be helpful for us.

const suffixes = new Map([
  ['one',   'st'],
  ['two',   'nd'],
  ['few',   'rd'],
  ['other', 'th']
]);
Enter fullscreen mode Exit fullscreen mode

And last step is format our number and remap the suffix.

const rule = pluralFmt.select(input);
const suffix = suffixes.get(rule);
return `${input}${suffix}`;
Enter fullscreen mode Exit fullscreen mode

And we're done! We can now successfully format our numbers.

Formatting relative time

The last thing I want to cover in this blog (as it's getting quite long) is formatting relative time. Now, by saying this, I don't mean anything like formatting dates. I mean something way more useful. We've all downloaded a file from the internet at some point. When you click download, you will see a sign telling you "15 minutes left" or something like this. This is exactly what we're going to do now!

First, we create the RelativeTimeFormat instance.

const relativeTimeFmt = new Intl.RelativeTimeFormat(locale, options);
Enter fullscreen mode Exit fullscreen mode

For us right now, the most important options are: numeric and style.
Numeric accepts always or auto.Β 
I recommend going with always, since auto could replace numbers with words, e.g., instead of "1 day ago" it will say "yesterday". This sounds like a good thing, but with some units other than days, it might be a problem, so keep it in mind.
Style accepts short, long and narrow. Short writes all units in shortened notation, and long writes out the whole words, e.g., "minute" for long and "min" for short.
Narrow is similar to "short" in some locales. This applies to the English locale.

Once we have done that we can finally format our relative time.

const result = relativeTimeFmt.format(input, unit);
Enter fullscreen mode Exit fullscreen mode

Note that input can be a negative number.
Supported units are: "year", "quarter", "month", "week", "day", "hour", "minute", "second".

Congratulations! Now you know how to properly format data in JavaScript!
This topic contains so much more information than I can possibly cover here. You can find all the examples used in this post here. And more about the Intl namespace here.
And if you don't want to go through all the hassle of creating objects, you can just use a library. A perfect example is Format.JS, which is a library based on the Intl namespace.
Thank you for reading this all the way through.

Top comments (0)

Classic DEV Post πŸ‘‡

Visualizing Promises and Async/Await 🀯

async await