JS魔法堂:不完全国际化&本地化手册 之 实战篇
前言
最近加入到新项目组负责前端技术预研和选型,其中涉及到一个熟悉又陌生的需求——国际化&本地化。熟悉的是之前的项目也玩过,陌生的是之前的实现仅仅停留在"有"的阶段而已。趁着这个机会好好学习整理一下,为后面的技术选型做准备。
本篇将于大家一起挽起袖子撸代码:)
如何获取Language tag?
在实现本地化处理前,我们起码先要获取Language tag吧?那么获取方式分为两类
1.直接获取浏览器的Language tag信息
一般来说浏览器语言的版本标示着用户所属或所期待接收哪种语言文化风俗的内容,于是通过以下函数获取浏览器的语言信息即可获取language-tag
function getLang(){
return navigator.language || navigator.browserLanguage
}
2.用户选择Language tag信息
大家在浏览苹果官网时也会发现以下界面,让我们自行选择language-tag。

注意苹果官网采用的是Server-driven Negotiation的机制提供本地化功能,和本篇主打前端实现有所区别。
最适当的设置和获取language-tag的方式当然就是上述两种方式相结合啦!首先自动获取浏览器的Language tag信息,并提供入口让用户自行选择Language tag信息。
认识JavaScript Internationalization API
有了本地化识别的根据(language tag)后,我们就可以开始实现本地化处理了,但从头开始处理还累了,幸好H5为我们提供新的API来减轻我们的工作量。它们分别是处理排序的Intl.Collator,处理日期格式化的Intl.DateTimeFormat和处理数字/货币等格式化的Intl.NumberFormat。
Intl.Collator
用于字符排序.
new Intl.Collator([locales[, options]])
@param Array|String [locales] - language-tag字符串或数组
@param Array [options] - 配置项
options的属性及属性值(如无特别说明则values第一个值为默认值)
@prop String localeMatcher
@desc 指定用于locale匹配的算法
@values 'best fit' | 'lookup'
@prop String usage
@desc 指定用途
@values 'sort' | 'search'
@prop String sensitivity
@desc 指定对比时是否忽略大小写、读音符号
@values 'base' - 大小写不敏感,读音符号不敏感
'accent' - 除采用base规则外,读音符号敏感
'case' - 除采用base规则外,大小写敏感
'variant' - base,accent和case的并集
@prop Boolean ignorePunctuation
@desc 指定是否忽略标点符号
@values false | true
@prop Boolean numeric
@desc 指定是否将两个数字字符转换为数字类型再作比较
@values false | true
@prop String caseFirst
@desc 指定是否以大写或小写作优先排序
@values 'false' | 'upper' | 'lower'
实例方法
Intl.Collator.prototype.compare(a, b):Number
@desc 比较字符串a和字符串b,若a排在b前面则返回-1,等于则返回0,排在后面则返回1.
Intl.Collator.prototype.resolveOptions():Object
@desc 返回根据构造函数中options入参生成的最终采用的options
Intl.DateTimeFormat
用于日期格式化输出.
new Intl.DateTimeFormat([locales[, options]])
@param Array|String [locales] - language-tag字符串或数组
@param Array [options] - 配置项
options的属性及属性值(如无特别说明则values第一个值为默认值)
@prop String localeMatcher
@desc 指定用于locale匹配的算法
@values 'best fit' | 'lookup'
@prop String timeZone
@desc 指定被格式化的时间所在的时区
@values [IANA time zone database](https://www.iana.org/time-zones) such as "Asia/Shanghai", "Asia/Kolkata", "America /New_York", "UTC"
@prop String timeZoneName
@desc 指定格式化后所显示的时区样式
@values 'short' | 'long'
@prop Boolean hour12
@desc 指定采用12小时制还是24小时制
@values false | true
@default-value 由locales入参决定
@prop String year
@desc 指定年份的样式
@values 'numeric' | '2-digit' | 'narrow' | 'short' | 'long'
@prop String month
@desc 指定月份的样式
@values 'numeric' | '2-digit' | 'narrow' | 'short' | 'long'
@prop String day
@desc 指定日期的样式
@values 'numeric' | '2-digit'
@prop String hour
@desc 指定小时的样式
@values undefined | 'numeric' | '2-digit'
@prop String minute
@desc 指定分钟的样式
@values undefined | 'numeric' | '2-digit'
@prop String second
@desc 指定秒的样式
@values undefined | 'numeric' | '2-digit'
@prop String weekday
@desc 指定周的样式
@values 'narrow' | 'short' | 'long'
实例方法
Intl.Collator.prototype.format(a):String
@desc 格式化日期
Intl.DateTimeFormat.prototype.resolveOptions():Object
@desc 返回根据构造函数中options入参生成的最终采用的options
Intl.NumberFormat
用于数字、货币格式化输出.
new Intl.NumberFormat([locales[, options]])
@param Array|String [locales] - language-tag字符串或数组
@param Array [options] - 配置项
options的属性及属性值(如无特别说明则values第一个值为默认值)
@prop String localeMatcher
@desc 指定用于locale匹配的算法
@values 'best fit' | 'lookup'
@prop String style
@desc 指定格式化的风格
@values 'decimal' | 'currency' | 'percent'
@remark 当style设置为currency后,属性currency必须设置
@prop String currency
@desc 指定货币的格式化信息
@values 如"USD"表示美元, "EUR"表示欧元, "CNY"表示RMB.[Current currency & funds code first](http://www.currency-iso.org/en/home/tables/table-a1.html)
@prop String currencyDisplay
@desc 指定货币符号的样式
@values 'symbol' | 'code' | 'name'
@prop Boolean useGrouping
@desc 指定是否采用如千位分隔符对数字进行分组
@values false | true
@prop Number minimumIntegerDigits
@desc 指定整数最小位数
@values 1-21
@prop Number maximumFractionDigits
@desc 指定小数部分最大位数
@values 0-20
@prop Number minimumFractionDigits
@desc 指定小数部分最小位数
@values 0-20
@prop Number maximumSignificantDigits
@desc 指定有效位最大位数
@values 1-21
@prop Number minimumSignificantDigits
@desc 指定有效为最小位数
@values 1-21
注意:minimumIntegerDigits,maximumFractionDigits和minimumFractionDigits为一组,而maximumSignificantDigits和minimumSignificantDigits为另一组,当设置maximumSignificantDigits后,minimumIntegerDigits这组的设置为全部失效。
上述Intl接口并不是所有浏览器均支持,幸好有大牛已为了我们准备好polyfill了,但由于Intl.Collator所以来的规则和实现的代码量较庞大,因此polyfill中仅仅实现了Intl.DateTimeFormat和Intl.NumberFormat接口而已,不过对于一般项目而言应该足矣。Intl polyfill
另外,还对String,Number和Date的原型作扩展,以便我们使用Intl的三剑客!
String.prototype.localeCompare(compareString[, locales[, options]])
Number.prototype.toLocaleString([locales[, options]])
Date.prototype.toLocaleString([locales[, options]])
Date.prototype.toLocaleDateString([locales[, options]])
Date.prototype.toLocaleTimeString(([locales[, options]])
Format.js——用在生产环境的i18n库
说了这么多那我们怎么让项目实现国际化/本地化呢?那当然要找个可靠的第三方库啦——Format.js,它不仅提供字符串替换还提供日期、数字和货币格式化输出的功能,而且各大前端框架都已将其作二次封装,使用得心应手呢!

要注意的是它依赖Intl.NumberFormat和Intl.DateTimeFormat,因此当浏览器部支持时需要polyfill一下。
var areIntlLocalesSupported = require('intl-locales-supported');
var localesMyAppSupports = [
/* list locales here */
];
if (global.Intl) {
// Determine if the built-in `Intl` has the locale data we need.
if (!areIntlLocalesSupported(localesMyAppSupports)) {
// `Intl` exists, but it doesn't have the data we need, so load the
// polyfill and replace the constructors with need with the polyfill's.
var IntlPolyfill = require('intl');
Intl.NumberFormat = IntlPolyfill.NumberFormat;
Intl.DateTimeFormat = IntlPolyfill.DateTimeFormat;
}
} else {
// No `Intl`, so use and load the polyfill.
global.Intl = require('intl');
}
intl-locales-supported顾名思义就是检查原生Intl是否支持特定的Language tag(如cmn-Hans),若不支持则使用Polyfill版本。
原生JavaScript使用示例:
<div id="msg"></div>
<script>
const msgs = {en: {GREETING: 'Hello {name}'}
,fr: {GREETING: 'Bonjour {name}'}}
const locale = getLang()
const msg = (msgs[locale] || msgs.en).GREETING
const txt = new IntlMessageFormat(msg, locale)
document.getElementById('msg').textContent = txt.format({name: 'fsjohnhuang'})
</script>
Polymer组件使用示例:
<link rel="import" href="./bower_components/app-localize-behavior/app-localize-behavior.html">
<dom-module id="x-demo">
<template>
<div>{{localize('name')}}</div>
</template>
<script>
Polymer({
is: 'x-demo',
properties: {name: {type: String, value: 'fsjohnhuang'}},
behaviors: [Polymer.AppLocalizeBehavior],
attached: function(){
this.loadResources(this.resolveUrl('./locales.json'))
}
})
</script>
</dom-module>
locales.json
{"en": {"GREETING": "Hello {name}"}
,"fr": {"GREETING": "Bonjour {name}"}}
更多的玩法请参考官网吧!
总结
项目中我们更多地是采用如Formatjs等上层i18n库,而不是更底层的IntlAPI,但若想更好地实现国际化和本地化,我想了解Intl及其背后的规则是十分有必要的。
另外细心的你会发现上文提到另一个概念——Server-driven Negotiation,到底它和国际化/本地化有什么关系呢?那么请期待下篇——《JS魔法堂:不完全国际化&本地化手册 之 拓展篇》
尊重原创,转载请注明来自: http://www.cnblogs.com/fsjohnhuang/p/5911482.html _肥仔John
感谢
JS魔法堂:不完全国际化&本地化手册 之 实战篇的更多相关文章
- JS魔法堂:不完全国际化&本地化手册 之 拓展篇
前言 最近加入到新项目组负责前端技术预研和选型,其中涉及到一个熟悉又陌生的需求--国际化&本地化.熟悉的是之前的项目也玩过,陌生的是之前的实现仅仅停留在"有"的阶段而已. ...
- JS魔法堂:不完全国际化&本地化手册 之 理論篇
前言 最近加入到新项目组负责前端技术预研和选型,其中涉及到一个熟悉又陌生的需求--国际化&本地化.熟悉的是之前的项目也玩过,陌生的是之前的实现仅仅停留在"有"的阶段而已. ...
- JS魔法堂:判断节点位置关系
一.前言 在polyfill querySelectorAll 和写弹出窗时都需要判断两个节点间的位置关系,通过jQuery我们可以轻松搞定,但原生JS呢?下面我将整理各种判断方法,以供日后查阅. 二 ...
- JS魔法堂:LINK元素深入详解
一.前言 我们一般使用方式为 <link type="text/css" rel="stylesheet" href="text.css&quo ...
- JS魔法堂:IMG元素加载行为详解
一.前言 在<JS魔法堂:jsDeferred源码剖析>中我们了解到img元素加载失败可以作为函数异步执行的优化方案,本文打算对img元素的加载行为进行更深入的探讨. 二.资源加载的相关属 ...
- JS魔法堂:jsDeferred源码剖析
一.前言 最近在研究Promises/A+规范及实现,而Promise/A+规范的制定则很大程度地参考了由日本geek cho45发起的jsDeferred项目(<JavaScript框架设计& ...
- JS魔法堂:属性、特性,傻傻分不清楚
一.前言 或许你和我一样都曾经被下面的代码所困扰 var el = document.getElementById('dummy'); el.hello = "test"; con ...
- JS魔法堂:那些困扰你的DOM集合类型
一.前言 大家先看看下面的js,猜猜结果会怎样吧! 可选答案: ①. 获取id属性值为id的节点元素 ②. 抛namedItem is undefined的异常 var nodes = documen ...
- JS魔法堂:doctype我们应该了解的基础知识
一.前言 什么是doctype?其实我们一直使用,却很少停下来看清楚它到底是什么,对网页有什么作用.本篇将和大家一起探讨那个默默无闻的doctype吧! 二.什么是doctype doctype或DT ...
随机推荐
- C#4.0泛型的协变,逆变深入剖析
C#4.0中有一个新特性:协变与逆变.可能很多人在开发过程中不常用到,但是深入的了解他们,肯定是有好处的. 协变和逆变体现在泛型的接口和委托上面,也就是对泛型参数的声明,可以声明为协变,或者逆变.什么 ...
- 对Castle Windsor的Resolve方法的解析时new对象的探讨
依赖注入框架Castle Windsor从容器里解析一个实例时(也就是调用Resolve方法),是通过调用待解析对象的构造函数new一个对象并返回,那么问题是:它是调用哪个构造函数呢? 无参的构造函数 ...
- TSQL Identity 用法全解
Identity是标识值,在SQL Server中,有ID列,ID属性,ID值,ID列的值等术语. Identity属性是指在创建Table时,为列指定的Identity属性,其语法是:column_ ...
- 卡片抽奖插件 CardShow
这个小项目(卡片秀)是一个卡片抽奖特效插件,用开源项目这样的词语让我多少有些羞愧,毕竟作为一个涉世未深的小伙子,用项目的标准衡量还有很大差距.不过该案例采用 jQuery 插件方式编写,提供配置参数并 ...
- [.NET] C# 知识回顾 - 委托 delegate (续)
C# 知识回顾 - 委托 delegate (续) [博主]反骨仔 [原文]http://www.cnblogs.com/liqingwen/p/6046171.html 序 上篇<C# 知识回 ...
- vmware上网的方式
vmware上网设置 vmware虚拟机上网设置 我的一些心得,如下: 如何使vmware虚拟机中的操作系统能够上网? 第一种情况: 主机使用PPPOE拨号上网 方法一:NAT方式 1.先关闭虚拟机中 ...
- Anders Hejlsberg 技术理想架构开发传奇
Anders Hejlsberg(安德斯-海森博格) 坐在自己的办公室,双眼直直的盯着前方.他要做一个决定,决定自己未来的命运和理想.这是1996年一个普通的下午,几个小时前,他刚与比尔-盖茨结束了 ...
- 用javascript 写个函数返回一个页面里共使用了多少种HTML 标签
今天我无意间看到一个面试题: 如何用javascript 写个函数返回一个页面里共使用了多少种HTML 标签? 不知你看到 是否蒙B了,如果是我 面试,肯定脑子嗡嗡的响.... 网上搜了搜也没有找到答 ...
- javascript之闭包理解以及应用场景
半个月没写博文了,最近一直在弄小程序,感觉也没啥好写的. 之前读了js权威指南,也写了篇博文,但是实话实说当初看闭包确实还是一头雾水.现在时隔一个多月(当然这一段时间还是一直有在看闭包的相关知识)理解 ...
- 关于sqlmap的使用
好记性不如烂笔头,记录一下. 带cookie的注入 python sqlmap.py -u "http://www.xxx.com?id=1" --cookie="coo ...