是否有任何 JavaScript 标准 API 可以根据语言环境解析为数字?

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

根据区域设置格式化数字,有一个标准 JavaScript API:Intl.NumberFormat

但是对于反向操作,将字符串解析为数字,我找不到任何标准 API 支持语言环境:

真的没有 JavaScript 标准 API 可以根据语言环境将字符串解析为数字吗?

如果没有:是否有任何已建立的市场、开源库可以这样做?

NPM 包 d2l-intl 提供了一个语言环境敏感的解析器。

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

不幸的是,该库仅支持 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。我不想把它归功于它,但我已经在这里复制它以供后代使用(根据我自己的喜好进行了一些细微的调整):

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

仅供思考的解决方案:

const decimalDigits = /\p{Decimal_Number}/u;

function replaceDecimalDigits(string) {
  return string.replace(new RegExp(decimalDigits.source, 'ug'), function (digit) {
    // The next code is using the property that decimal digits in unicode go one after another, the ranges also do not follow each other, except ranges in 0x1D7CE-0x1D799:
    let value = -1;
    do {
      value += 1;
      digit = String.fromCodePoint(digit.codePointAt(0) - 1);
    } while (decimalDigits.test(digit));
    return value % 10;
  }).replace(/[٫,\.]/g, '.').replace(/[٬]/g, '');
};

console.log(replaceDecimalDigits('١٬٢٣٤٫٥'));

可能是,也可以生成缓存...

“一个利用官方 Unicode CLDR JSON 数据进行国际化和本地化的 JavaScript 库。该库既适用于浏览器,也适用于 Node.js 模块。”

https://github.com/globalizejs/globalize

尝试 Mike Bostock 将 Intl.NumberFormat 工具强制转换为解析器。

来源:https://observablehq.com/@mbostock/localized-number-parsing

ES6:

class NumberParser {
  constructor(locale) {
    const parts = new Intl.NumberFormat(locale).formatToParts(12345.6);
    const numerals = [...new Intl.NumberFormat(locale, {useGrouping: false}).format(9876543210)].reverse();
    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;
  }
}

ES5:

var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();

    function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) arr2[i] = arr[i]; return arr2; } else { return Array.from(arr); } }

    function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

    var NumberParser = (function () {
        function NumberParser(locale) {
            _classCallCheck(this, NumberParser);

            var parts = new Intl.NumberFormat(locale).formatToParts(12345.6);
            var numerals = [].concat(_toConsumableArray(new Intl.NumberFormat(locale, { useGrouping: false }).format(9876543210))).reverse();
            var index = new Map(numerals.map(function (d, i) {
            return [d, i];
            }));
            this._group = new RegExp("[" + parts.find(function (d) {
            return d.type === "group";
            }).value + "]", "g");
            this._decimal = new RegExp("[" + parts.find(function (d) {
            return d.type === "decimal";
            }).value + "]");
            this._numeral = new RegExp("[" + numerals.join("") + "]", "g");
            this._index = function (d) {
            return index.get(d);
            };
        }

        _createClass(NumberParser, [{
            key: "parse",
            value: function parse(string) {
            return (string = string.trim().replace(this._group, "").replace(this._decimal, ".").replace(this._numeral, this._index)) ? +string : NaN;
            }
        }]);

        return NumberParser;
    })();

for de-DE 我是这样解析的:

function parseNumber(number: string): number {
  number = number
    .split('.').join('_')
    .split(',').join('.')
    .split('_').join(',')
  return parseFloat(number);
}

此库尝试处理所有语言环境。它检索包含数字的字符串,并尝试“猜测”数字来自哪个文化并将其转换为数字。

https://www.npmjs.com/package/number-parsing

使用方法如下:

var parser = require("number-parsing");
var a = parser("123'123.99USD"); // will return 123123.99
var b = parser("1234"); // will return 1234
var c = parser("123 123,777") // will return 123123.777
// and so on