Formatting data like a pro in JavaScript
Tags: javascript, webdev, programming, tutorial
Published: 2022-11-30 20:49:52

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.

```js
console.log(Intl.getCanonicalLocales(['EN', 'CS-CZ']));
```

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:

```js
["en","cs-CZ"]
```


## 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`.

```js
new Intl.NumberFormat(locale, options)
```
More info about `Intl.NumberFormat()` and its options [here](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/PluralRules/PluralRules).

### 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:

```js
const kilogramOptions = {
  style: 'unit',
  unit: 'kilogram',
  unitDisplay: 'short'
}; 
```

And create the NumberFormat instance:

```js
const kilogramFmt = new Intl.NumberFormat('en-UK', kilogramOptions);
```

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

```js
const result = kilogramFmt.format(input);
```

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`:

```js
new Intl.PluralRules(locales, options)
```

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:

```js
const pluralFmt = new Intl.PluralRules('en-US', {
  type: 'ordinal'
});
```

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

```js
const suffixes = new Map([
  ['one',   'st'],
  ['two',   'nd'],
  ['few',   'rd'],
  ['other', 'th']
]);
```

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

```js
const rule = pluralFmt.select(input);
const suffix = suffixes.get(rule);
return `${input}${suffix}`;
```

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.

```js
const relativeTimeFmt = new Intl.RelativeTimeFormat(locale, options);
```

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.

```js
const result = relativeTimeFmt.format(input, unit);
```

Note that input can be a negative number.
Supported `unit`s 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](https://codepen.io/milansav/pen/oNyyKxy). And more about the `Intl` namespace [here](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl).
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](https://formatjs.io/)**, which is a library based on the `Intl` namespace.
Thank you for reading this all the way through.