Search code examples
javascriptparsingnumberslocale

Is there any JavaScript standard API to parse to number according to locale?


For formatting a number according to locale, there is a standard JavaScript API: Intl.NumberFormat

But for the reverse action, parsing a string to a number I cannot find any standard API supporting locales:

Is there really no JavaScript standard API to parse a string to a number according to a locale?

And if not: are there any market established, open source libraries to do so?


Solution

  • The NPM package d2l-intl provides a locale-sensitive parser. It has since been superseded by @brightspace-ui/intl (essentially version 3), so some info below might or might not apply in its newer incantation.

    const { NumberFormat, NumberParse } = require('d2l-intl');
    const formatter = new NumberFormat('es');
    const parser = new NumberParse('es');
    const number = 1234.5;
    console.log(formatter.format(number));                 // 1.234,5
    console.log(parser.parse(formatter.format(1234.5)));   // 1234.5
    

    Unfortunately, that library only comes with support for a handful of locales out of the box. It also uses parseInt which only supports Western Arabic numerals, so for locales that use different numeral systems, you're going to have to get more clever. Here's one solution I found by Mike Bostock. I don't want to take credit for it, but I've reproduced it here for posterity (with some slight tweaks based on my own preferences):

    class NumberParser {
      constructor(locale) {
        const format = new Intl.NumberFormat(locale);
        const parts = format.formatToParts(12345.6);
        const numerals = Array.from({ length: 10 }).map((_, i) => format.format(i));
        const index = new Map(numerals.map((d, i) => [d, i]));
        this._group = new RegExp(`[${parts.find(d => d.type === "group").value}]`, "g");
        this._decimal = new RegExp(`[${parts.find(d => d.type === "decimal").value}]`);
        this._numeral = new RegExp(`[${numerals.join("")}]`, "g");
        this._index = d => index.get(d);
      }
      parse(string) {
        return (string = string.trim()
          .replace(this._group, "")
          .replace(this._decimal, ".")
          .replace(this._numeral, this._index)) ? +string : NaN;
      }
    }
    
    const formatter = new Intl.NumberFormat('ar-EG');
    const parser = new NumberParser('ar-EG');
    console.log(formatter.format(1234.5));               // ١٬٢٣٤٫٥
    console.log(parser.parse(formatter.format(1234.5))); // 1234.5