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 ?

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

    提案概览
    提案速览

    该提案引入了在 Intl.NumberFormat 格式化中将单位与数字关联的协议。它允许在 format 参数对象中传递单位,解决了 MessageFormat 2.0 的数据模型缺口。

    Note

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

    TC39 Intl 单位协议

    状态:第 2 阶段

    提案负责人:Shane F Carr(Google i18n)

    历史记录:

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

    背景

    i18n 社区已趋于一致,认为数字应当附有其所测量的量。与本地化的小数分隔符、数字编号系统和分组策略不同,单位是待格式化数据模型的核心部分,而非由格式化器应用的样式。

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

    const message = "您距目的地 {$distance :unit usage=road}";
    let formatter = new MessageFormat("zh", message);
    formatter.format({
      distance: /* 这里应放置什么? */
    })

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

    添加新的原始类型,例如 Amount,将解决此问题以及影响 i18n 的其他多个问题。一个关键问题是 Amount 如何与 Intl 交互,以及第三方库如何实现类似 Amount 的对象。提案负责人认为这是一个足够不同的问题空间,因此他们将 Intl 单位协议作为独立提案推进。

    拟议解决方案

    Intl.NumberFormat.prototype.format 中添加一个协议,该协议接受一个带有单位的数值类型。该协议也应被 Intl.PluralRules.prototype.select 接受。

    给定以下输入:

    let locale = /* 一个 Intl.Locale、一个字符串或这些的列表 */;
    let unit = /* 一个字符串 */;
    let value = /* 一个 Number、一个 BigInt 或一个字符串 */;

    用户目前可以编写:

    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 = /* 一个 Intl.Locale、一个字符串或这些的列表 */;
    let currency = /* 由 3 个大写 ASCII 字母组成的字符串 */;
    let value = /* 一个 Number、一个 BigInt 或一个字符串 */;
    
    // 目前:
    let formatter = new Intl.NumberFormat(locale, {
        style: "currency",
        currency,
    });
    let result = formatter.format(value);
    
    // 使用协议后:
    let formatter = new Intl.NumberFormat(locale, {
        style: "currency",
    });
    let result = formatter.format({
        value,
        unit: currency,
    });

    协议不要求必须提供单位

    协议接受带 value getter 的对象,即使其 unit getter 返回 undefined

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

    冲突的单位

    如果构造函数指定了单位,且该单位与协议中提供的单位不同,则将抛出异常,因为这是程序员错误。

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

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

    如果样式不是“unit”或“currency”,则不允许使用单位

    如果在协议中设置了 unit,但格式化器未配置为样式“unit”或“currency”,则将发生错误。

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

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

    范围格式化

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

    new Intl.NumberFormat("en", {
        style: "unit",
        unit: "meter",
    }).formatRange({
        value: 1000,
        // 如果未指定 `unit`,则使用构造函数的单位
    }, {
        value: 2000,
        unit: "meter",
    }) // "1000-2000 meters"

    与未来提案的集成

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

    Decimal 提案旨在添加一个以适合正确高效算术的形式表示十进制数的原始类型。它可能会作为协议中 value 字段的另一种数字类型而受到支持。

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