JavaScript Intl.RelativeTimeFormat:自动生成 “3 分钟前” 的国际化工具

在当今全球化的互联网环境下,Web 应用的国际化(i18n)和本地化(l10n)至关重要。用户期望看到的时间信息,能以他们熟悉的语言和习惯的方式呈现,例如 "3 分钟前""昨天""下周" 等相对时间表述,而非冷冰冰的时间戳或通用格式的日期。JavaScript 的 Intl.RelativeTimeFormat API 正是为解决这一需求而生,它能让开发者轻松实现相对时间的本地化格式化,极大提升用户体验。今天,我们就来深入探索这个实用的国际化工具。

一、为什么需要 Intl.RelativeTimeFormat

在传统的前端开发中,实现相对时间格式化并非易事。以往我们可能会使用像 Moment.js 这样的第三方库,但这些库往往存在一些痛点:

  1. 体积过大Moment.js 的完整版本包含大量功能,导致打包体积膨胀,影响页面加载速度。例如,在一个简单的社交类应用中,若引入 Moment.js 来处理相对时间,其几十 KB 的大小可能会使页面初始加载时间增加明显,尤其是在移动网络环境下。

  2. 本地化支持复杂:要实现多语言的相对时间格式化,需手动管理不同语言的翻译资源,不仅工作量大,还容易出错。如在一个面向全球用户的新闻资讯应用中,为支持英、法、德、中、日等多种语言,需为每个语言维护一套时间格式化的词汇表,过程繁琐且难以保证准确性。

  3. 性能问题:第三方库的复杂逻辑可能带来额外的性能开销,在频繁更新时间显示的场景下(如实时聊天窗口),会影响应用的流畅性。

Intl.RelativeTimeFormat 的出现,有效解决了这些问题。它是 JavaScript 内置的国际化 API 的一部分,由浏览器原生支持,无需引入额外的库,且其底层实现经过优化,性能表现出色。同时,它依托 Unicode CLDR(Common Locale Data Repository)提供的丰富语言数据,能轻松实现多语言的相对时间格式化,极大简化了开发流程。

二、Intl.RelativeTimeFormat 的基本使用

2.1 创建 Intl.RelativeTimeFormat 实例

使用 Intl.RelativeTimeFormat 首先要创建一个实例,其构造函数接受两个参数:

js 复制代码
new Intl.RelativeTimeFormat([locales [, options]])
  • locales:一个字符串或字符串数组,表示语言标签(如 'en' 代表英语,'zh-CN' 代表简体中文)。若不提供,将使用浏览器的默认语言设置。例如:
js 复制代码
// 创建一个英语的相对时间格式化实例
const rtfEn = new Intl.RelativeTimeFormat("en");
js 复制代码
// 创建一个简体中文的相对时间格式化实例
const rtfZh = new Intl.RelativeTimeFormat("zh-CN");
  • options:一个可选的配置对象,用于自定义格式化的行为,稍后我们会详细介绍。

2.2 使用 format 方法格式化时间

创建实例后,可通过 format 方法对相对时间进行格式化。该方法接受两个参数:

js 复制代码
format(value, unit);
  • value:一个数值,表示相对于当前时间的偏移量。正值表示未来时间,负值表示过去时间。例如,-1 表示过去的一个单位时间,2 表示未来的两个单位时间。

  • unit:一个字符串,表示时间单位,支持的单位有 'second'(秒)、'minute'(分钟)、'hour'(小时)、'day'(天)、'week'(周)、'month'(月)、'quarter'(季度)、'year'(年)等。

以下是一些示例:

js 复制代码
// 英语环境下
const rtfEn = new Intl.RelativeTimeFormat("en");

console.log(rtfEn.format(-1,'second')); // 输出: "1 second ago"
console.log(rtfEn.format(5,'minute')); // 输出: "in 5 minutes"
console.log(rtfEn.format(-3, 'hour')); // 输出: "3 hours ago"
console.log(rtfEn.format(1, 'day')); // 输出: "in 1 day"
console.log(rtfEn.format(-1, 'week')); // 输出: "1 week ago"
console.log(rtfEn.format(-1, 'year')); // 输出: "1 year ago"
js 复制代码
// 简体中文环境下
const rtfZh = new Intl.RelativeTimeFormat("zh-CN");

console.log(rtfZh.format(-1, "second")); // 输出: "1秒钟前"
console.log(rtfZh.format(5, "minute")); // 输出: "5分钟后"
console.log(rtfZh.format(-3, "hour")); // 输出: "3小时前"
console.log(rtfZh.format(1, "day")); // 输出: "1天后"
console.log(rtfZh.format(-1, "week")); // 输出: "1周前"
console.log(rtfZh.format(-1, "year")); // 输出: "1年前"

可以看到,Intl.RelativeTimeFormat 能根据设置的语言环境和传入的时间偏移量及单位,自动生成符合该语言习惯的相对时间表述,十分便捷。

三、配置 Intl.RelativeTimeFormat

Intl.RelativeTimeFormat 的构造函数的 options 参数可对格式化行为进行更细致的控制,常用的配置选项如下:

