Does JavaScript take local decimal separators into account?

Viewed 16290

I've got a web page that displays decimals in a user's localized format, like so:

  • English: 7.75
  • Dutch: 7,75

If I add two number variables together in JavaScript on my machine (where the numbers are taken from strings in the above formats) I get the following results:

  • English: 7.75 + 7.75 = 15.5
  • Dutch: 7,75 + 7,75 = 0

If I was to run this code on a Dutch users machine, should I expect the English-formatted addition to return 0, and the Dutch-formatted addition to return 15,5?

In short: Does the JavaScript calculation use local decimal separators in its string to number conversions?

7 Answers

Expanding on the solution from @naitsirch we can use Intl.NumberFormat.formatToParts() to have JS parse the group and decimal separators.

function parseLocaleNumber(stringNumber) {
  let num = 123456.789,
    fmt_local = new Intl.NumberFormat(),
    parts_local = fmt_local.formatToParts(num),
    group = '',
    decimal = '';

  parts_local.forEach(function(i) {
    switch (i.type) {
      case 'group':
        group = i.value;
        break;
      case 'decimal':
        decimal = i.value;
        break;
      default:
        break;
    }
  });

  return parseFloat(stringNumber
    .replace(new RegExp('\\' + group, 'g'), '')
    .replace(new RegExp('\\' + decimal), '.')
  );
}

//replace this string with a number formatted for your locale
console.log(parseLocaleNumber("987,654,321.01"));
//output for "en" locale: 987654321.01

Expanding on the solution from @OXiGEN we can use Intl.NumberFormat.formatToParts() to extract also the currency symbol :)

function parseLocaleNumber(stringNumber, locale, currency_code) {
  let num = 123456.789,
    fmt_local = new Intl.NumberFormat(locale, {
      style: 'currency',
      currency: currency_code
    }),
    parts_local = fmt_local.formatToParts(num),
    group = '',
    decimal = '',
    currency = '';

  // separators
  parts_local.forEach(function(i) {
    //console.log(i.type + ':' + i.value);
    switch (i.type) {
      case 'group':
        group = i.value;
        break;
      case 'decimal':
        decimal = i.value;
        break;
      case 'currency':
        currency = i.value;
        break;
      default:
        break;
    }
  });

  return parseFloat(stringNumber
    .replace(new RegExp('\\' + group, 'g'), '')
    .replace(new RegExp('\\' + decimal), '.')
    .replace(new RegExp('\\' + currency, 'g'), '')
  );
}

//replace inputs with a number formatted for your locale, your locale code, your currency code
console.log(parseLocaleNumber('$987,654,321.01', 'en', 'USD'));
//output for "en" locale and "USD" code: 987654321.01

Taking the symbol into account, this works in all cases:

parseLocaleNumber: function ( stringNumber ) 
{
    let thousandSeparator = (11111).toLocaleString().replace(/1/g, '');
    let decimalSeparator = (1.1).toLocaleString().replace(/1/g, '');
    let symbol = (0).toLocaleString()
        .replace(/0/g, '')
        .replace(new RegExp('\\' + decimalSeparator), '.')
        .trim();

    return parseFloat(
        stringNumber
            .replace(new RegExp('\\' + thousandSeparator, 'g'), '')
            .replace(new RegExp('\\' + decimalSeparator), '.')
            .replace(new RegExp( symbol ), '')
    );
}

2 details highlighted:

  1. use 11111 instead of 1111 because the first always shows thousand separator by default.
  2. Number format always uses '.' as decimal separator
Related