JS不完全国际化&本地化手册 之 理论篇


Posted in Javascript onSeptember 27, 2016

前言

 最近加入到新项目组负责前端技术预研和选型,其中涉及到一个熟悉又陌生的需求——国际化&本地化。熟悉的是之前的项目也玩过,陌生的是之前的实现仅仅停留在"有"的阶段而已。趁着这个机会好好学习整理一下,为后面的技术选型做准备。
 本篇将阐述国际化和本地化的概念,以及其中一个很重要的概念——Language tag(也叫Language code 或 Culture)。

何为国际化?

 国际化我认为就是应用支持多语言和文化习俗(数字、货币、日期和字符比较算法等),而本地化则是应用能识别用户所属文化习俗自动适配至相应的语言文化版本。
 过去常常以为国际化就是字符串的替换——如"你好!"替换为"What's up, man!",其实具体是分为以下5方面:

  1. 字符串替换
     如"你好!"替换为"What's up, man!".
  2. 数字表示方式
     如1200.01,英语表示方式为1,200.01,而法语则为1 200,01,德语则为1.200,01.
  3. 货币表示方式
     如人民币¥1,200.01,美元表示方式为$1,200.01,而英语的欧元则为?1,200.01,德语的欧元则为1.200,01 ?.
    注意: 这里没有还没算上汇率呢.
  4. 日期表示方式
     如2016年9月15日,英语表示方式为9/15/2016, 而法语为15/9/2016, 德语为15.9.2016.
  5. 字符比较算法
     如äz比较时,英语、德语中均是ä排在z前面,而在瑞典语中则是z排在ä前面.

    本地化的关键 —— Language Tag

     既然要自动适配至用户所属的语言文化版本,那么总得有个根据才能识别吧?我想大家应该对zh-CNen等不陌生吧,而它们正是我们所需的根据了!在我们使用已有i18n库实现国际化/本地化时,必定会写下以下文档

    {
     "en": { "name": "Enter Name" },
     "zh-CN": { "name": "输入姓名" }
    }

     但除了enzh-CN还有其他键吗?它们的组成规则又是如何的呢?下面我们来稍微深入的了解这些Language Tag吧!

语法规则

注意以下采用ABNF语言描述(ABNF的语法请参考语法规范:BNF与ABNF)

Language-Tag = langtag
    / privateuse
    / grandfathered

langtag = language
   ["-" script]
   ["-" region]
   *("-" variant)
   *("-" extension)
   ["-" privateuse]

可以看到Language-Tag分为langtagprivateusegrandfatherd三个子类,下面我们先了解一般情况用不上的两个吧!
privateuse
 标签的意思不由subtag registry定义,而是由使用的团队间私自定义、维护和使用。
 格式:

privateuse = "x" 1*("-" (1*8alphanum))

示例:x-zh-CN是privateuse,其意思不一定与languagezh-CN一致。
注意: 只作为小集团内部用可以,决不能大范围适用。

grandfathered
 用于向后兼容。由于RFC 4646前的标签无法完全匹配当前registry的标签语法和意思,因此通过grandfathered来提供向后兼容的特性。
 语法:

grandfathered = irregular
    / regualr
irregular = "en-GB-oed"   ; irregular tags do not match
   / "i-ami"    ; the 'langtag' production and
   / "i-bnn"    ; would not otherwise be
   / "i-default"   ; considered 'well-formed'
   / "i-enochian"  ; These tags are all valid,
   / "i-hak"    ; but most are deprecated
   / "i-klingon"   ; in favor of more modern
   / "i-lux"    ; subtags or subtag
   / "i-mingo" 
   / "i-navajo"
   / "i-pwn"
   / "i-tao"
   / "i-tay"
   / "i-tsu"
   / "sgn-BE-FR"
   / "sgn-BE-NL"
   / "sgn-CH-DE"
regular = "art-lojban"  ; these tags match the 'langtag'
  / "cel-gaulish"  ; production, but their subtags
  / "no-bok"   ; are not extended language
  / "no-nyn"   ; or variant subtags: their meaning
  / "zh-guoyu"   ; is defined by their registration
  / "zh-hakka"   ; and all of these are deprecated
  / "zh-min"   ; in favor of a more modern
  / "zh-min-nan"  ; subtag or sequence of subtags
  / "zh-xiang"

注意: 几乎所有grandfarthered标签均可被当前registry的标签及其组合作替代(像i-tao可以被tao代替),因此如无意外请使用现行的标签吧。

