为什么需要 i18n?
英语是世界上使用最广泛的语言,但只有七分之一的人会说英语。它是 3.79 亿人的第一(母语)语言,但有 9.17 亿人说中文普通话,4.6 亿人说西班牙语,3.41 亿人说印地语。
在互联网呈指数级增长的新兴市场,存在大量非英语用户。如果你的网站可以在全球范围内进行翻译,那么你的潜在目标市场可能会增加 700%!
i18n 全称 Internationalization,也就是国际化的意思,因为单词太长,所以中间的 18 个字母被缩写为 18,再加上开头和结尾的字母,就组成了 i18n。
JavaScript i18n API 可以帮助我们对网站进行多语言翻译,让它们可以轻松适应使用不同语言用户的需求。
在本文中,我将介绍 i18n API 提供的各种方法,以及如何在实际项目中实现 i18n 来覆盖更广泛、更国际化的用户。
i18n 其实很难
从上面的描述中,i18n 看起来很容易,但是当你尝试去做的时候,又发现它并不简单。
基于拉丁语的语言可能表面上相似。比如,请求姓名、电子邮件和日期的表单翻译如下:
英语 | name | date | |
西班牙语 | nombre | fecha | |
法语 | nom | date | |
德语 | name | datum |
Gettext 是一种在类 Unix 计算机操作系统上实现国际化和本地化程序的系统,它已经存在了几十年,而且这个库可以用在大多数编程语言中,在 nodejs 也可用。
在最简单的场景下,我们可以使用某种形式的标记来实现它。比如下面这段 HTML 模板:
<label for="name">{{ NAME }}</label>
当用户将英语设置为主要语言时,NAME 会被动态替换。
但是只是最简单的案例,实际情况中会有很多问题:
- 同一种语言可以有不同的变体。在西班牙使用的西班牙语与在南美洲使用的西班牙语不同。
- 一种语言中的单词在其他语言中可能会更长。例如,“电子邮件”在俄语中翻译为“электронное письмо”。
- 文本并不总是从左到右。有些语言是从右向左书写的,例如阿拉伯语、希伯来语、库尔德语和意第绪语。也有一些语言是可以从上到下书写的,比如中文、韩文、日文和闽南文。
更糟糕的问题
除了上面提到的问题,还有更糟糕的情况。
当我们需要显示日期、时间、数字、货币或单位时,会出现进一步的复杂问题。
在英文中显示其日,通常是 12/03/24 这种格式。但是在其他语言中:
- 使用 MDY 格式的美国居民会使用 3 December 2024。
- 使用 DMY 格式的欧洲、南美和亚洲居民会使用 12 March 2024
- 加拿大、中国、日本和匈牙利居民会使用 2012 年 3 月 24 日,他们选择了实用得多的 YMD 格式。
英文中的数字 1,000,在其他语言中:
- 美国、英国、加拿大、中国和日本会以千为单位,表示为一千。
- 西班牙、法国、德国和俄罗斯会表示为一个零点,其中数字的小数部分用逗号分隔。
JavaScript Intl API
其实很多人不知道,在 JavaScript 中存在 Intl 对象。
在大多数现代浏览器和运行时中都实现了 ECMAScript 国际化 API,并且兼容性还不错。甚至在 IE11 中也有很多比较有用的方法。
对于较旧的浏览器,还有一个 polyfill 可以用。
Intl API 有点不寻常。它为日期、时间、数字和列表分别提供了几个构造函数,它们会接收一个语言环境和一个包含配置参数的可选对象。
比如,指定美国英语的 DateTime 对象:
const dateFormatter = new Intl.DateTimeFormat('en-US');
这个对象可以被多次调用,传递一个 Date 实例,或者是一个 ES6 Temporal,如果它被支持的话。
format 是最常用也是最实用的方法。它的用法如下:
const valentinesDay = dateFormatter.format(new Date('2022-02-14')); // "2/14/2022" const starwarsDay = dateFormatter.format(new Date('2022-05-04')); // "5/4/2022"
你也可以这么用:
const starwarsDay = new Intl .DateTimeFormat('en-US') .format(new Date('2022-05-04'));
除了 format() 方法之外,某些对象还支持这些:
- formatToParts():返回一个包含格式化字符串的对象数组,例如 { type: 'weekday', value: 'Monday' }
- resolvedOptions(): 返回一个新对象,其属性反映所使用的语言环境和格式选项,例如 dateFormatter.resolvedOptions().locale。
定义语言环境
所有 Intl 对象都需要一个语言环境参数。它是一个字符串,它可以表示以下含义:
- 语言子标签
- 脚本子标签(可选)
- 地区(或国家)子标签(可选)
- 一个或多个变体子标签(可选)
- 一个或多个 BCP 47 扩展序列(可选)
- 私人使用的扩展序列(可选)
通常来说,只需要指定语言和地区就足够了。例如,"en-US"、"fr-FR"等。
除了使用字符串外,Intl.locale 对象还可用于构造语言环境,例如具有 12 小时时间格式的美国英语:
const us = new Intl.Locale('en', { region: 'US', hourCycle: 'h12', calendar: 'gregory' });
这也可以在另一个 Intl 构造函数中使用。
new Intl .DateTimeFormat(us, { timeStyle: 'medium' }) .format( new Date('2022-05-04T13:00:00') ); // "1:00:00 PM"
如果没定义区域设置,则使用设备的当前语言和区域设置。
new Intl .DateTimeFormat() .format( new Date('2022-05-04') );
I18n 的 API 其实比较多,在这里就不再多赘述,更加详细的内容可以参考 MDN 文档。
我会在下一篇文章中详细聊聊更详细的业界解决方案。
下一篇:前端 i18n 最佳实践:在 React 中使用 i18next