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/proposal-intl-unit-protocol.md.
  • 简体中文
  • explore associating a unit with a number ?

    中文标题:探索将单位与数字关联

    提案概览
    提案速览

    该提案旨在解决将数字与其所度量的单位关联起来的需求,这是 MessageFormat 2.0 所需的一种数据类型,但在 JavaScript 中没有对应物。它为 Intl.NumberFormat.prototype.format 增加了一种协议,同时也应由 Intl.PluralRules.prototype.select 接受,该协议接受与单位配对的数值。该协议允许在格式化时提供单位,而不是通过 Intl.NumberFormat 构造函数提供,包括货币单位,并规定了缺失字段、单位冲突以及不允许单位样式时的行为。

    Note

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

    TC39 Intl 单位协议

    状态:Stage 2

    提案人:Shane F Carr (Google i18n)

    历史:

    草案规范:https://tc39.es/proposal-intl-unit-protocol/

    背景

    i18n 社区已经达成共识:数字应当标注它所度量的量。与本地化的小数分隔符、数字的记数系统以及分组策略不同,单位是待格式化数据模型的核心组成部分,而不是由格式化器应用的样式。

    例如,在格式化消息时,这允许根据区域设置的单位偏好对度量进行本地化。

    const message = "You are {$distance :unit usage=road} from your destination";
    let formatter = new MessageFormat("en", message);
    formatter.format({
      distance: /* what goes here? */
    })

    带单位标注的数字是 MessageFormat 2.0 所需的唯一一种在 JavaScript 中没有对应物的数据类型。参见:MessageFormat 2.0 中的函数列表

    添加一个新的原始对象(primordial),例如 Amount,可以解决这个问题以及影响 i18n 的多个其他问题。一个关键问题是 Amount 如何与 Intl 对接,以及第三方库如何实现类似 Amount 的对象。提案负责人认为,这是一个足够不同的问题领域,因此他们正在将 Intl Unit Protocol 作为独立提案推进。

    提议的解决方案

    Intl.NumberFormat.prototype.format 增加一种协议,该协议接受与单位配对的数值类型。该协议也应被 Intl.PluralRules.prototype.select 接受。

    给定这些输入:

    let locale = /* an Intl.Locale, a string, or a list of these */;
    let unit = /* a string */;
    let value = /* a Number, a BigInt, or a string */;

    用户目前可以这样写:

    let formatter = new Intl.NumberFormat(locale, {
        style: "unit",
        unit,
    });
    let result = formatter.format(value);

    使用协议后,用户可以改为这样写:

    let formatter = new Intl.NumberFormat(locale, {
        style: "unit",
    });
    let result = formatter.format({
        value,
        unit,
    });

    构造与格式化

    Intl 长期以来允许构造函数和格式化函数相互分离。这实现了两个目的:

    1. 格式化器可以封装区域设置和选项,以指定待格式化值的样式和上下文,例如在初始化模板引擎时。
    2. 区域设置数据可以提前初始化,从而在循环中格式化多个项目时提高效率。

    通过将 unit 从构造函数一侧移到格式化一侧,我们推进了目标 1。

    该提案对目标 2 有一定代价,因为加载单位显示名称存在一些实现成本,而现在必须延迟这一成本。我们将与 ICU[4X] 合作以尽量减少这一成本。

    货币

    该协议将允许以类似方式指定货币单位。

    let locale = /* an Intl.Locale, a string, or a list of these */;
    let currency = /* a string consisting of 3 upper-case ASCII letters */;
    let value = /* a Number, a BigInt, or a string */;
    
    // Today:
    let formatter = new Intl.NumberFormat(locale, {
        style: "currency",
        currency,
    });
    let result = formatter.format(value);
    
    // With the protocol:
    let formatter = new Intl.NumberFormat(locale, {
        style: "currency",
    });
    let result = formatter.format({
        value,
        unit: currency,
    });

    协议要求同时提供 valueunit 字段

    该协议要求对象同时具有 valueunit 字段:

    let formatter = new Intl.NumberFormat(locale, {
        style: "unit",
    });
    formatter.format({
        value: 333,
        unit: null,
    });
    // '333'

    如果其中任一字段缺失或为 undefined,我们会回退到当前调用 Symbol.toPrimitive 的行为:

    let formatter = new Intl.NumberFormat(locale, {
        style: "unit",
    });
    formatter.format({
      value: 333,
      unit: undefined,
      [Symbol.toPrimitive](hint) {
        if (hint === 'number') {
          return 111;
        }
        return 222;
      },
    });
    // '111'

    单位冲突

    如果构造函数中有一个单位,而该单位与协议中的单位不同,则会抛出异常,因为这是程序员的错误。

    new Intl.NumberFormat("en", {
        style: "unit",
        unit: "meter",
    }).format({
        value: 1234,
        unit: "kilometer",
    }) // throws a RangeError

    当 Amount 提案推进时,可以将其改为自动把输入单位转换为格式化器单位。

    如果 style 不是 "unit" 或 "currency",则不允许使用单位

    如果在协议中设置了 unit,但格式化器 配置为 style "unit" 或 "currency",则会发生错误。

    这有助于实现方知道它们需要在构造函数中加载单位或货币数据,从而部分缓解上文讨论的“构造与格式化”影响。

    let formatter = new Intl.NumberFormat(locale);
    let result = formatter.format({
        value,
        unit,
    }) // throws a TypeError

    范围格式化

    目前,格式化范围要求单位相等。这一限制将来可能会放宽。

    new Intl.NumberFormat("en", {
        style: "unit",
        unit: "meter",
    }).formatRange({
        value: 1000,
        // if `unit` is not specified, the constructor unit is used
    }, {
        value: 2000,
        unit: "meter",
    }) // "1000-2000 meters"

    与未来提案的集成

    Amount 提案旨在添加一个封装数值类型与单位的原始对象(primordial)。它将实现此处指定的 Intl 协议。

    Decimal 提案旨在添加一个原始对象(primordial),以一种为正确且高效的算术运算而设计的形式表示十进制数。它很可能作为协议中 value 字段的另一种数值类型得到支持。

    将来还可以向该协议添加其他字段,例如表达精度的替代方式,或对构造函数中指定的有效数字位数进行覆盖。