下面就到了我们的重头戏langtag了,首先我们看看langtag下的第一个subtag——language.

Primary language subtag

 像en这种就是Primary language subtag,用于标识资源所对应的语言。
 语法:

language = 2*3ALPAH
   ["-" extlang]
   / 4ALPHA
   / 5*8ALPHA
extlang = 3ALPHA
   *2("-" 3ALPHA)

看到language有三种形式,其中让我比较好奇的是第一种2*3ALPHA ["-" extlang]。这种形式中前面的2*3ALPHA称为macrolanguage,用于标明资源对应一种语言的汇总,而具体的某一种语言/方言则通过extlang指定。而包含extlang部分的language也被称为encompassed language.
zh-cmnzh-yue就是encompassed language,其中zh是macrolanguage,而cmnyue则是extlang。
 这里有个很有趣的事情是,我们认为普通话和广东话等都是汉语的方言,但西方却认为普通话、广东话根本就不属于一种语言,因此像zh-cmnzh-yue在规范中被设置为redundant,建议直接使用cmnyue等。不过由于历史原因,我们还是使用zh-CN代表cmn-CN
 另外现在可以作为macrolanguage的就只有7个标签(ar,kok,ms,sw,uz,zhsgn)
 另外几个和cmn类似的subtags如下

cmn 普通话(官话、国语)
wuu 吴语(江浙话、上海话)
czh 徽语(徽州话、严州话、吴语-徽严片)
hak 客家语
yue 粤语(广东话)
nan 闽南语(福建话、台语)
cpx 莆仙话(莆田话、兴化语)
cdo 闽东语
mnp 闽北语
zco 闽中语
gan 赣语(江西话)
hsn 湘语(湖南话)
cjy 晋语(山西话、陕北话)

注意: 一般采用全小写

Script subtag

 用于指定字迹或文字系统资源所属的语言和方言等。
 语法:

script = 4ALPHA

注意: 一般采用首字母大写,后续字母全小写

Region subtag

 指定与国家、地域对应的语言/方言文化。
 语法:

region = 2ALPHA
  / 3DIGIT

注意: 一般采用全大写

Variant subtag

 指定其他subtag又无法提供的额外信息
 语法:

variant = 5*8alphanum
  / (DIGIT 3alphanum)

示例:de-CH-1996其中1996是variant subtag,整体意思是在Switzerland使用的自1996改良过的德语。

Extension subtag

 提供一种机制让我们去扩展langtag
 语法:

extension = singleton 1*("-" (2*8alphanum))
singleton = DIGIT
   / %x41-57
   / %x59-5A
   / %x61-77
   / %x79-7A

现在仅支持u作为sigleton的值。
示例:de-DE-u-co-phonebk表示采用电话本核对的方式对内容进行排序等操作。

更多关于language-tag的信息请参考BCP 47