3.1 numeric 选项:控制数字显示方式

numeric 选项决定了格式化结果中数字的显示形式,可选值有:

  • 'always':始终以数字形式显示时间偏移量,如 '1 day ago'

  • 'auto':根据语言习惯,将一些常见的时间偏移量转换为文字形式,如 'yesterday'(昨天)、'tomorrow'(明天)、'this week'(本周)等。这是默认值。

示例:

js 复制代码
// 使用 'always' 选项,始终显示数字
const rtfAlways = new Intl.RelativeTimeFormat("en", { numeric: "always" });

console.log(rtfAlways.format(-1, "day")); // 输出: "1 day ago"
console.log(rtfAlways.format(1, "year")); // 输出: "in 1 year"
js 复制代码
// 使用 'auto' 选项,根据习惯显示文字
const rtfAuto = new Intl.RelativeTimeFormat("en", { numeric: "auto" });

console.log(rtfAuto.format(-1, "day")); // 输出: "yesterday"
console.log(rtfAuto.format(1, "year")); // 输出: "next year"

3.2 style 选项:控制格式化风格

style 选项用于指定格式化结果的风格,可选值有:

  • 'long':完整的格式化风格,如 '1 day ago'(英语)、'1天前'(简体中文)。这是默认值。

  • 'narrow':较短的格式化风格,如 '1d ago'(英语)、'1天前'(简体中文,与 long 风格在中文下表现相同,在其他语言可能有差异)。

示例:

js 复制代码
// long 风格
const rtfLong = new Intl.RelativeTimeFormat("en", { style: "long" });

console.log(rtfLong.format(-1, "day")); // 输出: "1 day ago"
js 复制代码
// narrow 风格
const rtfNarrow = new Intl.RelativeTimeFormat("en", { style: "narrow" });

console.log(rtfNarrow.format(-1, "day")); // 输出: "1d ago"

3.3 localeMatcher 选项:语言匹配策略

localeMatcher 选项用于指定语言标签的匹配策略,可选值有:

  • 'best fit':尝试找到最匹配的语言环境,这是默认值。例如,若传入 'zh',它会尝试找到最合适的中文语言环境,可能是 'zh-CN''zh-TW' 等,具体取决于浏览器支持和系统设置。

  • 'lookup':直接查找完全匹配的语言标签,若找不到,则使用默认语言环境。例如,若传入 'zh' 且没有完全匹配的语言环境,则使用浏览器默认语言环境。

示例:

js 复制代码
// 使用 'best fit' 匹配策略
const rtfBestFit = new Intl.RelativeTimeFormat("zh", {
  localeMatcher: "best fit",
});
js 复制代码
// 使用 'lookup' 匹配策略
const rtfLookup = new Intl.RelativeTimeFormat("zh", {
  localeMatcher: "lookup",
});

四、实战场景:在项目中应用 Intl.RelativeTimeFormat

4.1 社交媒体应用中的时间显示

在社交媒体应用中,动态消息通常会显示发布时间,使用 Intl.RelativeTimeFormat 可轻松实现多语言环境下的相对时间显示。

假设我们有一个函数,用于获取动态消息的发布时间并格式化为相对时间:

js 复制代码
function formatPostTime(postTime) {
  const now = new Date();

  const diffInSeconds = Math.floor((now - postTime) / 1000);

  const rtf = new Intl.RelativeTimeFormat("en", { numeric: "auto" });

  if (diffInSeconds < 60) {
    return rtf.format(-diffInSeconds, "second");
  } else if (diffInSeconds < 3600) {
    return rtf.format(-Math.floor(diffInSeconds / 60), "minute");
  } else if (diffInSeconds < 86400) {
    return rtf.format(-Math.floor(diffInSeconds / 3600), "hour");
  } else if (diffInSeconds < 604800) {
    return rtf.format(-Math.floor(diffInSeconds / 86400), "day");
  } else {
    return rtf.format(-Math.floor(diffInSeconds / 29030400), "year");
  }
}

// 示例使用
const postTime = new Date("2025-08-20T10:00:00Z");

console.log(formatPostTime(postTime)); // 假设当前时间为2025-08-21T12:00:00Z,输出: "1 day ago"

若应用需要支持多语言,只需根据用户选择的语言修改 Intl.RelativeTimeFormatlocales 参数即可:

js 复制代码
function formatPostTime(postTime, locale) {
  const now = new Date();

  const diffInSeconds = Math.floor((now - postTime) / 1000);

  const rtf = new Intl.RelativeTimeFormat(locale, { numeric: "auto" });

  // 后续逻辑与上述相同
}

// 示例使用,用户选择简体中文
const postTime = new Date("2025-08-20T10:00:00Z");

console.log(formatPostTime(postTime, "zh-CN")); // 假设当前时间为2025-08-21T12:00:00Z,输出: "1天前"

4.2 事件提醒应用中的时间倒计时

在事件提醒应用中,可使用 Intl.RelativeTimeFormat 显示距离事件发生的剩余时间,以更友好的方式提醒用户。

