别再手写i18n了!深入浏览器原生Intl对象(数字、日期、复数处理)

我们做前端,经常会遇到国际化(i18n)的需求。最常见的就是,如何根据用户的地区,显示不同格式的数字、货币和日期。

在不了解Intl对象之前,我们可能会写出这样的代码:

JavaScript 复制代码
// 场景:显示商品价格
function formatPrice(price, currency) {
  if (currency === 'USD') {
    return '$' + price.toFixed(2);
  } else if (currency === 'JPY') {
    return '¥' + price.toFixed(0);
  } else {
    // 更多if...else...
    return price;
  }
}

这段代码不仅繁琐、难以维护,而且根本不严谨。比如,数字的千分位分隔符在不同国家是不一样的(美国用逗号,,德国用点.)。

为了解决这些问题,我们通常会引入一个庞大的第三方库,增加了项目的打包体积。

但实际上,浏览器已经给了我们一个"官方答案"------Intl对象。本文将带你深入了解这个原生API,看看如何用它来优雅地处理国际化需求,让你告别大部分手写i18n的繁琐工作。


数字格式化 (Intl.NumberFormat):不只是加个千分位

这是Intl对象中最常用的功能之一。它可以根据不同的语言环境,格式化数字、货币、单位等。

1. 千分位处理

JavaScript 复制代码
const number = 1234567.89;

// 美式英语
console.log(new Intl.NumberFormat('en-US').format(number));
// 输出: "1,234,567.89"

// 德语
console.log(new Intl.NumberFormat('de-DE').format(number));
// 输出: "1.234.567,89" (注意分隔符的区别)

2. 格式化货币、单位和百分比

Intl.NumberFormat的第二个参数是一个options对象,可以解锁更多强大的功能。

JavaScript 复制代码
const price = 99.9;

// 格式化货币
console.log(new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(price));
// 输出: "$99.90"
console.log(new Intl.NumberFormat('ja-JP', { style: 'currency', currency: 'JPY' }).format(price));
// 输出: "¥100" (日元会自动四舍五入到整数)

const speed = 120;
// 格式化单位
console.log(new Intl.NumberFormat('en-US', { style: 'unit', unit: 'kilometer-per-hour' }).format(speed));
// 输出: "120 km/h"
console.log(new Intl.NumberFormat('zh-CN', { style: 'unit', unit: 'gigabyte', unitDisplay: 'long' }).format(100));
// 输出: "100吉字节"

const percentage = 0.85;
// 格式化百分比
console.log(new Intl.NumberFormat('en-US', { style: 'percent' }).format(percentage));
// 输出: "85%"

3. 紧凑数字格式

这个功能非常适合在UI空间有限的地方显示大数字。

JavaScript 复制代码
const views = 123456;

console.log(new Intl.NumberFormat('en-US', { notation: 'compact' }).format(views));
// 输出: "123K"
console.log(new Intl.NumberFormat('zh-CN', { notation: 'compact' }).format(views));
// 输出: "12万"

日期和时间格式化 (Intl.DateTimeFormat):date-fns的轻量替代

处理日期是另一个常见的痛点。Intl.DateTimeFormat提供了一套完整且灵活的解决方案。

1. 基础用法与预设样式

JavaScript 复制代码
const date = new Date(); // 假设现在是 2025年7月25日

// 使用预设样式
console.log(new Intl.DateTimeFormat('en-US', { dateStyle: 'full' }).format(date));
// 输出: "Friday, July 25, 2025"
console.log(new Intl.DateTimeFormat('zh-CN', { dateStyle: 'long' }).format(date));
// 输出: "2025年7月25日"
console.log(new Intl.DateTimeFormat('en-GB', { dateStyle: 'short', timeStyle: 'short' }).format(date));
// 输出: "25/07/2025, 11:11" (英式日期格式)

2. 自定义格式与时区处理

你可以精确地控制你想显示的每一个部分。

JavaScript 复制代码
const date = new Date();

const options = {
    year: 'numeric',    // "2025"
    month: 'long',      // "July" or "七月"
    day: '2-digit',     // "25"
    weekday: 'short',   // "Fri" or "周五"
    hour: 'numeric',
    minute: 'numeric',
    second: 'numeric',
    timeZoneName: 'short',
    timeZone: 'Asia/Taipei' // 关键!指定时区
};

