Making Numbers Speak Your Language in JavaScript
Published: 2023-09-24 19:20:27


Numbers are a universal language, but how they are formatted and presented can greatly impact user experience, especially in a global context. In web development, `Intl.NumberFormat` is the tool that allows you to format numbers in a way that's not only easy to read but also culturally sensitive. In this blog post, we'll dive into the world of `Intl.NumberFormat` and explore how it can help you speak your users' language when it comes to numbers.

## Introduction to `Intl.NumberFormat`

To get started with `Intl.NumberFormat`, you need to create an instance of it, specifying the user's locale. Here's a basic example:

```javascript
const formatter = new Intl.NumberFormat('en-US');
```

In this example, we create a `formatter` that formats numbers according to the English (United States) locale.

To format a number, simply call the `format` method of the `formatter` instance:

```javascript
const formattedNumber = formatter.format(1234567.89);
console.log(formattedNumber); // "1,234,567.89"
```

The `formattedNumber` now contains a string representing the number 1234567.89 in the 'en-US' locale.

## Customizing Number Formatting

One of the key features of `Intl.NumberFormat` is the ability to customize how numbers are formatted. You can achieve this by passing an options object when creating the formatter. Here are some common options you can use:

- `style`: You can choose between `decimal` (default), `currency`,  `percent` or `unit`.
- `notation`: it determines how the number will be displayed. Possible values includes
  - standard: the default number format, and the only type that can be used with currencies
  - scientific: returns the order of magnitude of the number
  - engineering: returns the exponent of 10 if the number is greater than 999
  - compact: an abbreviated version e.g 1K instead of 1,000

```ts
new Intl.NumberFormat("en-GB", {
  notation: "standard",
}).format(1001246) // 1,001,246

new Intl.NumberFormat("en-GB", {
  notation: "scientific",
}).format(1001246) // 1.001E6

new Intl.NumberFormat("en-GB", {
  notation: "scientific",
}).format(100) // 1E2

new Intl.NumberFormat("en-GB", {
  notation: "engineering",
}).format(1001246) // 1.001E6

new Intl.NumberFormat("en-GB", {
  notation: "engineering",
}).format(100) // 100E0


new Intl.NumberFormat("en-GB", {
  notation: "compact",
}).format(1001246) // 1M
```

`Intl.NumberFormat` allows you to format numbers in four different ways namely

1. Decimals e.g. 3.14
2. Currency e.g. $340
3. Percent e.g. 3.14%
4. Unit e.g. 3.14 kg


### Currency
Of course, the first thing you want to do is to set the `style` to `currency`, and now determine the currency type.

The following options applies to currency

- `currency`: specifies the type of currency that you want to use e.g. USD for U Dollars. The full list of the codes can be found [here](https://en.wikipedia.org/wiki/ISO_4217#List_of_ISO_4217_currency_codes). This is ***required*** if the style is currency.
- `currencyDisplay`: Determines how the currency is represented, that is, as `symbol` (default to the locale e.g. $ or US$), `narrowSymbol` ($), `code` (USD) or `name` (US Dollars)
- `currencySign`: (`standard` or `accounting`) in most locale, negative numbers are preceded with `-` symbol. You can change it to `accounting` where negative numbers are surrounded by parentheses instead, following accounting standards

```ts
new Intl.NumberFormat("fr-FR", {
  style: "currency",
  currency: "EUR",
}).format(1837747) // 1 837 747,00 €


new Intl.NumberFormat("en-US", {
  style: "currency",
  currency: "EUR",
}).format(203325) // €203,325.00


new Intl.NumberFormat("en-GB", {
  style: "currency",
  currency: "GBP",
}).format(1001246) // £1,001,246.00

```

#### `currencyDisplay`

The locale will usually affect the way the currency are written, this can be override with `currencyDisplay`
It determines how the currency would be represented or written. It accept the following values
- symbol: the default value, uses the local symbol
- narrowSymbol: makes uses of symbol without any extra letters e.g. `$` instead of `$US`
- name: uses the local name of the currency like dollars or pounds
- code: uses the ISO currency code

```ts
new Intl.NumberFormat("fr-FR", {
    style: "currency",
    currency: "USD",
}).format(1837747) // 1 837 747,00 $US

new Intl.NumberFormat("fr-FR", {
  style: "currency",
  currency: "USD",
  currencyDisplay: "narrowSymbol",
}).format(1837747) // 1 837 747,00 $


new Intl.NumberFormat("fr-FR", {
  style: "currency",
  currency: "USD",
  currencyDisplay: "name",
}).format(1837747) // 1 837 747,00 dollars des États-Unis


new Intl.NumberFormat("en-US", {
  style: "currency",
  currency: "USD",
  currencyDisplay: "name",
}).format(1837747) // 1,837,747.00 US dollars


new Intl.NumberFormat("fr-FR", {
  style: "currency",
  currency: "USD",
  currencyDisplay: "code",
}).format(1837747) // 1 837 747,00 USD
```

#### `currencySign`
This determines how negative numbers are represented.
- standard: the default way of representing negative numbers, that is, `-200`
- accounting: this uses accounting/finance standards where negative numbers are represented with parenthesis, that is, `(200)`


If you want your currency to stick with accounting standards where negative numbers are represented with parenthesis, set currencySign to `accounting`


```ts
new Intl.NumberFormat("en-GB", {
  style: "currency",
  currency: "GBP",
}).format(-1001246) // -£1,001,246.00

new Intl.NumberFormat("en-GB", {
  style: "currency",
  currency: "GBP",
  currencySign: "accounting",
}).format(-1001246) // (£1,001,246.00)

```

### Unit

This allows the formatting of unit measurements. This accepts the following options
- unit: It is required if the style is `unit`, and it refers to units like `gram` or `liter`. Full list of the available units can be found [here](https://tc39.es/ecma402/#table-sanctioned-single-unit-identifiers)
- unitDisplay: Determine how the units would be written, the default value is `short`

```ts

new Intl.NumberFormat("en-GB", {
  style: "unit",
  unit: "kilometer",
}).format(1001246) //1,001,246 km


new Intl.NumberFormat("en-GB", {
  style: "unit",
  unit: "kilometer",
  unitDisplay: "short",
}).format(1001246) // 1,001,246 km


new Intl.NumberFormat("en-GB", {
  style: "unit",
  unit: "kilometer",
  unitDisplay: "long",
}).format(1001246) // 1,001,246 kilometres


new Intl.NumberFormat("en-GB", {
  style: "unit",
  unit: "kilometer",
  unitDisplay: "narrow",
}).format(1001246) // 1,001,246km

```

### Percent
It will return percentage (%) based on the provided value. It basically multiplies the value provided by 100 and returns it as a percent, so 1 will be 100%.


```ts
new Intl.NumberFormat("en-GB", {
  style: "percent",
}).format(1) // 100%


new Intl.NumberFormat("en-GB", {
  style: "percent",
}).format(0.35) // 35%


new Intl.NumberFormat("en-GB", {
  style: "percent",
}).format(35) // 3,500%
```

## Browser Support

`Intl.NumberFormat` is well-supported in modern web browsers, including Chrome, Firefox, Safari, and Edge. However, if you need to support older browsers, consider using a polyfill like `intl-numberformat` to ensure consistent behavior.

## Conclusion

`Intl.NumberFormat` is an invaluable tool for formatting numbers in web applications. It allows you to present numeric data in a way that is not only culturally sensitive but also customizable to meet your application's specific needs. By mastering `Intl.NumberFormat`, you can provide a more user-friendly and globally accessible experience, making your web application more inclusive and appealing to users from around the world.