查看原文
其他

【第1032期】前端国际化

sundway 前端早读课 2019-06-25

前言

第一次了解到这个概念,之前在QQ群也有人聊前端国际化如何做的问题,不知道这篇对他有帮到吗?今日早读文章来自阿里@sundway分享。

正文从这开始~

何为国际化

国际化(Internationalization)通常在很多地方会用 i18n 代替,意思就是 I 加18个字母加 n。跟国际化还有一个类似的概念叫做本地化(Localization)通常用 L10n 表示。这是两个比较接近的概念,它之间有什么区别呢?W3C 的 Localization vs. Internationalization 这篇文档详细了介绍了这一点,对国际化理解还不是特别清晰的强烈建议读一下这篇文章。简单的理解就是,国际化就是为本地化做很多的前期工作,可以根据所做的事情是否属于只是为某一地区来区分国际化和本地化。国际化包含的东西很多,对于前端,最常接触到的就是将可本地化的元素与源代码或内容分开,以便可以根据需要根据用户的国际偏好来加载或选择本地化的替代。但国际化的工作远不止这些,例如国际化CDN 部署,Unicode 编码都可以算作国际化的范畴。

国际化 API

其实在 2012 年就已经拟定了国际化 API,详尽的内容可以参考 《ECMAScript Internationalization API Specification》。下面重点介绍几个 API 的使用:

Intl.Collator

Intl.Collator 是用于语言敏感字符串比较的 collators构造函数。语法:

new Intl.Collator([locales[, options]])
Intl.Collator.call(this[, locales[, options]])

上述语法中,locales 是可选参数,locales 的参数必须遵从 BCP 47 规范,locales 标记必须是 "en-US" 和 "zh-Hans-CN 等,这个标记包含了语言、地区和国家。完整的列表可以查看 IANA language subtag registry。options 也是可选参数,它包含了特定比较选项的对象。

示例:

var co1 = new Intl.Collator(["de-DE-u-co-phonebk"]);  
var co2 = new Intl.Collator(["de-DE"]);  
var co3 = new Intl.Collator(["en-US"]);  

var arr = ["ä", "ad", "af", "a"];  

if (console && console.log) {  
   console.log(arr.sort(co1.compare));  // Returns a,ad,ä,af  
   console.log(arr.sort(co2.compare));  // Returns a,ä,ad,af  
   console.log(arr.sort(co3.compare));  // Returns a,ä,ad,af  
}  

Intl.DateTimeFormat

Intl.DateTimeFormat是根据语言来格式化日期和时间的类的构造器类。

new Intl.DateTimeFormat([locales[, options]])
Intl.DateTimeFormat.call(this[, locales[, options]])

Intl.DateTimeFormat是根据语言来格式化日期和时间的类的构造器类。可以使用 options 参数来自定义 日期时间格式化方法返回的字符串。

var date = new Date(Date.UTC(2012, 11, 20, 3, 0, 0));

// 下面是假定的所在时区
// 洛杉矶(America/Los_Angeles for the US)

// 美式英语(US English) 使用  month-day-year 格式
console.log(new Intl.DateTimeFormat('en-US').format(date));
// → "12/19/2012"

// 英式英语(British English) 使用 day-month-year 格式
console.log(new Intl.DateTimeFormat('en-GB').format(date));
// → "20/12/2012"

// 韩国使用 year-month-day 格式
console.log(new Intl.DateTimeFormat('ko-KR').format(date));
// → "2012. 12. 20."

//大部分阿拉伯国家使用阿拉伯字母(real Arabic digits)
console.log(new Intl.DateTimeFormat('ar-EG').format(date));
// → "٢٠‏/١٢‏/٢٠١٢"

//在日本,应用可能想要使用日本日历,
//2012 是平成24年(平成是是日本天皇明仁的年号,由1989年1月8日起开始计算直至现在)
console.log(new Intl.DateTimeFormat('ja-JP-u-ca-japanese').format(date));
// → "平成24/12/20"

