Is there a way to reverse the formatting by Intl.NumberFormat in JavaScript
Asked Answered
S

10

78

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.

Stupid answered 25/3, 2015 at 12:35 Comment(1)
Does this answer your question? Is there any JavaScript standard API to parse to number according to locale?Abrasion
S
52

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.
Stupid answered 26/3, 2015 at 7:35 Comment(6)
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 tooVeedis
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].valueGurolinick
Parsing a value of "123abc" gives the result 123 instead of NaN.Trapani
It shall not work with currency values like $ 2,384.21. It returns NaN.Abrasion
Where is the regex class \p{Number} coming from? I cannot find any documentation about it. Nevermind I found it: developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/…Revolutionize
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.Coenzyme
T
18

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'));
Truckage answered 15/8, 2018 at 10:25 Comment(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.Ahron
@JohnLeidegren How does this look? gist.github.com/OliverJAsh/8eb1d4eb3ed455f86cc8756be499ba8eColony
@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.Ahron
R
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>
Reilly answered 4/1, 2021 at 19:25 Comment(1)
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.Abrasion
R
4

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
Radarman answered 11/5, 2022 at 5:28 Comment(0)
H
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);
  }
Halfon answered 16/10, 2015 at 10:49 Comment(1)
There's a minor issue: you have to replace all separators, so you have to use let thousandSeparator = new RegExp('\\' + comparer[1], 'g');Messenger
M
-1

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.

Monzon answered 21/1, 2019 at 19:35 Comment(0)
S
-1

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.

Suber answered 27/8, 2021 at 8:25 Comment(0)
L
-3

You should be able to just use:

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

As any added formatting is going to be non digits

Longitudinal answered 14/5, 2020 at 21:51 Comment(3)
No, this won't work for decimal numbers: "10.05".replace(/\D/g, '') => "1005"Stupid
Ah, I was just thinking about ints instead of floats. You are right, if you need decimals then it won't work.Longitudinal
It's actually an elegant solution for integers.Notus
M
-3

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
};
Mickeymicki answered 18/8, 2020 at 20:13 Comment(1)
€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.Mineral
S
-3

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

Stokes answered 23/1, 2022 at 11:58 Comment(1)
This way you'll get a higher number because decimals we'll be part of natural number.Kelula

© 2022 - 2024 — McMap. All rights reserved.