Intl对象才是前端国际化的正确姿势

作者: jie 分类: JavaScript 发布时间: 2026-02-28 16:56

Intl 是什么?为什么前端开发都应该了解它?

Intl构成了 ECMAScript 国际化 API,该 API 提供了语言敏感的字符串比较、数字格式化、日期和时间格式化等功能。Intl 对象本身包含了多个构造函数,专门用于不同场景的国际化:

  • • Intl.DateTimeFormat: 日期和时间格式化
  • • Intl.NumberFormat:数字(包括货币、百分比)格式化
  • • Intl.RelativeTimeFormat:相对时间格式化(如“3天前”)
  • • Intl.ListFormat:列表格式化(如“A、B和C”)
  • • Intl.PluralRules:复数规则
  • • Intl.DisplayNames:语言、地区、脚本的显示名称

在控制台输入Intl,会打印这些信息:

1、DateTimeFormat – 日期、时间格式化

使用示范

const date = new Date() // 当时时间是2026/2/27 16:28:07
const formatter = new Intl.DateTimeFormat("zh-CN", {
	dateStyle: "medium", // 可选:full, long, medium, short
	timeStyle: "short", // 同样可选
	hour12: false, // 是否使用12小时制
})
console.log(formatter.format(date)) // 显示 2026年2月27日 16:28:07

dateStyle 和 timeStyle 是预设样式,还可以用定制化更高的方式

高定制化使用

new Intl.DateTimeFormat("zh-CN", {
	year: "numeric",
	month: "2-digit",
	day: "2-digit",
	hour: "2-digit",
	minute: "2-digit",
	second: "2-digit",
	hour12: false,
}).format(new Date())

可以输出 '2026/02/27 16:38:36',注意 dateStyle 和 timeStyle 不能与高定制化配置的方式混用,不然浏览器会报错

2、Intl.NumberFormat – 数字、货币、百分比格式化

前端经常遇到数字格式化的需求:金额、百分比、千位分隔符。

❌ 错误示范:

const price = 1234567.89
// 正则替换
console.log(price.toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ","))

输出 1,234,567.89, 这在德国人眼里是错误的显示方式,他们用 1.234.567,89 这种格式

✅ Intl 正确姿势:

const price = 1234567.89

// 中国习惯
new Intl.NumberFormat("zh-CN").format(price) // 输出 '1,234,567.89'

// 德国习惯
new Intl.NumberFormat("de-DE").format(price) // 输出 '1.234.567,89'

// 印度习惯
new Intl.NumberFormat("en-IN").format(price) // 输出 '12,34,567.89'

货币格式

const price = 1234567.89

// 美国习惯
new Intl.NumberFormat("en-US", {
	style: "currency",
	currency: "USD",
}).format(price)
// 输出 '$1,234,567.89'

// 中国习惯
new Intl.NumberFormat("zh-CN", {
	style: "currency",
	currency: "USD",
}).format(price)
// 输出 "¥1,234,567.89"

百分比和小数格式

const num = 0.123

new Intl.NumberFormat("zh-CN", {
	style: "percent", // percent是百分比显示
	minimumIntegerDigits: 2, // 最小整数位数,默认1
	minimumFractionDigits: 2, // 最小小数位数
	maximumFractionDigits: 2, // 最大小数位数
	// minimumSignificantDigits: 2, // 最小有效位数
	// maximumSignificantDigits: 2, // 最大有效位数
}).format(0.231)

// 输出 `'23.10%'`

当同时定义了有效位数和小数位数会怎么样呢?

根据规范,会把数字格式化选项分为两个互斥的组:

  • • 组A(数字位数): minimumIntegerDigits、minimumFractionDigits、maximumFractionDigits
  • • 组B(有效数位数):minimumSignificantDigits、maximumSignificantDigits

如果定义了组B中的任何一个属性,组A就会被完全忽略。你可以用 roundingPriority 选项,来更精细地控制冲突时的行为,现已被广泛支持。

