ECMA委员会在ES5.1时期公布了ECMAScript国际化1.0,然后也在ES2015时期公布了ECMAScript国际化2.0。ECMAScript总算有了一套国际化方案。虽然这套国际化方案更倾向于各种语言内容的格式化与比较/排序,不像Java的国际化那样可以自定义语言包,但基本上已经覆盖了前端编程所涵盖的多数情况。
新内置对象 Intl
所有国际化功能都被包含在这个对象里面。已经被规范化了的功能主要有Collator
、NumberFormat
和DateTimeFormat
。
Intl.getCanonicalLocales() 静态方法
返回所有规范区域名称,而区域名称遵循BCP 47规范。
'use strict';
alert(JSON.stringify(Intl.getCanonicalLocales('ZH-cn'))); // ['zh-CN']
alert(JSON.stringify(Intl.getCanonicalLocales(['zH-cN', 'Zh-Tw']))); // ['zh-CN', 'zh-TW']
try {
Intl.getCanonicalLocales('ZH_CN'); // 下划线是不在区域名规范内的
} catch (e) {
alert(e);
}
// 区域名有组合规则
alert(JSON.stringify(Intl.getCanonicalLocales('zh'))); // 它可以单独由区域代码组成
alert(JSON.stringify(Intl.getCanonicalLocales('zh-CN'))); // 因为每个区域都有默认语言
alert(JSON.stringify(Intl.getCanonicalLocales('zh-HK'))); // 也可以在区域代码最后附上其它语言
alert(JSON.stringify(Intl.getCanonicalLocales('zh-Hant-HK'))); // 也可以在区域代码中间附上其它字体
// 区域名后可以添加“-u-xx-xxxx”Unicode扩展
// -u-co-xxxx 扩展用于 Intl.Collator
alert(JSON.stringify(Intl.getCanonicalLocales('de-DE-u-co-phonebk'))); // 德国/德语/电话本顺序
// -u-nu-xxxx 扩展用于 Intl.NumberFormat
alert(JSON.stringify(Intl.getCanonicalLocales('th-TH-u-nu-thai'))); // 泰国/泰语/泰国数字顺序
// -u-ca-xxxx 扩展用于 Intl.DateTimeFormat
alert(JSON.stringify(Intl.getCanonicalLocales('en-GB-u-ca-islamic'))); // 英国/英语/伊斯兰历顺序
不过要注意,按照区域代码获取格式支持的时候未必会生效,例如我的浏览器没安装日语包就没办法获取日语格式的支持。
Intl.Collator 整理器
整理器用于排序和搜索,构造函数的第二个参数可以覆盖第一个参数的配置:
'use strict';
// 按拼音排序
const c1 = new Intl.Collator('zh-CN-u-co-pinyin'),
// 如果我们确切知道区域代码,就用不着默认的 localeMatcher: 'best fit' 逻辑
_c1 = new Intl.Collator('zh-CN-u-co-pinyin', { localeMatcher: 'lookup' });
const a = ['二', 'a', '一', 'c', '三', 1, 2, 3, 10, 'A', 'B'];
a.sort(c1.compare);
alert(JSON.stringify(a));
alert(JSON.stringify(c1.resolvedOptions()) === JSON.stringify(_c1.resolvedOptions()));
// 按拼音排序之余还按数字排序
const c2 = new Intl.Collator('zh-CN-u-co-pinyin-kn-true'),
_c2 = new Intl.Collator('zh-CN-u-co-pinyin', { numeric: true }); // 等价
a.sort(c2.compare);
alert(JSON.stringify(a));
alert(JSON.stringify(c2.resolvedOptions()) === JSON.stringify(_c2.resolvedOptions()));
// 按拼音、数字排序之余还以大写字母排前面
const c3 = new Intl.Collator('zh-CN-u-co-pinyin-kn-true-kf-upper'),
_c3 = new Intl.Collator('zh-CN-u-co-pinyin', { numeric: true, caseFirst: 'upper' }); // 等价
a.sort(c3.compare);
alert(JSON.stringify(a));
alert(JSON.stringify(c3.resolvedOptions()) === JSON.stringify(_c3.resolvedOptions()));
// 默认的用途都是排序,即 usage: 'sort'
// 但也可以使用搜索语言,即 usage: 'search'
const b = ['congrès', 'Congres', 'Assemblée', 'poisson'];
const c4 = new Intl.Collator('fr', { usage: 'search', sensitivity: 'base' }), // 不区分大小写与注音
c5 = new Intl.Collator('fr', { usage: 'sort', sensitivity: 'base' });
// 但老实说我没搞明白它们用起来有什么不同!😂
alert(b.filter(v => c4.compare(v, 'congres') === 0).join(', '));
alert(b.filter(v => c5.compare(v, 'congres') === 0).join(', '));
b.sort(c4.compare);
alert(JSON.stringify(b));
b.sort(c5.compare);
alert(JSON.stringify(b));
// 还可以忽略标点符号
const c = ['一', ',', '二', '。'];
const c6 = new Intl.Collator('zh-CN', { ignorePunctuation: true }),
c7 = new Intl.Collator('zh-CN');
c.sort(c6.compare);
alert(JSON.stringify(c));
c.sort(c7.compare);
alert(JSON.stringify(c));
// 还是没看到有什么不同!😂
// 这个方法可以获取支持的区域代码
alert(JSON.stringify(Intl.Collator.supportedLocalesOf(['zh-CN-u-co-pinyin', 'xxxxx'], { localeMatcher: 'lookup' })));
另外,字符串也新增了一个localeCompare()
方法,它的参数与行为与Intl.Collator
一致:
'use strict';
alert('congrès'.localeCompare('congres', 'fr', { sensitivity: 'base' }));
alert('Congres'.localeCompare('congres', 'fr', { sensitivity: 'base' }));
Intl.NumberFormat 数字格式
数字格式的format
方法只接受数字和能够隐式类型转换为数字的值:
'use strict';
const a = [1, '2', 10, '15', '叁', '五'];
// 将数字格式化为中文
const n1 = new Intl.NumberFormat('zh-u-nu-hanidec');
alert(a.map(n => n1.format(n)).join(', '));
// 百分比的格式
const n2 = new Intl.NumberFormat('zh', { style: 'percent' });
alert(n2.format(0.5));
// 货币的格式
const n3 = new Intl.NumberFormat('zh', {
style: 'currency',
currency: 'CNY', // 货币格式一定要提供货币代号
currencyDisplay: 'name', // 默认值是symbol、即显示¥,也可以选择code、即显示CNY
useGrouping: false // 默认为true,即默认都会对数字进行分组
});
alert(n3.format(123456.789) + '\n' +
// 这个方法有利于在格式化数字中再嵌入例如HTML之类的字符串
JSON.stringify(n3.formatToParts(123456.789), null, 4));
// 可以控制保留多少位小数
const n4 = new Intl.NumberFormat('zh', {
minimumFractionDigits: 2,
maximumFractionDigits: 4
});
alert(`${n4.format(1)}
${n4.format(1.23456)}`);
// 也可以控制保留多少位整数
const n5 = new Intl.NumberFormat('zh', { minimumIntegerDigits: 10 });
alert(n5.format(1));
// 也可以控制保留多少位有效数字
const n6 = new Intl.NumberFormat('zh', {
minimumSignificantDigits: 3,
maximumSignificantDigits: 4
});
alert(`${n6.format(1.2)}
${n6.format(123.456)}`);
// 与Intl.Collator一样,都有这两个方法
alert(JSON.stringify(n6.resolvedOptions(), null, 4));
alert(JSON.stringify(Intl.NumberFormat.supportedLocalesOf(['zh-u-nu-hanidec', 'xxxxx'], { localeMatcher: 'lookup' })));
另外,数字也新增了一个toLocaleString()
方法,它的参数与行为与Intl.NumberFormat
一致:
'use strict';
alert(Number(2019).toLocaleString('zh-u-nu-hanidec', { useGrouping: false }));
Intl.DateTimeFormat 日期时间格式
日期时间格式的format
方法只接受Date
实例:
'use strict';
// 将日期格式化为中国农历
function formatDateToTraditionalChineseCalendar(d) {
const 天干 = '甲乙丙丁戊己庚辛壬癸', 地支 = '子丑寅卯辰巳午未申酉戌亥';
const dFmt = new Intl.DateTimeFormat('zh-u-ca-chinese'),
nFmt = new Intl.NumberFormat('zh-u-nu-hanidec');
const a = dFmt.formatToParts(d),
year = parseInt(a[0].value) - 1,
month = parseInt(a[2].value),
day = parseInt(a[4].value);
function fmtN(n) {
if (n < 10) return nFmt.format(n);
else if (n == 10) return '十';
else if (n < 20) return '十' + nFmt.format(n % 10);
else if (n == 20) return '二十';
else if (n < 30) return '廿' + nFmt.format(n % 10);
else if (n == 30) return '三十';
else return nFmt.format(n);
}
return 天干[year % 天干.length] + 地支[year % 地支.length] + '年' +
fmtN(month) + '月' +
(day
另外,Date
实例也新增了toLocaleString()
、toLocaleDateString()
、toLocaleTimeString()
方法,它们的参数与行为与Intl.DateTimeFormat
一致:
'use strict';
alert((new Date()).toLocaleString('zh', { hour12: false }));
alert((new Date()).toLocaleDateString('zh'));
alert((new Date()).toLocaleTimeString('zh'));
全局国际化支持
Object.prototype.toLocaleString()
ECMA委员会给所有对象都添加了toLocaleString()
方法、即任何对象都可以通过覆盖这个方法提供国际化支持,默认的情况下它的返回值与toString()
一样。
'use strict';
alert(({}).toLocaleString('zh'));
Array.prototype.toLocaleString()
ECMA委员会同时也给数组添加了toLocaleString()
方法,它将会调用数组元素的各个toLocaleString()
方法来显示各元素。
'use strict';
class C {
#c = 15;
toString() {
return `C(${this.#c})`;
}
toLocaleString(code, options) {
return `C(${this.#c.toLocaleString(code, options)})`;
}
}
const a = [10, new Date(), new C()];
alert(a.toLocaleString('zh-u-nu-hanidec'));
后续
ES的国际化后续还有好几个API,但是都尚未正式发布,浏览器几乎都不支持,所以介绍到这里就算了。
打赏作者