For AI agents: the complete documentation index is available at /tc39-atlas/llms.txt, the full documentation bundle is available at /tc39-atlas/llms-full.txt, and this page is available as Markdown at /tc39-atlas/proposals/stage/4/proposal-unified-intl-numberformat.md.
  • 简体中文
  • Intl.NumberFormat Unified API Proposal S4

    中文标题:Intl.NumberFormat 统一 API 提案

    提案概览
    提案速览

    该提案为 Intl.NumberFormat 扩展了度量单位、紧凑表示法、科学计数法、符号显示选项和会计货币格式。它还清理了规范并添加了窄货币符号选项。

    Note

    以下 README 来自上游仓库,其中的阶段或状态标注可能滞后;当前信息以提案概览为准。

    Intl.NumberFormat 统一 API 提案

    本提案为 Intl.NumberFormat 添加了度量单位紧凑十进制表示法以及其他本地化数字格式功能。

    TC39 第二阶段提案

    TC39 第三阶段提案

    查看规范

    Polyfill

    ** 状态 **

    本提案已合并到 ECMA-402 中,并计划纳入 2020 年版规范。

    https://github.com/tc39/ecma402/pull/404

    请在主要的 ECMA-402 仓库上提交任何其他问题。

    https://github.com/tc39/ecma402/issues

    背景 / 动机

    有许多请求希望在 ECMA-402(JavaScript 的 Intl 标准库)中添加与数字格式相关的功能。其中包括:

    这些功能对最终用户和 Google 都很重要。由于这些功能中的大多数需要携带大量的区域设置数据才能进行正确的国际化支持,因此通过 JavaScript API 公开这些功能可以减少带宽,并降低采用国际化最佳实践的门槛。

    为了避免使用更多功能重叠的构造函数使 Intl 变得复杂,本提案旨在重构 Intl.NumberFormat 的规范,使其能够以“统一”的方式更轻松地支持其他功能。

    其他背景:先前的讨论

    I. 单位

    测量单位可以按如下方式格式化:

    (299792458).toLocaleString("en-US", {
        style: "unit",
        unit: "meter-per-second",
        unitDisplay: "short"
    });
    // ==> "299,792,458 m/s"

    该语法在 #3 中进行了讨论。

    • style 接收字符串值 "unit"
    • unit 接收一个字符串核心单位标识符,定义在 UTS #35, 第 2 部分, 第 6 节 中。从 完整列表 中选择了 一部分单位 用于 ECMAScript;关于选择该子集的方法论讨论见 #39。
    • unitDisplay,以货币对应的设置 currencyDisplay 命名,接受 "narrow"、"short" 或 "long"。

    特性检测:度量单位

    当传递 style: "unit" 时检查 RangeError:

    /**
     * 如果支持该单位,则返回一个 Intl.NumberFormat,
     * 如果不支持则返回 null。
     */
    function getIntlNumberFormatWithUnit(unit) {
      try {
        return new Intl.NumberFormat(undefined, {
          style: "unit",
          unit
        });
      } catch (e) {
        if (e.constructor !== RangeError) {
          throw e;
        }
        return null;
      }
    }

    II. 科学计数法和紧凑表示法

    科学计数法和紧凑表示法由新的选项 notation 表示,可以按如下方式格式化:

    (987654321).toLocaleString("en-US", {
        notation: "scientific"
    });
    // ==> 9.877E8
    
    (987654321).toLocaleString("en-US", {
        notation: "engineering"
    });
    // ==> 987.7E6
    
    (987654321).toLocaleString("en-US", {
        notation: "compact",
        compactDisplay: "long"
    });
    // ==> 987.7 million

    该语法在 #5 中进行了讨论。

    • notation 接受 "standard"(默认)、"scientific"、"engineering"、"compact" 之一
    • compactDisplay,仅在 notation 为 "compact" 时使用,接受 "short"(默认)或 "long" 之一

    舍入相关的设置(最小/最大整数/小数位数)在数字根据所选表示法进行缩放后应用。

    notation 为 "compact" 且没有用户指定的舍入选项时,会使用特殊的紧凑舍入策略:四舍五入到最接近的整数,但始终保留 2 位有效数字。例如,123.4K 舍入为 123K,1.234K 舍入为 1.2K。如果 notation 为 "compact" 没有指定任何舍入设置,用户可以在 resolvedOptions 中判断正在使用紧凑舍入策略。

    表示法样式可以与其他选项组合:

    (299792458).toLocaleString("en-US", {
        notation: "scientific",
        minimumFractionDigits: 2,
        maximumFractionDigits: 2,
        style: "unit",
        unit: "meter-per-second"
    });
    // ==> 3.00E8 m/s

    特性检测:表示法

    resolvedOptions() 中检查表示法:

    /**
     * 如果支持该表示法,则返回一个 Intl.NumberFormat,
     * 如果不支持则返回 null。
     */
    function getIntlNumberFormatWithNotation(notation) {
      let numberFormat = new Intl.NumberFormat("en-US", { notation });
      if (numberFormat.resolvedOptions().hasOwnProperty("notation")) {
        return numberFormat;
      }
      return null;
    }

    III. 符号显示

    正数上可以显示符号:

    (55).toLocaleString("en-US", {
        signDisplay: "always"
    });
    // ==> +55

    货币会计符号显示也通过一个新选项支持。在许多区域设置中,会计格式意味着用括号将数字括起来,而不是附加减号。例如:

    (-100).toLocaleString("bn", {
        style: "currency",
        currency: "EUR",
        currencySign: "accounting"
    });
    // ==> (১০০.০০€)

    该语法在 #6 中进行了讨论:

    • signDisplay: "auto"(默认)、"always"、"never"、"exceptZero"
      • 请参阅下面的示例。
    • currencySign: "standard"(默认)、"accounting"
      • "accounting" 在货币值上启用会计格式,如上面的示例所示。默认值 "standard" 禁用会计格式。

    signDisplay 选择的示例(使用拉丁数字):

    signDisplay-1-001NaN
    auto-1-001NaN
    always-1-0+0+1+NaN
    never1001NaN
    exceptZero-100+1NaN

    currencySign 为 "accounting" 时,signDisplay 选择的示例(使用拉丁数字):

    signDisplay-1-001NaN
    auto($1.00)($0.00)$0.00$1.00$NaN
    always($1.00)($0.00)+$0.00+$1.00+$NaN
    never$1.00$0.00$0.00$1.00$NaN
    exceptZero($1.00)$0.00$0.00+$1.00$NaN

    像往常一样,这可以与其他选项组合。

    (0.55).toLocaleString("en-US", {
        style: "percent",
        signDisplay: "exceptZero"
    });
    // ==> +55%

    特性检测:符号显示

    resolvedOptions() 中检查 signDisplay:

    /**
     * 如果支持 signDisplay,则返回一个 Intl.NumberFormat,
     * 如果不支持则返回 null。
     */
    function getIntlNumberFormatWithSignDisplay(signDisplay) {
      let numberFormat = new Intl.NumberFormat("en-US", { signDisplay });
      if (numberFormat.resolvedOptions().hasOwnProperty("signDisplay")) {
        return numberFormat;
      }
      return null;
    }

    IV. 规范清理

    规范的某些部分已根据以下目标进行了重构:

    此外,现有 currencyDisplay 设置添加了一个缺失的选项:"narrowSymbol",它使用 CLDR 窄格式符号:

    (100).toLocaleString("en-CA", {
        style: "currency",
        currency: "USD",
        currencyDisplay: "narrowSymbol"
    });
    // ==> "$100" (而不是 "US$100")

    特性检测:窄货币符号

    当传递 currencyDisplay: "narrowSymbol" 时检查 RangeError:

    /**
     * 如果支持窄货币,则返回一个 Intl.NumberFormat,
     * 如果不支持则返回 null。
     */
    function getIntlNumberFormatWithNarrowCurrency(currency) {
      try {
        return new Intl.NumberFormat(undefined, {
          style: "currency",
          currency,
          currencyDisplay: "narrowSymbol"
        });
      } catch (e) {
        if (e.constructor !== RangeError) {
          throw e;
        }
        return null;
      }
    }