一、为什么选择 Intl API 而非第三方库
长期以来,前端国际化方案依赖于 moment.js、i18next 等重量级库。这些库虽然在历史时期发挥过重要作用,但体积庞大,且许多功能已经被现代浏览器原生支持。ECMAScript Internationalization API(简称 Intl)是JavaScript内建的国际化解决方案,无需引入任何外部依赖即可完成数字格式化、货币显示、日期时间本地化、字符串排序、相对时间格式、列表连接等任务。它拥有以下核心优势:
- 零体积:浏览器原生实现,打包体积无增加。
- 高性能:底层由C++实现,速度远超纯JavaScript库。
- 自动适配:根据用户操作系统语言和地区自动调整格式。
- 标准化:符合ECMA-402规范,兼容现代所有主流浏览器。
本文将带你逐一攻克 Intl 的各个核心类,通过20多个可直接运行的实例,让你彻底摆脱对第三方格式化库的依赖。
二、Intl.NumberFormat:数字与货币格式化
数字在不同地区的表示方式差异巨大:小数点是逗号还是点?分组分隔符是什么?货币符号放在前面还是后面?Intl.NumberFormat 可以优雅地解决这些问题。
2.1 基本数字格式化
const number = 1234567.89;
// 默认为用户浏览器语言和国家
console.log(new Intl.NumberFormat().format(number));
// 美国英语输出: "1,234,567.89"
// 指定德语格式
console.log(new Intl.NumberFormat('de-DE').format(number));
// 输出: "1.234.567,89"
// 指定印地语格式
console.log(new Intl.NumberFormat('hi-IN').format(number));
// 输出: "12,34,567.89" (印度的特殊分组规则)
通过 locales 参数可以显式指定语言标签(BCP 47),结合可选选项参数可实现更精细的控制。
2.2 货币格式化
货币格式化需指定 style: ‘currency’ 以及 currency 货币代码。
const price = 2999.5;
// 人民币
console.log(new Intl.NumberFormat('zh-CN', {
style: 'currency',
currency: 'CNY'
}).format(price));
// 输出: "¥2,999.50"
// 美元
console.log(new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD'
}).format(price));
// 输出: "$2,999.50"
// 欧元(德国)
console.log(new Intl.NumberFormat('de-DE', {
style: 'currency',
currency: 'EUR'
}).format(price));
// 输出: "2.999,50 €"
还可以设置 currencyDisplay 为 ‘name’ 或 ‘code’ 来显示完整的货币名称或代码。
2.3 百分比与单位格式化
style: ‘percent’ 将数值乘以100并添加百分号;style: ‘unit’ 可用于长度、体积等单位。
// 百分比
console.log(new Intl.NumberFormat('en-US', {
style: 'percent',
minimumFractionDigits: 1
}).format(0.123));
// 输出: "12.3%"
// 单位(公里)
console.log(new Intl.NumberFormat('zh-CN', {
style: 'unit',
unit: 'kilometer'
}).format(15));
// 输出: "15公里"
三、Intl.DateTimeFormat:日期时间本地化
日期格式化是国际化中最常见的需求。Intl.DateTimeFormat 提供了极为丰富的选项,可以组合出几乎任何所需的日期时间格式。
3.1 基础日期格式
const date = new Date(2025, 5, 15, 14, 30, 0); // 2025年6月15日 14:30
// 用户本地格式
console.log(new Intl.DateTimeFormat().format(date));
// 中文浏览器通常输出: "2025/6/15"
// 美国英语
console.log(new Intl.DateTimeFormat('en-US').format(date));
// 输出: "6/15/2025"
// 英国英语
console.log(new Intl.DateTimeFormat('en-GB').format(date));
// 输出: "15/06/2025"
3.2 自定义组件:年月日与星期
通过 options 对象的 year、month、day、weekday 等属性可精细控制输出内容。
const options = {
weekday: 'long',
year: 'numeric',
month: 'long',
day: 'numeric'
};
console.log(new Intl.DateTimeFormat('zh-CN', options).format(date));
// 输出: "2025年6月15日星期日"
console.log(new Intl.DateTimeFormat('en-US', options).format(date));
// 输出: "Sunday, June 15, 2025"
3.3 时间与时区
灵活显示时分秒,并支持时区转换。
const timeOptions = {
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
hour12: false,
timeZone: 'America/New_York'
};
console.log(new Intl.DateTimeFormat('zh-CN', timeOptions).format(date));
// 根据北京时间转为纽约时间输出
3.4 相对时间格式:Intl.RelativeTimeFormat
显示“3天前”、“5分钟后”等相对时间描述。
const rtf = new Intl.RelativeTimeFormat('zh-CN', { numeric: 'always' });
console.log(rtf.format(-3, 'day')); // "3天前"
console.log(rtf.format(1, 'week')); // "1周后"
console.log(rtf.format(5, 'minute')); // "5分钟后"
// 使用 numeric: 'auto' 在可能时显示"昨天"等
const rtfAuto = new Intl.RelativeTimeFormat('zh-CN', { numeric: 'auto' });
console.log(rtfAuto.format(-1, 'day')); // "昨天"
console.log(rtfAuto.format(0, 'day')); // "今天"
四、Intl.Collator:多语言字符串排序
JavaScript原生的 Array.prototype.sort() 默认按Unicode码点排序,对多语言用户会产生意想不到的顺序。Intl.Collator 提供了语言敏感的字符串比较器。
const names = ['张三', '李四', '王五', 'änna', 'Béla', 'Catherine'];
// 默认拼音排序(中文)
console.log(names.sort(new Intl.Collator('zh').compare));
// 大致按拼音顺序排列
// 德语排序
console.log(names.sort(new Intl.Collator('de').compare));
// 忽略大小写和变音符号
const collator = new Intl.Collator('en', {
sensitivity: 'base',
numeric: true // 自然数字排序
});
const mixed = ['a10', 'a2', 'a1'];
console.log(mixed.sort(collator.compare));
// 输出: ["a1", "a2", "a10"] (真正的数字顺序)
sensitivity 选项控制比较的敏感度:‘base’ 忽略大小写和变音符号,‘accent’ 区分变音符号但忽略大小写,‘case’ 区分大小写但忽略变音,‘variant’ 区分所有。
五、其他实用 Intl 功能
5.1 Intl.ListFormat:列表连接词
将数组中的项目按照语言习惯连接成字符串,如“A、B和C”。
const list = ['苹果', '香蕉', '橘子'];
console.log(new Intl.ListFormat('zh-CN', {
style: 'long',
type: 'conjunction'
}).format(list));
// 输出: "苹果、香蕉和橘子"
console.log(new Intl.ListFormat('en-US', {
style: 'long',
type: 'disjunction'
}).format(['apples', 'oranges', 'bananas']));
// 输出: "apples, oranges, or bananas"
5.2 Intl.DisplayNames:语言、货币、地区的显示名称
const displayNames = new Intl.DisplayNames('zh-CN', { type: 'language' });
console.log(displayNames.of('en')); // "英语"
console.log(displayNames.of('de')); // "德语"
const currencyNames = new Intl.DisplayNames('zh-CN', { type: 'currency' });
console.log(currencyNames.of('USD')); // "美元"
console.log(currencyNames.of('EUR')); // "欧元"
六、构建一个完整的本地化工具模块
基于上述API,我们可以封装一个轻量级的本地化工具,便于在项目中复用。以下模块自动检测用户语言,提供格式化数字、货币、日期和相对时间的方法:
// i18n.js
const userLocale = navigator.language || 'zh-CN';
export const formatNumber = (num, options = {}) => {
return new Intl.NumberFormat(userLocale, options).format(num);
};
export const formatCurrency = (amount, currency = 'CNY') => {
return new Intl.NumberFormat(userLocale, {
style: 'currency',
currency
}).format(amount);
};
export const formatDate = (date, options = {}) => {
return new Intl.DateTimeFormat(userLocale, options).format(new Date(date));
};
export const formatRelative = (diffValue, unit) => {
const rtf = new Intl.RelativeTimeFormat(userLocale, { numeric: 'auto' });
return rtf.format(diffValue, unit);
};
// 计算两个日期的相对描述
export const formatTimeAgo = (date) => {
const now = Date.now();
const past = new Date(date).getTime();
const diffSeconds = Math.round((past - now) / 1000);
const absDiff = Math.abs(diffSeconds);
const units = [
{ unit: 'year', seconds: 31536000 },
{ unit: 'month', seconds: 2592000 },
{ unit: 'week', seconds: 604800 },
{ unit: 'day', seconds: 86400 },
{ unit: 'hour', seconds: 3600 },
{ unit: 'minute', seconds: 60 },
{ unit: 'second', seconds: 1 }
];
for (const { unit, seconds } of units) {
if (absDiff >= seconds || unit === 'second') {
const value = Math.round(diffSeconds / seconds);
return formatRelative(value, unit);
}
}
};
// 使用示例
console.log(formatCurrency(1299, 'CNY')); // ¥1,299.00
console.log(formatDate(new Date(), { dateStyle: 'full' }));
console.log(formatTimeAgo('2025-05-20T12:00:00'));
这个轻量模块总计不超过50行代码,却覆盖了大多数国际化需求。无需引入任何第三方依赖,体积为零,性能极佳。
七、最佳实践与注意事项
- 始终提供用户语言选项:使用 navigator.language 作为默认值,同时允许用户手动切换并存储到localStorage。
- 回退策略:Intl构造函数接受一个语言数组作为 locales 参数,浏览器会按顺序选择第一个支持的语言。例如 [‘zh-TW’, ‘zh’, ‘en’]。
- 缓存 Intl 实例:反复创建相同的 Intl.NumberFormat 等实例会有微小开销。对于固定格式,可将其缓存复用。
- 避免在服务端渲染中使用:Intl依赖浏览器环境,在Node.js中从IQ 16开始支持,但使用前需确保环境兼容性。
- 检查支持情况:绝大多数现代浏览器均已完整支持Intl API,但若需兼容非常老旧的浏览器,可使用 Intl.js polyfill作为回退方案。
八、总结
JavaScript Intl API 提供了一整套功能强大且易于使用的国际化工具。通过本文的讲解,你已经掌握了数字、货币、日期、排序、列表连接、相对时间以及显示名称等核心能力。利用这些原生API,你可以放心地放弃庞大的第三方库,构建出轻量、高效、用户友好的多语言Web应用。
国际化不再是前端开发的难题,而是一次优雅的原生体验。立即在你的下一个项目中尝试使用Intl API,体验无依赖开发的畅快感受。