3、RelativeTimeFormat – 相对时间格式化

const rtf = new Intl.RelativeTimeFormat("zh-CN", {
	numeric: "auto", // 自动选择“昨天”还是“1天前”
	style: "long", // 长格式
})

rtf.format(-1, "day") // 输出:'昨天'
rtf.format(-3, "day") // 输出:'3天前'
rtf.format(2, "day") // 输出:'后天'
rtf.format(-200, "hour") // 输出:'200小时前'
const rtf = new Intl.RelativeTimeFormat("en-US", {
	numeric: "auto", // 自动选择“昨天”还是“1天前”
	style: "long", // 长格式
})

rtf.format(-1, "day") // 输出:'yesterday'
rtf.format(1, "day") // 输出:'tomorrow'
rtf.format(2, "day") // 输出:'in 2 days'
rtf.format(-200, "hour") // 输出:'200 minutes ago'

不过对于复合单位的输出,比如”3天2小时前“这种格式,通过它没办法直接得到,需要自己动手实现。

建议:除非产品明确要求“3天2小时前”这种格式,否则推荐只用最大的单位。因为这是大多数用户习惯的阅读方式,也是主流产品的做法。

4、Intl.ListFormat – 列表格式化

const list = ["A", "B", "C"]
const lf = new Intl.ListFormat("en-US", {
	style: "long", // 可选:long, short, narrow
	type: "conjunction", // conjunction是连接词(and),disjunction 是连接词(or)
})
lf.format(list) // 输出:'A, B, and C'
const list = ["A", "B", "C"]
const lf = new Intl.ListFormat("zh-CN", {
	style: "long", // 可选:long, short, narrow
	type: "conjunction", // conjunction是连接词(and),disjunction 是连接词(or)
})
lf.format(list) // 输出:'A、B和C'

5、Intl.PluralRules – 复数规则

const prUs = new Intl.PluralRules("en-US")

function getAppleCountMessage(count) {
	const pluralRule = prUs.select(count)
	const messages = {
		one: `There is ${count} apple.`,
		other: `There are ${count} apples.`,
	}
	return messages[pluralRule] || messages.other
}

getAppleCountMessage(1) // 输出:"There is 1 apple."
getAppleCountMessage(3) // 输出:"There are 3 apples."

// 不同语言的复数规则不同
const frPr = new Intl.PluralRules("fr-FR")
console.log(frPr.select(1)) // "one"  (法语1是名词单数)
console.log(frPr.select(2)) // "other"  (法语2是名词复数)

对于中文,这个API显然没有什么作用

6、Intl.DisplayNames – 语言、地区、脚本的显示名称

对于多语言的网站,你显示需要这个API。

const languageName = new Intl.DisplayNames(["zh-CN"], {
	type: "language",
})

languageName.of("en") // 输出:'英语'
languageName.of("zh") // 输出:'中文'
languageName.of("ja") // 输出:'日语'
const regionName = new Intl.DisplayNames(["zh-CN"], {
	type: "region",
})

regionName.of("CN") // 输出:'中国'
regionName.of("JP") // 输出:'日本'
regionName.of("US") // 输出:'美国'

7、浏览器兼容性怎么样?

API 名称现代浏览器 (Chrome/Edge/Firefox)Safari生产环境建议
Intl.DateTimeFormat✅ 全支持✅ 10.1+可用
Intl.NumberFormat✅ 全支持✅ 10.1+可用
Intl.RelativeTimeFormat✅ 71✅ 14+可用,Safari注意
Intl.ListFormat✅ 72✅ 14.1可用,Safari注意
Intl.PluralRules✅ 63✅ 13+可用,Safari注意
Intl.DisplayNames✅ 81✅ 14.1可用,Safari注意

8、总结

为什么我们要用 Intl:

  • • 日期格式、数字格式、列表格式都符合当地习惯
  • • 减少第三方库的使用
  • • 性能更优秀

以上是 Intl 对象的核心成员

发表回复