Javascript number.toLocaleString currency without currency sign

Viewed 48307

Suppose we have

var number = 123456.789;

What I want is to display this number in locale 'de-DE' as

123.456,79

in locale 'ja-JP' as

123,457

in locale 'en-US' as

123,456.79

and so on according to user's locale. The problem is that Javascript's number.toLocaleString requires to specify currency sign and I can't find out how to tell to not display it at all.

What I tried:

number.toLocaleString('de-DE', { style: 'currency' }));
// TypeError: undefined currency in NumberFormat() with currency style

number.toLocaleString('de-DE', { style: 'currency', currency: '' }));
// RangeError: invalid currency code in NumberFormat():

number.toLocaleString('de-DE', { style: 'currency', currency: false }));
// RangeError: invalid currency code in NumberFormat(): false

number.toLocaleString('de-DE', { style: 'currency', currency: null }));
// RangeError: invalid currency code in NumberFormat(): null

The function also has option currencyDisplay. I tried the same values as above with currency option but with same result.


UPDATE (2020-11-25)

A few people pointed to .resolvedOptions(). It basically solves the question:

const currencyFractionDigits = new Intl.NumberFormat('de-DE', {
    style: 'currency',
    currency: 'EUR',
}).resolvedOptions().maximumFractionDigits;

const value = (12345.678).toLocaleString('de-DE', {
    maximumFractionDigits: currencyFractionDigits 
});

console.log(value); // prints 12.345,68

Thank you.

12 Answers

Here how I solved this issue. When I want to format currency without any signs, I format it with the currency code and then just remove 3-chars code from the result.

export function getCurrencyFormatWithSymbol(currencyCode) {
  return {
    style: 'currency',
    currency: currencyCode,
    currencyDisplay: 'symbol',
  }
}

export function getCurrencyFormatWithIsoCode(currencyCode) {
  return {
    style: 'currency',
    currency: currencyCode,
    currencyDisplay: 'code',
  }
}

export function getCurrencyFormatWithLocalName(currencyCode) {
  return {
    style: 'currency',
    currency: currencyCode,
    currencyDisplay: 'name',
  }
}

export function getCurrencyFormatNumbersOnly(currencyCode) {
  return {
    style: 'currency',
    currency: currencyCode,
    currencyDisplay: 'none',
  }
}

export function formatCurrency (value, format, lang) {
  const stripSymbols = (format.currencyDisplay === 'none')
  const localFormat = stripSymbols ? {...format, currencyDisplay: 'code'} : format
  let result = Intl.NumberFormat(lang, localFormat).format(value)
  if (stripSymbols) {
    result = result.replace(/[a-z]{3}/i, "").trim()
  }
  return result
}

Usage:

const format = getCurrencyFormatNumbersOnly('JPY')
formatCurrency(12345, format, 'ja')
formatCurrency(123456, format, 'ja')
formatCurrency(1234567, format, 'ja')
formatCurrency(12345678, format, 'ja')

Edit: The only minus, in this case, is the speed. On simple tasks, it will work perfectly. But if you are going to format a lot of numbers (for example, if you are fetching financial reports with raw data from backend and then format numbers according to user settings) this function can slow down your algorithms significantly and become a bottleneck on some browsers. So, test it carefully before using in production.

Here is a solution that isn't using regex and will deal with any locale, properly.

It uses the currency formatter of the locale and iterates all parts of it to exclude the literal and currency, properly, resulting in only getting the number as string. (Btw, the literal is the space between number and currency symbol).

const value = new Intl.NumberFormat('de-DE', {
    style: 'currency',
    currency: 'EUR',
}).formatToParts(12345.678).map(
    p => p.type != 'literal' && p.type != 'currency' ? p.value : ''
).join('')

console.log(value) // prints 12.345,68

You can use the currencyDisplay: 'code' option, and since you know the currency code you can easily replace it by the symbol you want :

return Intl.NumberFormat(language, {
    style: 'currency', currency: currency.code, currencyDisplay: 'code'
  }).format(amount).replace(currency.code, currency.symbol);

This way you're keeping all the currency formatting standards implied in NumberFormat and replacing only the symbol. In your case the custom symbol would be an empty string ('') and you may want to trim your string too with .trim().

Slight variation on the OPs answer including the minimumFractionDigits

const resolvedOptions = new Intl.NumberFormat('en-GB', { style: 'currency', currency: 'GBP', }).resolvedOptions();
const currencyOptions = {
    minimumFractionDigits: resolvedOptions.minimumFractionDigits,
    maximumFractionDigits: resolvedOptions.maximumFractionDigits
}
const value = (12345.678).toLocaleString('en-GB', currencyOptions)

The solution proposed in the OP won't work for the fr-CH locale because there is a distinction between a “currency amount” and a “non-currency amount”. The former uses a dot as decimal separator whereas the latter uses a comma:

const n = 1234.56
console.log(n.toLocaleString('fr-CH'))
console.log(n.toLocaleString('fr-CH', {
  style: 'currency',
  currency: 'CHF'
}))

Using .replace() either with a regex or directly with the currency code does seem to be the fastest solution but here is a solution with the .formatToParts() function of NumberFormat and how it can be used to solve the OP's question:

console.log(new Intl
  .NumberFormat('fr-CH', { style: 'currency', currency: 'CHF' })
  .formatToParts(1234.56) // returns an array of the different parts of the amount
  .filter(p => p.type != 'currency') // removes the currency part
  .reduce((s, p) => s + p.value, '') // joins the remaining values
  .trim())

You just need to split the string with the sign, and then get the second value of the array.

// value 350011
 const valueFormated = (value).toLocaleString('en-US', {
  style: 'currency',
  currency: 'USD',
 });

 valueFormated.split('$')
 // if you console.log the result would be
 (2) ['', '3,500.11']

 // here is the value formated
 valueFormated.split('$')[1]
 // 3,500.11

I found this thread by searching this use case, and the trick i use using class Intl.NumberFormat, with Regex of symbol it supported on Mozilla Firefox and Google Chrome.The trick is take currency symbol and using it as a needle for regex replace after localisation.

This sample code should do the trick:

var number = 123456.789;

function getCurrencySymbol(locale, currency) {
  return (0).toLocaleString(locale, {
    style: 'currency',
    currency: currency,
    minimumFractionDigits: 0,
    maximumFractionDigits: 0
  }).replace(/\d/g, '').trim();
}
var numeric_format = new Intl.NumberFormat('id-ID', { style: 'currency', currency: 'IDR', currencyDisplay: 'symbol' });
var localCurrencySymbol  = getCurrencySymbol('id-ID', 'IDR');
var CurrencySymbolNeedle = new RegExp(localCurrencySymbol, "g");

var amount = numeric_format.format(number);
console.log(localCurrencySymbol); // Rp
console.log(amount); // Rp 123.456,79
amount = amount.replace(CurrencySymbolNeedle, '').replace(/\s+/g, '');
console.log(amount); // 123.456,79

I don't test if this class support cross browser

Edit: Function to get currency symbol take from Get the currency symbol for a locale

Just put options as:

{ minimumFractionDigits: 2, maximumFractionDigits: 2 }

const s = 1234.567 
const options = { minimumFractionDigits: 2, maximumFractionDigits: 2 }
const result = new Intl.NumberFormat('pt-BR', options).format(s);

according to MDN you can use following format

new Intl.NumberFormat('de-DE', { maximumSignificantDigits: 3 }).format(number))

use maximumSignificantDigits option only

THIS WHAT YOU NEED TO DO

new Intl.NumberFormat("en-US").format(6700); //return 6,700.00 without currency,

Related