console.log(new Intl.DateTimeFormat('en-US', options).format(date));
// 输出: "Fri, July 25, 2025 at 11:11:10 AM GMT+8"
console.log(new Intl.DateTimeFormat('zh-TW', options).format(date));
// 输出: "2025年7月25日 週五 上午11:11:10 GMT+8"

时区处理Intl.DateTimeFormat的一大亮点,无需任何第三方库,就能准确地在不同时区之间转换和显示时间。


复数处理 (Intl.PluralRules):终结 if (count > 1)

如何根据数量显示单数或复数?比如 "1 item" 和 "2 items"。在英语里,一个if (count > 1)似乎就解决了。但在其他语言里,复数规则远比这复杂(比如俄语、阿拉伯语有好几种复数形式)。

Intl.PluralRules就是用来解决这个问题的"标准答案"。它不会直接帮你拼接字符串,而是告诉你一个数字在特定语言环境下,属于哪种复数类别"zero", "one", "two", "few", "many", "other")。

JavaScript 复制代码
// 以英语为例
const enPluralRules = new Intl.PluralRules('en-US');

console.log(enPluralRules.select(0)); // "other"
console.log(enPluralRules.select(1)); // "one"
console.log(enPluralRules.select(2)); // "other"

// 以波兰语为例,它的复数规则更复杂
const plPluralRules = new Intl.PluralRules('pl-PL');

console.log(plPluralRules.select(1)); // "one" (1)
console.log(plPluralRules.select(2)); // "few" (2, 3, 4)
console.log(plPluralRules.select(5)); // "many" (5, 6, ...)

用法:

JavaScript 复制代码
const messages = {
  zh: {
    one: `有 {count} 个项目`,
    other: `有 {count} 个项目`
  },
  en: {
    one: `Found {count} item`,
    other: `Found {count} items`
  }
};

function getPluralMessage(locale, count) {
  const langMessages = messages[locale];
  const pluralRules = new Intl.PluralRules(locale);
  const rule = pluralRules.select(count); // 获取复数类别
  return langMessages[rule].replace('{count}', count); // 根据类别选择正确的字符串
}

console.log(getPluralMessage('en', 1)); // "Found 1 item"
console.log(getPluralMessage('en', 5)); // "Found 5 items"

相对时间格式化 (Intl.RelativeTimeFormat)

这也是一个非常实用的功能,用于显示"昨天"、"5分钟前"、"3周后"这种相对时间。

JavaScript 复制代码
const rtf = new Intl.RelativeTimeFormat('zh-CN', { numeric: 'auto' });

console.log(rtf.format(-1, 'day'));   // "昨天"
console.log(rtf.format(0, 'day'));    // "今天"
console.log(rtf.format(2, 'hour'));   // "2小时后"
console.log(rtf.format(-5, 'minute'));// "5分钟前"

Intl对象是浏览器提供的一套强大、标准且高效的国际化工具集。

下次再遇到国际化需求时,先别急着npm install。看一看浏览器原生的Intl对象,它可能已经为你准备好了更轻量、更标准的解决方案。

相关推荐
ywf12151 小时前
前端的dist包放到后端springboot项目下一起打包
前端·spring boot·后端
恋猫de小郭1 小时前
2026,Android Compose 终于支持 Hot Reload 了,但是收费
android·前端·flutter
hpoenixf7 小时前
2026 年前端面试问什么
前端·面试
还是大剑师兰特7 小时前
Vue3 中的 defineExpose 完全指南
前端·javascript·vue.js
泯泷7 小时前
阶段一:从 0 看懂 JSVMP 架构,先在脑子里搭出一台最小 JSVM
前端·javascript·架构
mengchanmian8 小时前
前端node常用配置
前端
华洛8 小时前
利好打工人,openclaw不是企业提效工具,而是个人助理
前端·javascript·产品经理
xkxnq9 小时前
第六阶段:Vue生态高级整合与优化(第93天)Element Plus进阶:自定义主题(变量覆盖)+ 全局配置与组件按需加载优化
前端·javascript·vue.js
A黄俊辉A9 小时前
vue css中 :global的使用
前端·javascript·vue.js
小码哥_常10 小时前
被EdgeToEdge适配折磨疯了,谁懂!
前端