引言
Intl
对象是 ECMAScript
国际化 API
的一个命名空间, 它提供了以下几个功能:
- 字符串比较
- 数字格式化
- 日期时间格式化
- 相对时间格式化
- 根据数量获取复数形式
- 格式化字符串列表
- 文本分割
一、排序: Intl.Collator
Intl.Collator
是一个构造函数, 实例对象提供一系列的接口, 可根据设置的语言区域对字符串进行比较
- 语法:
new Intl.Collator([locales[, options]])
- 可选参数
locales
说明: 用于设置国际化语言类型, 是一个 BCP 47 语言标记 的字符串, 或者是一个包括多个语言标记的数组
- 可选参数
options
说明: 可选配置, 配置有点多这里就不一一展开, 具体可查阅 MDN-Collator
1.1 三个方法
- 实例方法 Intl.Collator.prototype.compare() 根据给定的语言区域, 对给定的两个字符串进行比较, 如下代码所示:
- 如果第一个字符大于第二个则返回正整数
- 如果第一个字符小于第二个则返回负整数
- 如果第一个字符等于第二个则返回数字
0
js
console.log(new Intl.Collator().compare("2", "1")); // 1, 或一些其他的正值
console.log(new Intl.Collator().compare("1", "2")); // -1, 或一些其他的负值
console.log(new Intl.Collator().compare("1", "1")); // 0
- Intl.Collator.prototype.resolvedOptions() 方法返回
Collator
实例对象计算后的所有配置信息
js
const col = new Intl.Collator('zh')
console.log(col.resolvedOptions())
- 静态方法 Intl.Collator.supportedLocalesOf() 方法返回一个数组, 该方法从提供的语言区域列表中, 筛选出符合规则或者说支持的语言列表
js
const locales = ['ban', 'id-u-co-pinyin', 'de-ID'];
const options = { localeMatcher: 'lookup' };
console.log(Intl.Collator.supportedLocalesOf(locales, options));
// ["id-u-co-pinyin", "de-ID"]
1.2 在 Array.prototype.sort() 中使用
方法 Intl.Collator.prototype.compare()
参数和返回值格式, 和 Array.prototype.sort()
方法的函数参数是一致的, 也就是说它能够直接作为 Array.prototype.sort()
参数被使用
js
['b', 'f', 'a', 'd', 'c', 'e'].sort(new Intl.Collator().compare)
// ['a', 'b', 'c', 'd', 'e', 'f']
1.3 不同 locales 效果
下面我们来看不同 locales
下, 方法 Intl.Collator.prototype.compare()
的一个具体表现, 已知:
- 在德语(
de
)中,ä
是排在字母z
的前面 - 在瑞典语(
sv
)中,ä
则是排在字母z
的后面
js
// de: 德语
console.log(new Intl.Collator('de').compare('ä', 'z')); // -1
console.log(['a', 'z', 'ä'].sort(new Intl.Collator('de').compare)); // ['a', 'ä', 'z']
// sv: 瑞典语
console.log(new Intl.Collator('sv').compare('ä', 'z')); // 1
console.log(['a', 'z', 'ä'].sort(new Intl.Collator('sv').compare)); // ['a', 'z', 'ä']
1.4 数字字符串排序
开始前我们来看一个经典的 数字字符串
排序问题, 如下代码如果使用 Array.prototype.sort()
直接对 ['15', '2', '100']
进行排序, 排序结果将会是 ['100', '15', '2']
而之所以会是这么一个结果, 那是因为在 JS
对于字符串的比较是按位进行对比的, 也就是说会先对比第一位字符、如若相等则继续对比下一位字符...
js
['15', '2', '100'].sort();
// 结果是: ['100', '15', '2']
很显然, 大部分情况下上面👆🏻这个排序结果并不是我们期望的一个结果; 对于字符串如果我们期望按照数字来进行排序, 这里可以通过 Intl.Collator
的 numeric
参数来实现:
js
['15', '2', '100'].sort(new Intl.Collator(void 0, { numeric: true }).compare);
// 结果是: ['2', '15', '100']
1.5 中文按照拼音排序
如题, 现在我们有个需求, 需要将一组 中文字符串
按照 中文拼音
进行排序, 如下代码如果我们直接通过 Array.prototype.sort()
得出的结果显然并不能符合我们的一个预期
js
const list = ['不', '都', '阿', '非', '词']
list.sort()
// 结果是: ['不', '词', '都', '阿', '非']
但是这里我们如果使用 Intl.Collator
就很容易实现咯, 只需要设置正确的 locales
即可
js
const list = ['不', '都', '阿', '非', '词']
list.sort(new Intl.Collator('zh').compare)
// 结果是: ['阿', '不', '词', '都', '非']
二、时间格式化: Intl.DateTimeFormat
Intl.Collator
是一个构造函数, 实例对象提供一系列的接口, 可根据设置的语言区域对时间进行格式化
- 语法:
new Intl.DateTimeFormat([locales[, options]])
- 可选参数
locales
说明: 用于设置国际化语言类型, 是一个 BCP 47 语言标记的字符串, 或者是一个包括多个语言标记的数组
- 可选参数
options
说明: 可选配置, 配置有点多这里就不一一展开, 具体可查阅 MDN-DateTimeFormat
- 实例方法 Intl.DateTimeFormat.prototype.format() 可根据设置的语言区域, 对时间参数进行一个格式化
js
const df = new Intl.DateTimeFormat('zh', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
hour12: false
})
df.format(new Date()) // '2023/08/22 08:12:41'
参数说明:
year: 'numeric'
表示使用数值来表示年份2-digit
表示使用2
位数字来进行表示, 如果数值小于10
则会在前面添加0
hour12: false
表示采用24
小时制
- 实例方法 Intl.DateTimeFormat.prototype.formatToParts() 作用和
format
一致, 只是它返回的是一个对象数组, 对象数组包含了相对时间的一些列值, 拿到该对象后我们可以实现自定义格式化
js
const df = new Intl.DateTimeFormat('zh', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
hour12: false
})
df.formatToParts(new Date()) // '2023/08/22 08:12:41'
- 实例方法 Intl.DateTimeFormat.prototype.formatRange() 可根据设置的语言区域, 将给定的两个日期参数进行格式化
js
const date1 = new Date(Date.UTC(1906, 0, 10, 10, 0, 0));
const date2 = new Date(Date.UTC(1906, 0, 10, 11, 0, 0));
const date3 = new Date(Date.UTC(1906, 0, 20, 10, 0, 0));
const fmt1 = new Intl.DateTimeFormat("en", {
year: "2-digit",
month: "numeric",
day: "numeric",
hour: "numeric",
minute: "numeric",
});
console.log(fmt1.format(date1)); // '1/10/06, 10:00 AM'
console.log(fmt1.formatRange(date1, date2)); // '1/10/06, 10:00 -- 11:00 AM'
console.log(fmt1.formatRange(date1, date3)); // '1/10/06, 10:00 AM -- 1/20/07, 10:00 AM'
const fmt2 = new Intl.DateTimeFormat("en", {
year: "numeric",
month: "short",
day: "numeric",
});
console.log(fmt2.format(date1)); // 'Jan 10, 1906'
console.log(fmt2.formatRange(date1, date2)); // 'Jan 10, 1906'
console.log(fmt2.formatRange(date1, date3)); // 'Jan 10 -- 20, 1906'
- 实例方法 Intl.DateTimeFormat.prototype.formatRangeToParts() 作用和
formatRange
一致, 只是它返回的是一个对象数组, 对象数组包含了相对时间的一些列值, 拿到该对象后我们可以实现自定义格式化
js
const date1 = new Date(Date.UTC(1906, 0, 10, 10, 0, 0));
const date2 = new Date(Date.UTC(1906, 0, 10, 11, 0, 0));
const fmt = new Intl.DateTimeFormat("en", {
hour: "numeric",
minute: "numeric",
});
console.log(fmt.formatRange(date1, date2)); // '10:00 -- 11:00 AM'
fmt.formatRangeToParts(date1, date2);
// [
// { type: 'hour', value: '10', source: "startRange" },
// { type: 'literal', value: ':', source: "startRange" },
// { type: 'minute', value: '00', source: "startRange" },
// { type: 'literal', value: ' -- ', source: "shared" },
// { type: 'hour', value: '11', source: "endRange" },
// { type: 'literal', value: ':', source: "endRange" },
// { type: 'minute', value: '00', source: "endRange" },
// { type: 'literal', value: ' ', source: "shared" },
// { type: 'dayPeriod', value: 'AM', source: "shared" }
// ]
- 实例方法 Intl.DateTimeFormat.prototype.resolvedOptions() 方法返回实例对象计算后的所有配置信息
js
const df = new Intl.DateTimeFormat('zh')
df.resolvedOptions()
- 静态方法 Intl.DateTimeFormat.supportedLocalesOf() 方法返回一个数组, 该方法从提供的语言区域列表中, 筛选出符合规则或者说支持的语言列表
js
const locales = ['ban', 'id-u-co-pinyin', 'de-ID'];
const options = { localeMatcher: 'lookup' };
console.log(Intl.DateTimeFormat.supportedLocalesOf(locales, options));
// ["id-u-co-pinyin", "de-ID"]
三、相对时间格式化 Intl.RelativeTimeFormat
在很多新闻、社区等地方经常可以看到很多相对时间的计算, 比如: 今天、昨天、上周...., 在没有 Intl.RelativeTimeFormat
之前我们可能得通过复杂的时间计算来得到这个相对值, 现在有了它我们就可以很方便的完成相对时间的计算!!! 下面我们来简单了解下 Intl.RelativeTimeFormat
的一个基本使用
Intl.RelativeTimeFormat
是一个构造函数, 实例对象提供一系列的接口, 可根据设置的语言区域对相对时间进行格式化
- 语法:
new Intl.RelativeTimeFormat([locales[, options]])
- 可选参数
locales
说明: 用于设置国际化语言类型, 是一个 BCP 47 语言标记的字符串, 或者是一个包括多个语言标记的数组
- 可选参数
options
说明: 可选配置, 配置有点多这里就不一一展开, 具体可查阅 MDN-RelativeTimeFormat
3.1 四个方法
- 实例方法 Intl.RelativeTimeFormat.prototype.format(num, unit) 根据给定的语言区域, 返回一个相对时间, 其中参数
num
是一个具体的整数,unit
则是对应的单位, 可选值有:year
、quarter
、month
、week
、day
、hour
、minute
、second
js
const rtf = new Intl.RelativeTimeFormat('zh')
rtf.format(1, 'day') // 1天后
- 实例方法 Intl.RelativeTimeFormat.prototype.formatToParts() 作用和
format
一致, 只是它返回的是一个对象数组, 对象数组包含了相对时间的一些列值, 拿到该对象后我们可以实现自定义格式化
js
const rtf = new Intl.RelativeTimeFormat('zh')
rtf.formatToParts(1, 'day') // 1天后
- 实例方法 Intl.RelativeTimeFormat.prototype.resolvedOptions() 用于获取当前实例对象的配置信息
js
const rtf = new Intl.RelativeTimeFormat('zh')
rtf.resolvedOptions()
- 静态方法 Intl.RelativeTimeFormat.supportedLocalesOf() 方法返回一个数组, 该方法从提供的语言区域列表中, 筛选出符合规则或者说支持的语言列表
js
const locales = ['ban', 'id-u-co-pinyin', 'de-ID'];
const options = { localeMatcher: 'lookup' };
console.log(Intl.RelativeTimeFormat.supportedLocalesOf(locales, options));
// ["id-u-co-pinyin", "de-ID"]
3.2 更符合本土语言
上文演示 format(1, 'day')
返回的结果是 1天后
, 但大部分情况下我们可能更想要得到的结果是 明天
; 这里我们可以修改 numeric
配置来实现
js
const rtf = new Intl.RelativeTimeFormat('zh', {
numeric: 'auto'
});
rtf.format(0, 'day') // 今天
rtf.format(-1, 'day'); // 昨天
rtf.format(1, 'day'); // 明天
rtf.format(-2, 'day') // 前天
rtf.format(2, 'day') // 后天
rtf.format(0, 'year') // 今年
rtf.format(1, 'year') // 明年
rtf.format(-1, 'year') // 明年
四、数字格式化: Intl.NumberFormat
Intl.NumberFormat
是一个构造函数, 实例对象提供一系列的接口, 可根据设置的语言区域对数字进行格式化
- 语法:
new Intl.NumberFormat([locales[, options]])
- 可选参数
locales
说明: 用于设置国际化语言类型, 是一个 BCP 47 语言标记的字符串, 或者是一个包括多个语言标记的数组
- 可选参数
options
说明: 可选配置, 配置有点多这里就不一一展开, 具体可查阅 MDN-NumberFormat
4.1 六个方法
- 实例方法 Intl.NumberFormat.prototype.format, 根据给定的语言区域, 对给定是数字进行格式化
js
const nf = new Intl.NumberFormat('zh');
console.log(nf.format(654321.987)); // 654,321.987
- 实例方法 Intl.NumberFormat.prototype.formatToParts() 作用和
format
一致, 只是它返回的是一个对象数组, 对象数组包含了格式化后各个部分的值, 拿到该对象后我们可以实现自定义格式化
js
const nf = new Intl.NumberFormat('zh');
nf.formatToParts(12312312313)
- 实例方法 Intl.NumberFormat.prototype.formatRange() 根据给定的语言区域, 将给定的两个数值参数格式化为一个数值范围字符串
js
const nf = new Intl.NumberFormat('zh');
nf.formatRange(101231, 1231231.212) // '101,231-1,231,231.212'
- 实例方法 Intl.NumberFormat.prototype.formatRangeToParts() 作用和
formatRange
一致, 只是它返回的是一个对象数组, 对象数组包含了格式化后各个部分的值, 拿到该对象后我们可以实现自定义格式化
js
const nf = new Intl.NumberFormat('zh');
nf.formatRangeToParts(101231, 1231231.212)
- 实例方法 Intl.NumberFormat.prototype.resolvedOptions() 用于获取当前实例对象的配置信息
js
const nf = new Intl.NumberFormat('zh');
nf.resolvedOptions()
- 静态方法 Intl.NumberFormat.supportedLocalesOf() 方法返回一个数组, 该方法从提供的语言区域列表中, 筛选出符合规则或者说支持的语言列表
js
const locales = ['ban', 'id-u-co-pinyin', 'de-ID'];
const options = { localeMatcher: 'lookup' };
console.log(Intl.RelativeTimeFormat.supportedLocalesOf(locales, options));
// ["id-u-co-pinyin", "de-ID"]
4.2 千位分隔符分隔
在实际开发问题中, 对于较大数值常常需要使用千位分隔符进行分隔, 在日常中我们可能会使用 Number.toLocaleString()
方法来实现
js
(12345678.231).toLocaleString() // '12,345,678.231'
当然我们这里其实也可以使用 Intl.NumberFormat
来实现
js
const nf = new Intl.NumberFormat()
nf.format(1234567890.12) // '1,234,567,890.12'
如果只是需要将数字按每 3
位数进行一个分割, 那么 Number.toLocaleString()
和 Intl.NumberFormat
都是可以做到, 但是假设想要按照每 4
位数字进行分割, 那么这里就只能通过 Intl.NumberFormat
来实现了
js
const nf = new Intl.NumberFormat(void 0, {
minimumFractionDigits: 4
})
nf.format(1234567890.12) // '1,234,567,890.1200'
这里需要注意的是当小数位数小于 minimumFractionDigits
将会自动补 0
4.3 数字不足位数补 0
有时我们可能需要为数字补全, 比如如果数字小于 10
, 如 8
则需要进行补全也就是 08
; 面对该需求我们可以使用 ES6
现成的 API
padStart()
进行补全
js
String(1).padStart(2, '0') // '01'
这里除了使用 padStart()
其实使用 Intl.NumberFormat
也是可以实现的
js
const nf = new Intl.NumberFormat(void 0, {
minimumIntegerDigits: 2
})
nf.format(8) // '08'
同时, 如果位数较大, 超过了 3
位, 那么为了避免遇到逗号分隔的尴尬, 这里可以设置 useGrouping
为 false
js
new Intl.NumberFormat(void 0, {
minimumIntegerDigits: 10,
}).format(8) // '0,000,000,008'
new Intl.NumberFormat(void 0, {
useGrouping: false,
minimumIntegerDigits: 10,
}).format(8) // '0000000008'
4.4 金额
通过 Intl.NumberFormat
可将数值按照金额进行格式化
js
new Intl.NumberFormat('zh', {
style: 'currency',
currency: 'CNY',
currencyDisplay: 'name'
}).format(12345.6789) // 12,345.68人民币
new Intl.NumberFormat('zh', {
style: 'currency',
currency: 'USD',
currencyDisplay: 'name'
}).format(12345.6789) // 12,345.68美元
new Intl.NumberFormat('en', {
style: 'currency',
currency: 'USD',
currencyDisplay: 'name'
}).format(12345.6789) // 12,345.68 US dollars
当然大部分情况下我们可能需要将给定的数字格式化为 ¥ + 数字
或者 $ + 数字
的形式, 那么我们只需要在上面 demo
的基础上去除 currencyDisplay
配置即可
js
new Intl.NumberFormat('zh', {
style: 'currency',
currency: 'CNY',
}).format(12345.6789) // ¥12,345.68'
new Intl.NumberFormat('en', {
style: 'currency',
currency: 'USD',
}).format(12345.6789) // $12,345.68
4.5 数字转中文
在时间处理中, 我们都知道 new Date().getDay()
获取到的是数字, 在转换过程中我们可能需要将数字转为中文, 这里我们其实可以借用 Intl.NumberFormat
来实现, 而不是通过创建数字和中文的映射表来实现
js
const nf = new Intl.NumberFormat('zh-Hans-CN-u-nu-hanidec')
const day = new Date().getDay()
const format = `星期${nf.format(day)}` // 星期六
五、字符串列表格式化: Intl.ListFormat
Intl.ListFormat
是一个构造函数, 实例对象提供一系列的接口, 可根据设置的语言区域对字符串列表进行格式化
- 语法:
new Intl.ListFormat([locales[, options]])
- 可选参数
locales
说明: 用于设置国际化语言类型, 是一个 BCP 47 语言标记 的字符串, 或者是一个包括多个语言标记的数组
- 可选参数
options
说明: 可选配置, 配置有点多这里就不一一展开, 具体可查阅 MDN-ListFormat
- 实例方法 Intl.ListFormat.prototype.format() 根据给定的语言区域, 对给定是字符串列表进行格式化
js
const list = ["Motorcycle", "Bus", "Car"];
new Intl.ListFormat("en", { style: "long", type: "conjunction" }).format(list)
// Motorcycle, Bus and Car
new Intl.ListFormat("en", { style: "short", type: "disjunction" }).format(list)
// Motorcycle, Bus or Car
new Intl.ListFormat("en", { style: "narrow", type: "unit" }).format(list)
// Motorcycle Bus Car
new Intl.ListFormat("zh", { style: "long", type: "conjunction" }).format(list)
// Motorcycle、Bus和Car
new Intl.ListFormat("zh", { style: "short", type: "disjunction" }).format(list)
// Motorcycle、Bus或Car
- 实例方法 Intl.ListFormat.prototype.formatToParts() 和
format
作用一致, 只不过它返回的是一个对象数组, 包含格式化后的各个部分的内容, 方便我们进行自定义
js
const list = ["Motorcycle", "Bus", "Car"];
new Intl.ListFormat("en", { style: "long", type: "conjunction" }).formatToParts(list)
- 实例方法 Intl.ListFormat.prototype.resolvedOptions() 用于获取当前实例对象的配置信息
js
const lf = new Intl.ListFormat("en", { style: "long", type: "conjunction" })
lf.resolvedOptions()
- 静态方法 Intl.ListFormat.supportedLocalesOf() 方法返回一个数组, 该方法从提供的语言区域列表中, 筛选出符合规则或者说支持的语言列表
js
const locales = ['ban', 'id-u-co-pinyin', 'de-ID'];
const options = { localeMatcher: 'lookup' };
console.log(Intl.ListFormat.supportedLocalesOf(locales, options));
// ["id-u-co-pinyin", "de-ID"]
六、复数格式: Intl.PluralRules
Intl.PluralRules
是一个构造函数, 实例对象提供一系列的接口, 可根据设置的语言区域进行一些列复数操作
- 语法:
new Intl.PluralRules([locales[, options]])
- 可选参数
locales
说明: 用于设置国际化语言类型, 是一个 BCP 47 语言标记 的字符串, 或者是一个包括多个语言标记的数组
- 可选参数
options
说明: 可选配置, 配置有点多这里就不一一展开, 具体可查阅 MDN-PluralRules
- 实例方法 Intl.PluralRules.prototype.select() 根据给定的语言区域, 对给定的数值格式化为复数形式
js
const pr = new Intl.PluralRules('en')
pr.select(0); // 'zero'
pr.select(1); // 'one'
pr.select(2); // 'two'
pr.select(6); // 'few'
pr.select(18); // 'many'
- 实例方法 Intl.PluralRules.prototype.selectRange() 根据给定的语言区域, 对给定的数值范围将其格式化为复数形式
js
const pr = new Intl.PluralRules('en')
new Intl.PluralRules("sl").selectRange(102, 201); // 'few'
new Intl.PluralRules("pt").selectRange(102, 102); // 'other'
- 实例方法 Intl.PluralRules.prototype.resolvedOptions() 用于获取当前实例对象的配置信息
js
const pr = new Intl.PluralRules('en')
pr.resolvedOptions()
- 静态方法 Intl.PluralRules.supportedLocalesOf() 方法返回一个数组, 该方法从提供的语言区域列表中, 筛选出符合规则或者说支持的语言列表
js
const locales = ['ban', 'id-u-co-pinyin', 'de-ID'];
const options = { localeMatcher: 'lookup' };
console.log(Intl.PluralRules.supportedLocalesOf(locales, options));
// ["id-u-co-pinyin", "de-ID"]
七、文本分割: Intl.Segmenter
Intl.Segmenter
是一个构造函数, 实例对象提供一系列的接口, 可根据设置的语言区域对字符串进行分割
- 语法:
new Intl.Segmenter([locales[, options]])
- 可选参数
locales
说明: 用于设置国际化语言类型, 是一个 BCP 47 语言标记 的字符串, 或者是一个包括多个语言标记的数组
- 可选参数
options
说明: 可选配置, 配置有点多这里就不一一展开, 具体可查阅 MDN-Segmenter
- 实例方法 Intl.Segmenter.prototype.segment() 根据给定的语言区域, 对给定的字符串进行分割; 需要注意的是这里返回的是可迭代对象
js
const segmenterFr = new Intl.Segmenter('fr', { granularity: 'word' });
const res = segmenterFr.segment('Que ma joie demeure')
console.log([...res])
- 实例方法 Intl.Segmenter.prototype.resolvedOptions() 用于获取当前实例对象的配置信息
js
const segmenterFr = new Intl.Segmenter('fr')
segmenterFr.resolvedOptions()
- 静态方法 Intl.Segmenter.supportedLocalesOf() 方法返回一个数组, 该方法从提供的语言区域列表中, 筛选出符合规则或者说支持的语言列表
js
const locales = ['ban', 'id-u-co-pinyin', 'de-ID'];
const options = { localeMatcher: 'lookup' };
console.log(Intl.Segmenter.supportedLocalesOf(locales, options));
// ["id-u-co-pinyin", "de-ID"]
- 和使用
String.prototype.split(' ')
有何区别
如果字符串对应语言是使用空格来分隔单词的, 那么从结束上来看是一样的; 但是如果是其他语言(不是使用空格分个单词)那么使用它们得到结果就不一定是一致的咯, 例如: 日语、中文、泰语、老挝语、高棉语、缅甸语等
js
const str = "吾輩は猫である。名前はたぬき。";
console.log(str.split(' '))
const segmenterJa = new Intl.Segmenter("ja-JP", { granularity: "word" });
const segments = segmenterJa.segment(str);
console.table([...segments]);