69

The Intl.NumberFormat (see Mozilla's doc) provides a nice way in Javascript to format numbers into a current locale`s version like this:

new Intl.NumberFormat().format(3400); // returns "3.400" for German locale

But I couldn't find a way to reverse this formatting. Is there something like

new Intl.NumberFormat().unformat("3.400"); // returns 3400 for German locale

Thanks for any help.

stollr
  • 6,534
  • 4
  • 43
  • 59
  • 1
    Does this answer your question? [Is there any JavaScript standard API to parse to number according to locale?](https://stackoverflow.com/questions/55364947/is-there-any-javascript-standard-api-to-parse-to-number-according-to-locale) – wladimirguerra Jul 08 '21 at 00:13

10 Answers10

45

I have found a workaround:

/**
 * Parse a localized number to a float.
 * @param {string} stringNumber - the localized number
 * @param {string} locale - [optional] the locale that the number is represented in. Omit this parameter to use the current locale.
 */
function parseLocaleNumber(stringNumber, locale) {
    var thousandSeparator = Intl.NumberFormat(locale).format(11111).replace(/\p{Number}/gu, '');
    var decimalSeparator = Intl.NumberFormat(locale).format(1.1).replace(/\p{Number}/gu, '');

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

Using it like this:

parseLocaleNumber('3.400,5', 'de');
parseLocaleNumber('3.400,5'); // or if you have German locale settings
// results in: 3400.5

Not the nicest solution but it works :-)

If anyone knows a better way of achieving this, feel free to post your answer.

Update

  • Wrapped in a complete reusable function
  • Using the regex class \p{Number} to extract the separator. So that it also works with non-arabic digits.
  • Using number with 5 places to support languages where numbers are separated at every fourth digit.
stollr
  • 6,534
  • 4
  • 43
  • 59
  • You can add a `+`sign before the return to cast to number. When I do this I usually want to convert into a numeric value too – Alwin Kesler Jan 17 '16 at 23:27
  • 4
    Love the function! Note though, that it is not necessary to find the thousand and decimal separators using RegEx. The `Intl API` also provides the `formatToParts()` function to easily extract the separator symbols: `const thousandSeparator = Intl.NumberFormat(i18n.language).formatToParts(11111)[1].value` `const decimalSeparator = Intl.NumberFormat(i18n.language).formatToParts(1.1)[1].value` – Marnix.hoh May 28 '21 at 07:26
  • Parsing a value of "123abc" gives the result 123 instead of NaN. – Iúri dos Anjos Jun 22 '21 at 17:19
  • It shall not work with currency values like `$ 2,384.21`. It returns `NaN`. – wladimirguerra Jul 08 '21 at 00:11
  • 1
    Where is the regex class `\p{Number}` coming from? I cannot find any documentation about it. Nevermind I found it: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions/Unicode_Property_Escapes – clapas Apr 13 '22 at 12:09
  • This doesn't work for when the thousandSeparator is a blank space. I had to special case that situation and then it seems to work. – chuckles May 26 '22 at 18:19
17

Here I have created a function for Reverse of format() function. This function will support reverse formatting in all locales.

function reverseFormatNumber(val,locale){
        var group = new Intl.NumberFormat(locale).format(1111).replace(/1/g, '');
        var decimal = new Intl.NumberFormat(locale).format(1.1).replace(/1/g, '');
        var reversedVal = val.replace(new RegExp('\\' + group, 'g'), '');
        reversedVal = reversedVal.replace(new RegExp('\\' + decimal, 'g'), '.');
        return Number.isNaN(reversedVal)?0:reversedVal;
    }

console.log(reverseFormatNumber('1,234.56','en'));
console.log(reverseFormatNumber('1.234,56','de'));
Saqib Ahmed
  • 1,240
  • 1
  • 15
  • 25
  • 3
    This is the right idea but we ran into issues as soon as we started using additional locales and formatting rules. There's a function called `formatToParts` which can be used instead of format to extract the components. You can then find the correct values for the separator and decimal point from that array from the `formatToParts` function. – John Leidegren May 29 '20 at 11:26
  • 1
    @JohnLeidegren How does this look? https://gist.github.com/OliverJAsh/8eb1d4eb3ed455f86cc8756be499ba8e – Oliver Joseph Ash Apr 07 '21 at 14:35
  • @OliverJosephAsh yeah, look about right. Just remember that not all parts will be there all the time. Only numbers in the thousands will have thousands separators and only decimal numbers will have the decimal point. – John Leidegren Apr 08 '21 at 05:38
4

I just solved it using group replacers

const exp = /^\w{0,3}\W?\s?(\d+)[.,](\d+)?,?(\d+)?$/g
const replacer = (f, group1, group2, group3) => {
return group3 ? 
            `${group1}${group2}.${group3}` : 
            `${group1}.${group2}`
}


const usd = '$10.15'.replace(exp, replacer)
// 10.15

const eu = '€01.25'.replace(exp, replacer)
// 1.25

const brl = 'R$ 14.000,32'.replace(exp, replacer)
// 14000.32

const tai = 'TAI 50.230,32'.replace(exp, replacer)
// 50230.32


// just to test!
const el = document.getElementById('output')

const reverseUSD = new Intl.NumberFormat('en-us', { style: 'currency', currency: 'USD' }).format(usd)

el.innerHTML += `<br> from: ${reverseUSD} to ${parseFloat(usd)}`

const reverseBRL = new Intl.NumberFormat('pt-br', { style: 'currency', currency: 'BRL' }).format(brl)

el.innerHTML += `<br> from: ${reverseBRL} to ${parseFloat(brl)}`

const reverseTAI = new Intl.NumberFormat('en-us', { style: 'currency', currency: 'TAI' }).format(tai)

el.innerHTML += `<br> from: ${reverseTAI} to ${parseFloat(tai)}`

const reverseEU = new Intl.NumberFormat('eur', { style: 'currency', currency: 'EUR' }).format(eu)

el.innerHTML += `<br> from: ${reverseEU} to ${parseFloat(eu)}`
<output id=output></output>
Erick Wendel
  • 402
  • 6
  • 14
  • 2
    Great answer, but it has an issue. Conversion of numbers without decimal places will have wrong result. Eg. `R$ 2.393` will result `0.393`. – wladimirguerra Jul 07 '21 at 23:45
2

what I've done so far is a multi step approach which you can see in the below code. nf is the NumberFormat service. This function takes the formatted number as well as the used locale. Now we create a comparator by dividing 10k by 3 thus guaranteeing a decimal and thousandSeparator at a fixed position. Then remove the thousand separator and all other non-numeric signs, like currency symbols. after that we replace the decimal separator with the english one and finally return a casted number.

  uf(number, locale) {
    let nf = this.nf({}, locale);
    let comparer = nf.format(10000 / 3);

    let thousandSeparator = comparer[1];
    let decimalSeparator = comparer[5];

    // remove thousand seperator
    let result = number.replace(thousandSeparator, '')
    // remove non-numeric signs except -> , .
      .replace(/[^\d.,-]/g, '')
    // replace original decimalSeparator with english one
      .replace(decimalSeparator, '.');

    // return real number
    return Number(result);
  }
zewa666
  • 2,593
  • 17
  • 20
  • There's a minor issue: you have to replace all separators, so you have to use `let thousandSeparator = new RegExp('\\' + comparer[1], 'g');` – k102 Nov 29 '18 at 11:09
2

You can use the formatToParts method available on the instance, this is generally better as you can ignore the currency and group separators this way

  function convertToFloat(number, locale = 'en', currency = 'AUD') {
  const instance = new Intl.NumberFormat(locale, {
    style: 'currency',
    currency,
  });
  const roundedValue = instance.formatToParts(number)
    .filter(part => !['currency', 'group'].includes(part.type))
    .reduce((acc, part) => `${acc}${part.value}`, '').replace(/,/g, '.');
  // then just parse it as a float
  return [instance.format(number), '->', parseFloat(roundedValue)];
}
console.log(convertToFloat(1234.56)); // -> 1234.56
console.log(convertToFloat(1234.56, 'de-DE', 'EUR')); // -> 1234.56
Shannon Hochkins
  • 11,763
  • 15
  • 62
  • 95
0

Not sure of the relevance of this approach performance-wise, but it's always good to have several options, so here is another one :

function getNumPrice(price, decimalpoint) {
    var p = price.split(decimalpoint);
    for (var i=0;i<p.length;i++) p[i] = p[i].replace(/\D/g,'');
    return p.join('.');
}

In my case the locale is set from PHP, so I get it with <?php echo cms_function_to_get_decimal_point(); ?>, but obviously one can use the division trick suggested in other answers instead.

Skippy le Grand Gourou
  • 6,976
  • 4
  • 60
  • 76
0

I have tried the accepted answer and agree with that. So I upvoted as well. I may have another workaround without specifying any local, but instead look for the separator manually.

P.s. The digit argument is used to specify the amount of decimal places.

parseLocaleNumber(stringnum: string, digit: number): number {
    let retValue: number = parseFloat(stringnum);
    var arr: string[] = stringnum.split('');
    arr.slice().reverse().forEach((x, i, arr) => {
        if (i === digit) {
            if (x === '.') {
                retValue = parseFloat(stringnum.split(',').join(''));
                arr.length = i + 1;
            } else if (x === ',') {
                retValue = parseFloat(stringnum.split('.').join(''));
                arr.length = i + 1;
            }
        }
    });
    return retValue;
}

Example to use this method:

console.log(parseLocaleNumber('123,456,789.12'));
// 123456789.12

The code is written with the use of TypeScript.

JW Geertsma
  • 857
  • 3
  • 13
  • 19
-1

You should be able to just use:

value.replace(/\D/g, '');

As any added formatting is going to be non digits

Ben
  • 872
  • 7
  • 18
-1

Is not very clean, but works for me:

//value is "in-En" format --> $1,200.51
function unformatter(value){
    value = value.replace("$","");
    value = value.replace(",","");
    value = parseFloat(value);
    return(value); //returns --> 1200.51
};
  • `€1.499,99` will produce `NaN`, and even with adding `€` to be removed it would produce `1.49999` instead of the correct `1499.99` so that won't work. There is no generic way to solve this without knowing the decimal separator of the input locale. – CherryDT May 04 '22 at 16:41
-1

A dummy solution maybe

const getInformattedMoney = (formattedValue) => Number(formmattedValue.replaceAll('.','').replaceAll(',','.').replace(`${currencySymbol}`,''))

For me the . (dot) is thousand seperator and the , (comma) is double seperator

Keyyubi
  • 247
  • 1
  • 3
  • 13