DEV Community

Cover image for Number and Currency Formatting in JavaScript using Intl.NumberFormat
Schalk Neethling
Schalk Neethling

Posted on

Number and Currency Formatting in JavaScript using Intl.NumberFormat

While this post does not by any means aim to cover all the amazing things you can do with Intl.NumberFormat, I do cover two very common use cases you may have faced when formatting numbers in JavaScript. As with most things concerning programming, there are many ways to achieve the same end result, this is one of my favorite APIs when formatting numbers in JavaScript.

NOTE: Feel free to share how you handle number formatting with JavasScript in the comments.

First things first, support for Intl.NumberFormat and the related format function across runtime and browser environments is excellent so you can safely follow this post and use Intl.NumberFormat in production.

Basic number formatting

Let's say you have a magical coin that superficially increases in value every 500 milliseconds until it reaches 2 million at which point it stops.

const increment = 1000;
const maxCoinValue = 2000000; // two million
let magicCoinCurrentValue = 0;

const magicalCoinMiner = setInterval(() => {
  if (magicCoinCurrentValue < maxCoinValue) {
    magicCoinCurrentValue = magicCoinCurrentValue + increment;
    console.log(magicCoinCurrentValue);
  } else {
    clearInterval(magicalCoinMiner);
  }
}, 500);
Enter fullscreen mode Exit fullscreen mode

As the number grows ever bigger, the readability of the number will suffer as we are currently outputting the number without any formatting.

1000 # one thousand
...
30000 # thirty thousand
...
400000 # four hundred thousand
...
1800000 # one million eight hundred thousand
Enter fullscreen mode Exit fullscreen mode

Let's address the formatting using Intl.NumberFormat and the related format function.

const increment = 1000;
const maxCoinValue = 2000000; // two million
let magicCoinCurrentValue = 0;

const magicalCoinMiner = setInterval(() => {
  if (magicCoinCurrentValue < maxCoinValue) {
    magicCoinCurrentValue = magicCoinCurrentValue + increment;
    const formattedValue = Intl.NumberFormat().format(magicCoinCurrentValue);

    console.log(formattedValue);
  } else {
    clearInterval(magicalCoinMiner);
  }
}, 500);
Enter fullscreen mode Exit fullscreen mode

With this change, the output will now look as follows:

1,000 # one thousand
...
30,000 # thirty thousand
...
400,000 # four hundred thousand
...
1,800,000 # one million eight hundred thousand
Enter fullscreen mode Exit fullscreen mode

I am sure that you can agree that this new format is a lot easier to read. Right now, we are using the default NumberFormat but we can make this locale aware by specifying the locale as the first parameter to NumberFormat like so:

const formattedValue = Intl.NumberFormat("no").format(magicCoinCurrentValue);

console.log(formattedValue);
Enter fullscreen mode Exit fullscreen mode

The no value sets the locale to Norwegian. With this, the numbers will now be formatted as follows:

1 000 # one thousand
...
30 000 # thirty thousand
Enter fullscreen mode Exit fullscreen mode

If we change the locale to de for German, the numbers will be formatted as follows:

1.000 # one thousand
...
30.000 # thirty thousand
Enter fullscreen mode Exit fullscreen mode

The numbers we are printing are monetary values so they are missing a decimal value and a currency symbol. One way we can do this is by using JavaScript template literals to append and prepend the pieces we are missing.

console.log(`$${formattedValue}.00`);
Enter fullscreen mode Exit fullscreen mode

This is not ideal for several reasons. Our decimal value is hard coded as is our currency symbol. It is also hard to read and error-prone. In addition, the hard-coded decimal value is especially problematic.

Currencies

One of the many things NumberFormat can help us with is with currencies. To get both the currency symbol and our decimals without the need for the template literal or hard coding, we can specify the style and currency parameters for the NumberFormat constructor:

const formattedValue = Intl.NumberFormat('en', {currency: 'USD', style: 'currency'}).format(magicCoinCurrentValue);

console.log(formattedValue);
Enter fullscreen mode Exit fullscreen mode

NOTE: You need to specify the locale or undefined (which will use the default locale) or the options object will be ignored.

With this configuration, we will get the following output:

$1,000.00 # one thousand
...
$30,000.00 # thirty thousand
Enter fullscreen mode Exit fullscreen mode

Pretty neat, right?! There are a couple of things to be aware of though. Making our output locale aware can result in surprising output if you are not used to how monetary values are displayed in different locales.

What do I mean by that?

Well, if we change the example above to use the de locale and the Euro currency symbol you might expect output like, €1.000.00.

const formattedValue = Intl.NumberFormat('de', {currency: 'EUR', style: 'currency'}).format(magicCoinCurrentValue);

console.log(formattedValue);
Enter fullscreen mode Exit fullscreen mode

However, this is the output you will get:

1.000,00 € # one thousand
...
30.000,00 € # thirty thousand
Enter fullscreen mode Exit fullscreen mode

There is nothing wrong with this and it is the way currencies are commonly displayed in countries that are part of the European Union. However, if you want the currency symbol displayed on the left, keep the en locale.

const formattedValue = Intl.NumberFormat('en', {currency: 'EUR', style: 'currency'}).format(magicCoinCurrentValue);

console.log(formattedValue);
Enter fullscreen mode Exit fullscreen mode

Now the output will be as follows:

€1,000.00 # one thousand
...
€30,000.00 # thirty thousand
Enter fullscreen mode Exit fullscreen mode

Do note that the number separators have also changed to match the locale. What happens if we do something like this?

const formattedValue = Intl.NumberFormat('en', {currency: 'CAD', style: 'currency'}).format(magicCoinCurrentValue);
Enter fullscreen mode Exit fullscreen mode

This will result in the following:

CA$1,000.00 # one thousand
...
CA$30,000.00 # thirty thousand
Enter fullscreen mode Exit fullscreen mode

What happens if we change the locale to ca for Canada?

const formattedValue = Intl.NumberFormat('ca', {currency: 'CAD', style: 'currency'}).format(magicCoinCurrentValue);
Enter fullscreen mode Exit fullscreen mode

Now we get:

1.000,00 CAD # one thousand
...
30.000,00 CAD # thirty thousand
Enter fullscreen mode Exit fullscreen mode

NOTE: The number separator has also changed above.

But what if you wanted it to display just the dollar symbol? That is where the currencyDisplay property comes into play:

const formattedValue = Intl.NumberFormat('en', {currency: 'CAD', currencyDisplay: 'narrowSymbol', style: 'currency'}).format(magicCoinCurrentValue);
Enter fullscreen mode Exit fullscreen mode

NOTE: I have also changed the locale above to en.

Now we get:

$1,000.00 # one thousand
...
$30.000.00 # thirty thousand
Enter fullscreen mode Exit fullscreen mode

Conclusion

As you can tell from the above, and this only touches the surface, Intl.NumberFormat is incredibly powerful especially its ability to ease the localization of number formatting and currencies.

I hope you found this quick look at the basics of this API helpful and that it sparked your curiosity to learn more. Think about some of the projects you are working on at the moment, are there areas where you can take advantage of this to simplify and make your code more robust?

Until the next one! Keep making the web awesome, open, and accessible. 🙏

Resources

Top comments (2)

Collapse
 
dev-i-am profile image
Derek

There is no such locale 'ca' for Canada. it's en-CA or fr-CA. This number is European 1.000,00. English Canada is $100,000.00

Collapse
 
schalkneethling profile image
Schalk Neethling

That explains a lot :) Thank you, Derek. As mentioned I did find that to be kinda strange.