//当请求一个语言可能不支持,如巴厘(ban),若有备用的语言印尼语(id),
//那么将使用印尼语(id)
console.log(new Intl.DateTimeFormat(['ban', 'id']).format(date));
// → "20/12/2012"

Intl.NumberFormat

Intl.NumberFormat是对语言敏感的格式化数字类的构造器类。

new Intl.NumberFormat([locales[, options]])
Intl.NumberFormat.call(this[, locales[, options]])

示例:

var number = 123456.789;

// 德语使用逗号作为小数点,使用.作为千位分隔符
console.log(new Intl.NumberFormat('de-DE').format(number));
// → 123.456,789

// 大多数阿拉伯语国家使用阿拉伯语数字
console.log(new Intl.NumberFormat('ar-EG').format(number));
// → ١٢٣٤٥٦٫٧٨٩

// India uses thousands/lakh/crore separators
console.log(new Intl.NumberFormat('en-IN').format(number));
// → 1,23,456.789

// 通过编号系统中的nu扩展键请求, 例如中文十进制数字
console.log(new Intl.NumberFormat('zh-Hans-CN-u-nu-hanidec').format(number));
// → 一二三,四五六.七八九

//当请求的语言不被支持,例如巴里,包含一个回滚语言印尼,这时候就会使用印尼语
console.log(new Intl.NumberFormat(['ban', 'id']).format(number));
// → 123.456,789

这个例子显示了一些本地化的数字格式的一些变化。为了获得用于您的应用程序的用户界面的语言格式,请确保设定了语言(可能还有一些回退语言)参数。

以下各浏览器的支持情况:


可以看到个浏览器对 Intl API 的支持已经相当不错了。对于一些不兼容的浏览器,我们可以引入 Intl polyfill。

生产环境中的应用

在生产环境中我们一般会引用第三方库,不同的技术方案引入的库也会不同。以下是几个主流的库/框架的解决方案:

  • vue + vue-i18n

  • angular + angular-translate

  • react + react-intl

  • jquery + jquery.i18n.property

下面以 react 为例,这里建议直接可以使用阿里开源的 react-intl-universal,相比于 Yahoo react-intl API,它不仅支持 react 组件,同时也支持原生 js。

import intl from 'react-intl-universal';

// locale data
const locales = {
 "en-US": require('./locales/en-US.js'),
 "zh-CN": require('./locales/zh-CN.js'),
};

class App extends Component {

 state = {initDone: false}

 componentDidMount() {
   this.loadLocales();
 }

 loadLocales() {
   // init method will load CLDR locale data according to currentLocale
   // react-intl-universal is singleton, so you should init it only once in your app
   intl.init({
     currentLocale: 'en-US', // TODO: determine locale here
     locales,
   })
   .then(() => {
     // After loading CLDR locale data, start to render
 this.setState({initDone: true});
   });
 }

 render() {
   return (
     this.state.initDone &&
     <div>
       {intl.get('SIMPLE')}
     </div>
   );
 }

}

当然有些项目没有使用上面任何一个库/框架,例如阿里小蜜。也有相应地方案去解决。主要思路:


  • 通过构建工具去完成样式, 图片替换, class属性等的替换工作。

  • 通用的翻译函数去完成静态文案及动态文案的翻译工作。


阿里小蜜中使用的是 nunjucks 模版引擎,那么这里可以使用 nunjucks-intl。如果是其他模版工具,也可以自己在 polyfill 上写一个简单的实现。


最后,推荐一下 Yahoo 的 FormatJS,官方给出的解释是一系列 JavaScript 库的集合,主要用来格式化数字,日期和字符串。它包含一系列核心代码,而这些代码建立在 JS 原生 Intl 以及各种 i18n 标准之上的。


关于本文

作者:@sundway

原文:https://github.com/sundway/blog/issues/9

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存