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/year/pending/proposal-intl-sequence-units.md.
  • 简体中文
  • Intl Sequence Units ?

    中文标题:Intl 序列单位

    提案概览
    提案速览

    该提案规定了一种在 Intl.NumberFormat 中格式化测量单位序列(例如英尺和英寸,或米和厘米)的机制。它扩展 Intl.NumberFormat 以支持由 -and- 分隔符连接的复合单位标识符,其中 format 和 formatToParts 接收与序列子单位对应的值数组。它定义了验证、提取、组件格式化、使用 Intl.ListFormat 进行组合,以及针对长度不匹配、混合符号和非整数中间值的错误处理。

    Note

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

    Intl 序列单位

    本提案规定了一种在 Intl.NumberFormat 中格式化测量单位序列(例如“英尺和英寸”或“米和厘米”)的机制。

    演示文稿:

    • 第 114 次 TC39 会议(2026 年 5 月):Stage 1 & 2 - 幻灯片 | 笔记
    • 第 115 次 TC39 会议(2026 年 7 月):幻灯片
    • 第 116 次 TC39 会议(2026 年 9 月):Stage 2 更新 - 幻灯片

    动机

    测量系统经常连续使用多个单位来表示一个单一量值。例如:

    • 人的身高:“5 英尺 11 英寸”或“1 米 80 厘米”
    • 质量:“2 磅 4 盎司”

    当前的 ECMAScript 实现需要手动组合多个 Intl.NumberFormat 输出。这种方法会带来本地化分隔符、单位顺序和复数一致性方面的风险,而这些在不同区域设置之间差异很大。本提案通过为多单位格式化提供标准化接口来满足这些需求。

    例如,如今要格式化“5 feet, 11 inches”,开发者必须编写:

    const feetNf = new Intl.NumberFormat("en", { style: "unit", unit: "foot" });
    const inchNf = new Intl.NumberFormat("en", { style: "unit", unit: "inch" });
    const lf = new Intl.ListFormat("en", { type: "unit" });
    
    lf.format([feetNf.format(5), inchNf.format(11)]);
    // "5 feet, 11 inches"

    虽然这是可行的,但它并不符合人体工学,而且容易导致用户错误:

    • 它需要创建和管理多个 Intl.NumberFormat 实例。
    • 开发者必须手动确保正确的单位顺序。
    • 它不会自动处理整个序列中的符号显示(例如,只显示第一个单位上的负号)。

    提议的解决方案

    扩展 Intl.NumberFormat 以支持由 -and- 分隔符连接的复合单位标识符(例如,foot-and-inch、meter-and-centimeter、pound-and-ounce)。

    当指定序列单位时,format 和 formatToParts 接受一个值数组(或可迭代对象,见 #19)作为输入。数组的每个元素必须按顺序对应于序列中标识的子单位。

    示例

    const nf = new Intl.NumberFormat('en-US', {
      style: 'unit',
      unit: 'foot-and-inch',
    });
    
    // "5 feet, 11 inches"
    nf.format([5, 11]); 
    
    // "-5 feet, 11 inches" (Only the first unit renders the minus sign)
    nf.format([-5, -11]); 
    
    const massNf = new Intl.NumberFormat('en-US', {
      style: 'unit',
      unit: 'pound-and-ounce',
      unitDisplay: 'long'
    });
    
    // "2 pounds, 4 ounces" (Actual output is locale-dependent)
    massNf.format([2, 4]);

    技术语义

    序列单位的格式化过程遵循以下步骤:

    1. 验证:序列必须是由测量同一数量且按量级降序排列的受认可单位组成的有效组合。受认可的序列在规范中明确列出(例如 foot-and-inch、kilogram-and-gram)。注意,时间单位被排除在序列单位之外,因为它们由 Intl.DurationFormat 管理。有效数字舍入选项(minimumSignificantDigits、maximumSignificantDigits,或由 [[RoundingType]] 跟踪且值不为 "auto" 的 roundingPriority)不允许用于序列单位,并会在构造期间抛出 RangeError。
    2. 提取:按顺序从输入数组中获取值。
    3. 组件格式化:
      • 中间单位:必须是整数,并按整数格式化。
      • 末尾单位:根据 Intl.NumberFormat 实例上配置的小数位数和舍入设置进行格式化。
    4. 组合:使用 Intl.ListFormat 并将 type: "unit" 和指定的 unitDisplay 样式应用于得到的组件字符串进行连接,以确保符合区域设置的连接词和间距。

    错误处理

    传给 format 或 formatToParts 的输入必须是一个数组,其长度与单位标识符中定义的子单位数量一致;否则会抛出 RangeError。如果序列中的任何元素为 undefined,则立即抛出 TypeError。

    在读取完所有元素并转换为数字后,会执行两项最终验证:

    1. 混合符号:所有子单位必须具有相同的符号。混合正值和负值(例如 [5, -11])会抛出 RangeError。零值(0 和 -0)在此检查中既不被视为正也不被视为负,并且可以与任一种符号一起出现。由于只有第一个子单位会渲染负号(并且后续子单位中的负号——包括 -0 上的负号——会被去除),当第一个子单位为零时,会使用前导 -0 来显示负号(例如 [-0, -6] 格式化为 "-0 feet, 6 inches",而 [0, -6] 则省略负号)。
    2. 中间整数:所有中间子单位(除最后一个之外的所有子单位)必须是整数。提供非整数的中间值(例如 [5.5, 6])会抛出 RangeError。
    // Throws RangeError: invalid unit sequence (not in a sanctioned group or order)
    new Intl.NumberFormat('en-US', { style: 'unit', unit: 'meter-and-foot' });
    
    // Throws RangeError: significant digit options are disallowed with sequence units
    new Intl.NumberFormat('en-US', { style: 'unit', unit: 'foot-and-inch', maximumSignificantDigits: 2 });
    
    const nf = new Intl.NumberFormat('en-US', {
      style: 'unit',
      unit: 'foot-and-inch',
    });
    
    // Throws RangeError: array length does not match the unit sequence
    nf.format([5]); 
    
    // Throws RangeError: sub-units have mixed signs
    nf.format([5, -11]);
    
    // Throws RangeError: intermediate sub-unit is not an integer
    nf.format([5.5, 6]);

    与 Intl Unit Protocol 集成

    本提案与 Intl Unit Protocol 兼容。使用该协议时,value 属性接受子单位量值数组:

    const nf = new Intl.NumberFormat('en-US');
    
    nf.format({ 
      unit: "foot-and-inch", 
      value: [6, 4] 
    });

    与 Amount 提案集成

    本提案与 Amount 提案保持一致。为支持序列单位,Amount 实例接受子单位量值数组(例如 new Amount([2, 30], 'hour-and-minute')),而不是标量数值。

    先前技术

    序列单位的格式化需求定义在 Unicode Technical Standard #35 (LDML) 的 Unit Sequences (Mixed Units) 一节中。TR35 规定使用本地化的 listPattern 数据来组合这些序列,本提案为 ECMAScript 实现了这一点。

    未来方向

    额外单位

    一些常用于序列的单位,例如 arcminute 和 arcsecond,目前尚未在 ECMA-402 中获得认可。格式化类似“5° 30′ 12″”的角度测量值(概念上会使用 degree-and-arcminute-and-arcsecond)将需要一个单独的提案,将 arcminute 和 arcsecond 添加到受认可的单一单位标识符列表中,然后才能将它们用作序列单位。

    考虑过的替代方案

    标量输入与自动划分

    接受单个标量值(例如 6.5)并自动将其划分为序列单位(例如 "6 feet, 6 inches")的可能性经过了评估,但出于以下原因被拒绝:

    • 输入歧义:标量输入本身并不定义它们表示序列中的哪一个子单位(例如,在 foot-and-inch 中,不清楚 6.5 指的是英尺还是英寸)。
    • 转换数据不完整:引擎可能不具备所有可能单位组合的转换因子,尤其是自定义或未来的单位。这阻碍了可靠的自动划分。
    • 精度与舍入:通过浮点运算进行自动划分可能会引入错误。要求预先划分的输入可确保输出准确反映开发者的数据。