如何选择Language Tag

 硬着头皮啃下这么多规范的内容,但我还不知道如何组合合适的language-tag呢:(其实选择和组合的原则就只有一条
在足以区别当前上下文中其他language-tag的前提下,保持language-tag足够地短小精干
示例1:下文普通话、粤语并存

<p lang="cmn">
小陈说:"老大爷,东方广场怎么走啊?"
老大爷回答道:"<span lang="yue">你讲咩也啊?我听唔明喔。</span>"
</p>

示例2:下文含大陆人讲英语、香港人讲普通话和美国人说英语

<p lang="cmn">
小陈说:"<span lang="en-CN">Hi, where are you come from?</span>"
李先生说:"<span lang="cmn-HK">你的英文跟我的普通话一样普通啊,哈哈!</span>"
Simon说:"<span lang="en">Hey, what's up!</span>"
</p>

 那现在引出另一个问题,那就是我们怎么知道各个subtag具体定义了哪些值呢?
具体都定义在IANA Language Subtag Registry中了。
假如觉得查找起来还是不方便,那么就使用Language Subtag Lookup tool吧!
另外若不清楚各国各地区所使用的语言或方言时,可通过Ethnologue查看,直接点击地图上的区域即可获取相应的subtag信息。

总结

 现在我们已经对国际化和本地化有了更全面的理解,也对Language tag有了更深入的认识,现在是不是迫不及待想挽起袖子撸代码呢?敬请期待下篇《JS魔法堂:不完全国际化&本地化手册 之 实战篇》

感谢

网页头部的声明应该是用 lang="zh" 还是 lang="zh-cn"?
Language Subtag Registry
BCP 47
Language on the Web
Choosing a Language Tag
Language tags in HTML and XML

Javascript 相关文章推荐
ASP.NET中使用后端代码注册脚本 生成JQUERY-EASYUI的界面错位的解决方法
Jun 12 Javascript
jQuery的实现原理的模拟代码 -5 Ajax
Aug 07 Javascript
javascript动态的改变IFrame的高度实现自动伸展
Oct 12 Javascript
JavaScript参数个数可变的函数举例说明
Oct 10 Javascript
jQuery验证插件validation使用指南
Apr 21 Javascript
JavaScript使用encodeURI()和decodeURI()获取字符串值的方法
Aug 04 Javascript
jquery validate表单验证的基本用法入门
Jan 18 Javascript
基于JavaScript实现简单的音频播放功能
Jan 07 Javascript
快速解决brew安装特定版本flow的问题
May 17 Javascript
js实现图片上传并预览功能
Aug 06 Javascript
JavaScript中的全局属性与方法深入解析
Jun 14 Javascript
Nuxt.js 静态资源和打包的操作
Nov 06 Javascript
Javascript json object 与string 相互转换的简单实现
Sep 27 #Javascript
js转html实体的方法
Sep 27 #Javascript
JSON与String互转的实现方法(Javascript)
Sep 27 #Javascript
通过JS获取Request.QueryString()参数的值实现方法
Sep 27 #Javascript
微信小程序使用第三方库Underscore.js步骤详解
Sep 27 #Javascript
微信小程序使用第三方库Immutable.js实例详解
Sep 27 #Javascript
微信小程序 在Chrome浏览器上运行以及WebStorm的使用
Sep 27 #Javascript
You might like
延长phpmyadmin登录时间的方法
2011/02/06 PHP
PHP类继承 extends使用介绍
2014/01/14 PHP
php通过字符串调用函数示例
2014/03/02 PHP
yii,CI,yaf框架+smarty模板使用方法
2015/12/29 PHP
js中将HTMLCollection/NodeList/伪数组转换成数组的代码
2011/07/31 Javascript
页面调用单个swf文件,嵌套出多个方法。
2011/11/21 Javascript
打印json对象的内容及JSON.stringify函数应用
2013/03/29 Javascript
jQuery简单实现两级下拉菜单效果代码
2015/09/15 Javascript
Javascript 6里的4个新语法
2016/08/25 Javascript
Bootstrap缩略图的创建方法
2017/03/22 Javascript
JavaScript ES6中const、let与var的对比详解
2017/06/18 Javascript
jquery.validate.js 多个相同name的处理方式
2017/07/10 jQuery
nodejs acl的用户权限管理详解
2018/03/14 NodeJs
vue技术分享之你可能不知道的7个秘密
2018/04/09 Javascript
vue使用技巧及vue项目中遇到的问题
2018/06/04 Javascript
Vue项目中添加锁屏功能实现思路
2018/06/29 Javascript
vscode中vue-cli项目es-lint的配置方法
2018/07/30 Javascript
JavaScript学习笔记之DOM操作实例分析
2019/01/08 Javascript
原生js实现的移动端可拖动进度条插件功能详解
2019/08/15 Javascript
解决使用layui的时候form表单中的select等不能渲染的问题
2019/09/18 Javascript
VUE table表格动态添加一列数据,新增的这些数据不可以编辑(v-model绑定的数据不能实时更新)
2020/04/03 Javascript
[53:15]Mineski vs iG 2018国际邀请赛小组赛BO2 第二场 8.16
2018/08/17 DOTA
python快速查找算法应用实例
2014/09/26 Python
python构建自定义回调函数详解
2017/06/20 Python
Python获取CPU、内存使用率以及网络使用状态代码
2018/02/08 Python
对Python w和w+权限的区别详解
2019/01/23 Python
Python使用numpy模块实现矩阵和列表的连接操作方法
2019/06/26 Python
YSL圣罗兰美妆美国官网:Yves Saint Lauret US
2016/11/21 全球购物
新教师工作感言
2014/02/16 职场文书
电台实习生求职信
2014/02/25 职场文书
2016年幼儿园教师政治学习心得体会
2016/01/23 职场文书
利用Nginx代理如何解决前端跨域问题详析
2021/04/02 Servers
vue实现可拖拽的dialog弹框
2021/05/13 Vue.js
【海涛教你打DOTA】黑鸟第一视角解说
2022/04/01 DOTA
SQL Server数据库查询出现阻塞之性能调优
2022/04/10 SQL Server
vue组件vue-esign实现电子签名
2022/04/21 Vue.js