Reputation: 6931
I am trying to use Intl as a default currency formatter and it is almost perfect.
Using the example from a Intl.NumberFormat()
constructor:
const number = 123456.789;
console.log(new Intl.NumberFormat('de-DE', { style: 'currency',
currency: 'EUR' }).format(number)); // expected output: "123.456,79 €"
// the Japanese yen doesn't use a minor unit
console.log(new Intl.NumberFormat('ja-JP', { style: 'currency', currency: 'JPY'
}).format(number)); // expected output: "¥123,457"
This is almost perfect but I would actually like to drop the symbol from the output. So I would expect to see:
// expected output: "123.456,79"
// expected output: "123,457"
I find it bizarre that I spend over an hour looking for a solution and only found some sort of replace/trim usage.
Why there is not an option to format the number with all the Intl power but only dropping the currency symbol?!?
I hope I missed it, tbh.
Upvotes: 20
Views: 29640
Reputation: 28982
One simple way to do achieve what you want is to use String#replace()
to remove the currency from the string. To make this easier, you can set currencyDisplay
to "code"
which will use the ISO currency code - the same one passed in to currency
:
const number = 123456.789;
console.log(new Intl.NumberFormat('de-DE', {
style: 'currency',
currency: 'EUR',
currencyDisplay: "code"
})
.format(number)
.replace("EUR", "")
.trim()
); // 123.456,79
// the Japanese yen doesn't use a minor unit
console.log(new Intl.NumberFormat('ja-JP', {
style: 'currency',
currency: 'JPY',
currencyDisplay: "code"
})
.format(number)
.replace("JPY", "")
.trim()
); // 123,457
This can be extracted into a function:
const number = 123456.789;
console.log(format('de-DE', 'EUR', number)); // 123.456,79
console.log(format('ja-JP', 'JPY', number)); // 123,457
function format (locale, currency, number) {
return new Intl.NumberFormat(locale, {
style: 'currency',
currency,
currencyDisplay: "code"
})
.format(number)
.replace(currency, "")
.trim();
}
An alternative that allows you more control is to use Intl.NumberFormat#formatToParts()
which formats the number but gives you tokens that you can programmatically consume and manipulate. For example, using the method with locale = "de-DE"
and currency = "EUR"
you get the following output:
[
{
"type": "integer",
"value": "123"
},
{
"type": "group",
"value": "."
},
{
"type": "integer",
"value": "456"
},
{
"type": "decimal",
"value": ","
},
{
"type": "fraction",
"value": "79"
},
{
"type": "literal",
"value": " "
},
{
"type": "currency",
"value": "EUR"
}
]
Which means that you can easily filter out "type": "currency"
and then combine the rest into a string. For example:
const number = 123456.789;
console.log(format('de-DE', 'EUR', number)); // 123.456,79
console.log(format('ja-JP', 'JPY', number)); // 123,457
function format (locale, currency, number) {
return new Intl.NumberFormat(locale, {
style: 'currency',
currency,
currencyDisplay: "code",
})
.formatToParts(number)
.filter(x => x.type !== "currency")
.filter(x => x.type !== "literal" || x.value.trim().length !== 0)
.map(x => x.value)
.join("")
}
NOTE: the exclusion here: .filter(x => x.type !== "literal" || x.value.trim().length !== 0)
handles whitespace characters within the number. That might come up when using the option currencySign: 'accounting'
in the formatter. In some locales this will use parentheses for negative numbers which would leave a space inside if just the currency is removed:
const number = -123456.789;
const parts = new Intl.NumberFormat('ja-JP', {
style: 'currency',
currency: 'JPY',
currencySign: "accounting",
currencyDisplay: "code",
})
.formatToParts(number);
console.log(parts);
/* output:
[
{ type: "literal" , value: "(" },
{ type: "currency", value: "JPY" },
{ type: "literal" , value: " " },
{ type: "integer" , value: "123" },
{ type: "group" , value: "," },
{ type: "integer" , value: "457" },
{ type: "literal" , value: ")" }
]
*/
.as-console-wrapper { max-height: 100% !important; }
Thus negative numbers are handled correctly:
const number = -123456.789;
console.log(format('de-DE', 'EUR', number)); // 123.456,79
console.log(format('ja-JP', 'JPY', number)); // 123,457
function format (locale, currency, number) {
return new Intl.NumberFormat(locale, {
style: 'currency',
currency,
currencyDisplay: "code",
currencySign: "accounting",
})
.formatToParts(number)
.filter(x => x.type !== "currency")
.filter(x => x.type !== "literal" || x.value.trim().length !== 0)
.map(x => x.value)
.join("")
}
Thanks to Chris Peckham for pointing out potential pitfalls when using the accounting currency sign option.
Upvotes: 27
Reputation: 123178
If you only want the separators in between individual digits, just format as a number instead of using currency
:
const numberFormatter = new Intl.NumberFormat("en-US", {
// Do not show fractions - front end should only handle whole numbers
maximumFractionDigits: 0,
});
And then numberFormatter.format(asNumber);
with whatever number
you like.
For your example of 123456
, "de-DE"
is 123.456
, "ja-JP"
is 123,456
Upvotes: 4