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 相关文章推荐
不间断滚动JS打包类,基本可以实现所有的滚动效果,太强了
Dec 08 Javascript
慎用 somefunction.prototype 分析
Jun 02 Javascript
jquery 学习之二 属性 文本与值(text,val)
Nov 25 Javascript
调试代码导致IE出错的避免方法
Apr 04 Javascript
node.js中的fs.chmodSync方法使用说明
Dec 18 Javascript
JavaScript_ECMA5数组新特性详解
Jun 12 Javascript
微信小程序页面传值实例分析
Apr 19 Javascript
JS判断时间段的实现代码
Jun 14 Javascript
详解VUE2.X过滤器的使用方法
Jan 11 Javascript
基于axios封装fetch方法及调用实例
Feb 05 Javascript
整理 node-sass 安装失败的原因及解决办法(小结)
Feb 19 Javascript
ES2020 已定稿,真实场景案例分析
May 25 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
根德YB400的电路分析
2021/03/02 无线电
百事可乐也出咖啡了 双倍咖啡因双倍快乐
2021/03/03 咖啡文化
Drupal读取Excel并导入数据库实例
2014/03/02 PHP
在WordPress中使用wp-cron插件来设置定时任务
2015/12/10 PHP
谈谈php对接芝麻信用踩的坑
2016/12/01 PHP
jquery简单体验
2007/01/10 Javascript
jquery 插件之仿“卓越亚马逊”首页弹出菜单效果
2008/12/25 Javascript
javascript 面向对象,实现namespace,class,继承,重载
2009/10/29 Javascript
[JSF]使用DataModel处理表行事件的实例代码
2013/08/05 Javascript
jquery原创弹出层折叠效果点击折叠弹出一个层
2014/03/12 Javascript
setTimeout()递归调用不加引号出错的解决方法
2014/09/05 Javascript
javascript+HTML5的Canvas实现Lab单车动画效果
2015/08/07 Javascript
浅析JavaScript 调试方法和技巧
2015/10/22 Javascript
jQuery源码分析之sizzle选择器详解
2017/02/13 Javascript
javascript cookie的基本操作(添加和删除)
2017/07/24 Javascript
JS实现瀑布流布局
2017/10/21 Javascript
vue.js  父向子组件传参的实例代码
2017/10/29 Javascript
Angular2学习笔记之数据绑定的示例代码
2018/01/03 Javascript
浅谈vue的props,data,computed变化对组件更新的影响
2018/01/16 Javascript
jQuery使用each遍历循环的方法
2018/09/19 jQuery
vue中,在本地缓存中读写数据的方法
2018/09/21 Javascript
解决node-sass偶尔安装失败的方法小结
2018/12/05 Javascript
深入浅析Vue 中 ref 的使用
2019/04/29 Javascript
Node.js如何优雅的封装一个实用函数的npm包的方法
2019/04/29 Javascript
JavaScript实现alert弹框效果
2020/11/19 Javascript
Python文件常见操作实例分析【读写、遍历】
2018/12/10 Python
django form和field具体方法和属性说明
2020/07/09 Python
Python 在 VSCode 中使用 IPython Kernel 的方法详解
2020/09/05 Python
python中用ctypes模拟点击的实例讲解
2020/11/26 Python
Chemist Warehouse中文网:澳洲连锁大药房
2021/02/05 全球购物
体育专业个人求职信范文
2013/12/27 职场文书
领导视察欢迎词
2014/01/15 职场文书
在校大学生个人的自我评价
2014/02/13 职场文书
党的群众路线教育实践活动督导组工作情况汇报
2014/10/28 职场文书
职工培训工作总结
2015/08/10 职场文书
教师师德承诺书2016
2016/03/25 职场文书