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/2020/proposal-intl-relative-time.md.
  • 简体中文
  • Intl.RelativeTimeFormat S4

    中文标题:Intl.RelativeTimeFormat API 规范 [草案]

    提案概览
    提案速览

    该提案引入了一个新的 Intl API,Intl.RelativeTimeFormat,用于以区域设置敏感的方式格式化相对时间值。它通过提供一个接受数字和单位的低级 API,解决了当前许多库实现的相对时间格式化的标准化需求。

    Note

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

    Intl.RelativeTimeFormat API 规范 [草案]

    概述

    动机

    由于普遍使用,相对时间格式化值存在于大多数网站中,并且可用于大多数框架(例如,React,通过 react-intlreact-globalize;Ember,通过 ember-intl)。流行的本地化库如 Moment.jsFormat.jsGlobalize其他 也已经实现了相对时间值的格式化过程。

    当前大多数相对时间格式化实现很可能需要大量的 CLDR 原始数据或编译数据来格式化相对时间值。将其引入平台将提高 Web 性能和开发人员生产力,因为他们不再需要为了格式化相对时间值而引入额外的负担。

    使用示例

    以下示例展示了如何使用英语创建相对时间格式化器。

    单位:"year"(年)、"quarter"(季度)、"month"(月)、"week"(周)、"day"(日)、"hour"(小时)、"minute"(分钟)和 "second"(秒)。

    // 使用显式传入的默认值在您的区域设置中创建相对时间格式化器。
    const rtf = new Intl.RelativeTimeFormat("en", {
        localeMatcher: "best fit", // 其他值:"lookup"
        numeric: "always", // 其他值:"auto"
        style: "long", // 其他值:"short" 或 "narrow"
    });
    
    
    // 使用负值 (-1) 格式化相对时间。
    rtf.format(-1, "day");
    // > "1 day ago"
    
    // 使用正值 (1) 格式化相对时间。
    rtf.format(1, "day");
    // > "in 1 day"
    

    注意:如果传入 numeric:auto 选项,它将产生字符串 yesterdaytomorrow,而不是 1 day agoin 1 day,这允许输出中不必总是使用数值。

    // 在您的区域设置中创建相对时间格式化器,
    // 并传入 numeric: "auto" 选项值。
    const rtf = new Intl.RelativeTimeFormat("en", { numeric: "auto" });
    
    // 使用负值 (-1) 格式化相对时间。
    rtf.format(-1, "day");
    // > "yesterday"
    
    // 使用正的天数单位 (1) 格式化相对时间。
    rtf.format(1, "day");
    // > "tomorrow"

    实现状态

    阶段 4

    实现进展

    反向指针

    Polyfills

    有几种可用的 polyfill,它们列在下面的比较表中。所有 polyfill 在 API 方面的功能相同:它们仅在实现细节上有所不同,例如 polyfill 的导入方式、区域设置的加载方式,或者实现是否通过官方 ECMAScript 一致性测试 以完全覆盖所有可能的边缘情况。

    作者
    • Caridy Patiño (@caridy)
    • Eric Ferraiuolo (@ericf)
    • Zibi Braniecki (@zbraniecki)
    • Rafael Xavier (@rxaviers)
    • Daniel Ehrenberg (@littledan)
    审阅者

    待定

    提案

    Intl.RelativeTimeFormat 是一个低级 API,通过提供日期和时间字段的国际化消息(在可用时使用惯用词或短语),帮助库和框架以本地化方式格式化相对时间。

    规范

    您可以查看规范文本 或渲染为 HTML

    技术设计

    本提案基于 ICU 相对日期时间格式化器以及 Unicode CLDR 日历字段相对值:

    它还基于 LDML 规范,C.11 语言复数规则:

    先例
    Java
    • ICU: com.ibm.icu.impl.RelativeDateFormat
    • org.ocpsoft.prettytime.PrettyTime
    Ruby
    include ActionView::Helpers::DateHelper
    def index
      @friendly_date = time_ago_in_words(Date.today - 1)
    end
    命名

    为了与 Intl.NumberFormatIntl.DateTimeFormat 保持一致,我们为这个新特性选择了类似的形式。创建 Intl.RelativeTimeFormat 实例是一个昂贵的操作,需要解析区域设置数据,而且很可能,库会尝试缓存这些实例,就像他们对 Intl.NumberFormatIntl.DateTimeFormat 所做的那样。

    我们还选择了 style 作为在不同格式化形式之间切换的主要方式,以与 Intl.NumberFormatIntl.DateTimeFormat 保持一致。

    由于这个新特性确实格式化提供的值,就像 Intl.NumberFormatIntl.DateTimeFormat 的实例一样,我们选择了相同的形式,通过提供实例的 format(value) 方法,该方法返回格式化后的字符串值。

    输入采用数字而不是日期对象

    相对时间用于显示日期距离,因此输入的自然形式应该直观地是一个日期对象。但是,在此 API 中,我们选择接受数字而不是日期对象,原因如下:

    1. 基本上,将数字作为格式方法的输入而不是日期对象,大大简化了此提案的范围,同时仍然完全解决了主要目标,即提供解决此问题领域的 i18n 构建块。
    2. 接受日期对象意味着我们应该实现比较逻辑(相对时间是关于目标日期和源日期之间的日期距离)。源日期通常是 现在,但并不总是。我们必须解决修改它的问题。请参阅 #4
    3. 接受日期对象还意味着我们应该允许不同的日历计算,这暗示 Date 应该支持它。请参阅 #6#13
    4. 接受日期对象表明我们应该能够实现 bestFit 算法,这在标准化适用于所有情况的方法方面有其自身的 API 挑战。请参阅 #7#14#15。我们可能需要为用户提供一个标志,没有默认设置,以在日历计算的选项之间进行选择。
    接受数字作为输入而不是暴露底层数据库

    在“可扩展 Web”的背景下,有人提出一个想法:仅暴露引擎的 CLDR 数据库副本,而不是提供更高级别的接口会更好。对于本规范,已经有一个 JS 对象模型准备好了——区域设置数据库在规范内部表示为 JavaScript 对象。

    但是,我们选择不采用这种方式,原因如下:

    • 如上所述,该 API 已经相当低级,接受数字而不是日期。
    • 尽管对于将日期舍入到单位的策略有不同的用例,但我们还没有遇到需要查看底层数据的用例。
    • 这个新 API 与之前的 API 类似,这对学习该系统的人应该是有用的。
    • CLDR 会随时间改变模式;如果数据模型改进,实现可以透明地升级用户,通过相同的 API 获得更好的结果。但是,如果我们冻结在当前逻辑,旧的数据模型将需要被模拟。
    UnitFormat 的区别

    RelativeTimeFormatUnitFormat 之间的根本区别在于,RelativeTimeFormat 显示相对单位(例如,5 days agoin 5 days),而 UnitFormat 显示绝对单位(例如,-5 meters5 meters)。请注意,RelativeTimeFormat 根据值符号方向使用不同的国际化消息,而 UnitFormat 对所有值使用相同的国际化消息。

    倒计时,例如,15 天 0 小时 27 分钟 52 秒

    例如,倒计时是 UnitFormatListFormat 的混合体,而不是 RelativeTimeFormat

    NumberFormat 选项(例如,useGrouping、maximumFractionDigits)

    RelativeTimeFormat 消息可能包含数字部分(例如,1,000 days ago 中的 1,000),这些部分使用 NumberFormat 默认选项进行格式化。

    在此设计中,我们没有发现任何用例可以证明允许更改/覆盖这些 NumberFormat 默认选项是合理的。因此,RelativeTimeFormat 不包含任何 NumberFormat 选项。

    API

    Intl.RelativeTimeFormat([locales[, options]])

    Intl.RelativeTimeFormat 对象是一个构造函数,用于创建能够进行语言敏感的相对时间格式化的对象。

    locales

    可选。一个包含 BCP 47 语言标签的字符串,或此类字符串的数组。有关 locales 参数的一般形式和解释,请参阅 Intl 页面

    options

    可选。一个包含以下部分或全部属性的对象:

    localeMatcher

    要使用的区域设置匹配算法。可能的值为 "lookup""best fit";默认值为 "best fit"。有关此选项的信息,请参阅 Intl 页面

    numeric

    输出消息的格式。可能的值为 "always"(默认值,例如,1 day ago)或 "auto"(例如,yesterday)。"auto" 允许输出中不必总是使用数值。

    style

    国际化消息的长度。可能的值为:"long"(默认值,例如,in 1 month);"short"(例如,in 1 mo.)或 "narrow"(例如,in 1 mo.)。对于某些区域设置,narrow 样式可能类似于 short 样式。

    示例
    // 在您的区域设置中创建相对时间格式化器。
    let rtf = new Intl.RelativeTimeFormat("en", {
        localeMatcher: "best fit", // 其他值:"lookup"
        numeric: "always", // 其他值:"auto"
        style: "long", // 其他值:"short" 或 "narrow"
    });

    Intl.RelativeTimeFormat.prototype.format(value, unit)

    Intl.RelativeTimeFormat.prototype.format 方法根据此 Intl.RelativeTimeFormat 对象的区域设置和格式化选项格式化 valueunit

    虽然此方法自动提供正确的复数形式,但语法形式在其他方面尽可能中性。由调用者负责处理截断逻辑,例如决定显示“in 7 days”还是“in 1 week”。此 API 不支持涉及复合单位的相对日期。例如“in 5 days and 4 hours”。

    value

    用于国际化相对时间消息的数值。

    unit

    用于相对时间国际化消息的单位。可能的值为:"year""quarter""month""week""day""hour""minute""second"。也允许复数形式。

    示例
    const rtf = new Intl.RelativeTimeFormat("en", { numeric: "auto" });
    
    // 使用天单位格式化相对时间。
    rtf.format(-1, "day");
    // > "yesterday"
    
    rtf.format(2.15, "day");
    // > "in 2.15 days"
    
    rtf.format(100, "day");
    // > "in 100 days"
    
    rtf.format(0, "day");
    // > "today"
    
    rtf.format(-0, "day");
    // > "today"

    此外,通过组合类选项 styleunit,您可以实现以下任何结果:

    last year
    this year
    next year
    in 1 year
    in 2 years
    1 year ago
    2 years ago
    yr.
    last yr.
    this yr.
    next yr.
    in 1 yr.
    in 2 yr.
    1 yr. ago
    2 yr. ago
    last quarter
    this quarter
    next quarter
    in 1 quarter
    in 2 quarters
    1 quarter ago
    2 quarters ago
    last qtr.
    this qtr.
    next qtr.
    in 1 qtr.
    in 2 qtrs.
    1 qtr. ago
    2 qtrs. ago
    last month
    this month
    next month
    in 1 month
    in 2 months
    1 month ago
    2 months ago
    last mo.
    this mo.
    next mo.
    in 1 mo.
    in 2 mo.
    1 mo. ago
    2 mo. ago
    last week
    this week
    next week
    in 1 week
    in 2 weeks
    1 week ago
    2 weeks ago
    last wk.
    this wk.
    next wk.
    in 1 wk.
    in 2 wk.
    1 wk. ago
    2 wk. ago
    in 1 day
    in 2 days
    1 day ago
    2 days ago
    yesterday
    today
    tomorrow
    in 1 hour
    in 2 hours
    1 hour ago
    2 hours ago
    in 1 hr.
    in 2 hr.
    1 hr. ago
    2 hr. ago
    in 1 minute
    in 2 minutes
    1 minute ago
    2 minutes ago
    in 1 min.
    in 2 min.
    1 min. ago
    2 min. ago
    in 1 second
    in 2 seconds
    1 second ago
    2 seconds ago
    in 1 sec.
    in 1 sec.
    1 sec. ago
    2 sec. ago
    now

    Intl.RelativeTimeFormat.prototype.formatToParts(value, unit)

    Intl.RelativeTimeFormat.prototype.formatToParts 方法是 format 方法的一个版本,它返回一个对象数组,这些对象表示对象的“部分”,将格式化的数字分隔为其组成部分,并将其与周围的其他文本分开。这些对象有两个属性:type,一个 NumberFormat formatToParts 类型,以及 value,它是输出的字符串组件。如果“部分”来自 NumberFormat,它将有一个 unit 属性,表示正在格式化的单位;作为更大框架一部分的文本将不具有此属性。

    示例
    const rtf = new Intl.RelativeTimeFormat("en", { numeric: "auto" });
    
    // 使用天单位格式化相对时间。
    rtf.formatToParts(-1, "day");
    // > [{ type: "literal", value: "yesterday"}]
    
    rtf.formatToParts(100, "day");
    // > [{ type: "literal", value: "in " }, { type: "integer", value: "100", unit: "day" }, { type: "literal", value: " days" }]

    开发

    渲染规范

    npm install
    npm run build
    open index.html