假设我们有一个事件对象,包含事件名称和开始时间:

js 复制代码
const event = {
  name: "重要会议",
  startTime: new Date("2025-08-25T14:00:00Z"),
};

function formatEventReminder(event) {
  const now = new Date();

  const diffInSeconds = Math.floor((event.startTime - now) / 1000);

  const rtf = new Intl.RelativeTimeFormat("zh-CN", { numeric: "auto" });

  if (diffInSeconds < 0) {
    return "事件已结束";
  } else if (diffInSeconds < 60) {
    return `距离 ${event.name} 开始还有 ${rtf.format(diffInSeconds, "second")}`;
  } else if (diffInSeconds < 3600) {
    return `距离 ${event.name} 开始还有 ${rtf.format(
      Math.floor(diffInSeconds / 60),
      "minute"
    )}`;
  } else if (diffInSeconds < 86400) {
    return `距离 ${event.name} 开始还有 ${rtf.format(
      Math.floor(diffInSeconds / 3600),
      "hour"
    )}`;
  } else {
    return `距离 ${event.name} 开始还有 ${rtf.format(
      Math.floor(diffInSeconds / 86400),
      "day"
    )}`;
  }
}

console.log(formatEventReminder(event));

// 假设当前时间为2025-08-25T13:30:00Z,输出: "距离 重要会议 开始还有 30分钟"

五、浏览器兼容性与注意事项

5.1 浏览器兼容性

Intl.RelativeTimeFormat 是 ES2018 引入的特性,现代浏览器(如 Chrome 71+、Firefox 65+、Safari 12.1+、Edge 79+)都提供了较好的支持。但对于一些旧版本浏览器,可能需要使用 polyfill 来实现兼容。例如,可以使用 Intl.js 库作为 polyfill,在项目中引入后,即可在不支持的浏览器中使用 Intl.RelativeTimeFormat

5.2 注意事项

  1. 语言标签的准确性 :确保传入的 locales 参数是正确的 BCP 47 语言标签,否则可能无法得到预期的语言环境匹配。例如,'en-US' 是正确的美国英语语言标签,而 'en_US' 则不符合标准,可能导致匹配失败。

  2. 时间单位的一致性 :在格式化时间时,确保 valueunit 的搭配符合逻辑。例如,若 value 是表示分钟的数值,unit 应设置为 'minute',否则结果可能不符合预期。

  3. 性能优化 :在频繁格式化相对时间的场景下,可考虑缓存 Intl.RelativeTimeFormat 实例,避免重复创建带来的性能开销。例如,在一个实时聊天窗口中,可在页面初始化时创建好不同语言环境的 Intl.RelativeTimeFormat 实例,后续使用时直接调用,而不是每次更新时间都创建新实例。

六、总结

Intl.RelativeTimeFormat 作为 JavaScript 国际化工具箱中的一员,为开发者提供了强大且便捷的相对时间格式化能力。它不仅简化了多语言环境下相对时间显示的开发流程,还通过浏览器原生支持和优化的底层实现,提升了性能和用户体验。无论是社交媒体应用中的动态时间展示,还是事件提醒应用中的倒计时显示,Intl.RelativeTimeFormat 都能轻松胜任。

随着互联网全球化的深入发展,Web 应用的国际化需求愈发重要。掌握 Intl.RelativeTimeFormat 这样的国际化工具,能让我们的应用在全球范围内更具亲和力和竞争力。不妨在下次项目中尝试使用它,为用户带来更贴心的时间显示体验。

你在使用 Intl.RelativeTimeFormat 过程中遇到过哪些有趣的问题或有什么独特的应用场景?欢迎在评论区分享~

相关推荐
辻戋9 分钟前
从零实现React Scheduler调度器
前端·react.js·前端框架
徐同保11 分钟前
使用yarn@4.6.0装包,项目是react+vite搭建的,项目无法启动,报错:
前端·react.js·前端框架
Qrun1 小时前
Windows11安装nvm管理node多版本
前端·vscode·react.js·ajax·npm·html5
中国lanwp1 小时前
全局 npm config 与多环境配置
前端·npm·node.js
JELEE.2 小时前
Django登录注册完整代码(图片、邮箱验证、加密)
前端·javascript·后端·python·django·bootstrap·jquery
TeleostNaCl4 小时前
解决 Chrome 无法访问网页但无痕模式下可以访问该网页 的问题
前端·网络·chrome·windows·经验分享
前端大卫6 小时前
为什么 React 中的 key 不能用索引?
前端
你的人类朋友6 小时前
【Node】手动归还主线程控制权:解决 Node.js 阻塞的一个思路
前端·后端·node.js
小李小李不讲道理7 小时前
「Ant Design 组件库探索」五:Tabs组件
前端·react.js·ant design
毕设十刻7 小时前
基于Vue的学分预警系统98k51(程序 + 源码 + 数据库 + 调试部署 + 开发环境配置),配套论文文档字数达万字以上,文末可获取,系统界面展示置于文末
前端·数据库·vue.js