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防止DIV布局滚动时闪动的解决方法
Oct 30 Javascript
javascript框架设计读书笔记之种子模块
Dec 02 Javascript
JavaScript的模块化开发框架Sea.js上手指南
May 12 Javascript
对象转换为原始值的实现方法
Jun 06 Javascript
jQuery实现右下角可缩放大小的层完整实例
Jun 20 Javascript
Web纯前端“旭日图”实现元素周期表
Mar 10 Javascript
Angularjs根据json文件动态生成路由状态的实现方法
Apr 17 Javascript
ES6 javascript中Class类继承用法实例详解
Oct 30 Javascript
vue 自定义组件 v-model双向绑定、 父子组件同步通信的多种写法
Nov 27 Javascript
Node.js readline 逐行读取、写入文件内容的示例
Mar 01 Javascript
基于vue中css预加载使用sass的配置方式详解
Mar 13 Javascript
vue基于v-charts封装双向条形图的实现代码
Dec 09 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
弄了个检测传输的参数是否为数字的Function
2006/12/06 PHP
调试一段PHP程序时遇到的三个问题
2012/01/17 PHP
解决Codeigniter不能上传rar和zip压缩包问题
2014/03/07 PHP
php计算指定目录下文件占用空间的方法
2015/03/13 PHP
学习php设计模式 php实现门面模式(Facade)
2015/12/07 PHP
SSO单点登录的PHP实现方法(Laravel框架)
2016/03/23 PHP
关于php unset对json_encode的影响详解
2018/11/14 PHP
JAVASCRIPT 对象的创建与使用
2021/03/09 Javascript
js两行代码按指定格式输出日期时间
2011/10/21 Javascript
再谈javascript面向对象编程
2012/03/18 Javascript
Javascript 垃圾收集机制介绍理解
2013/05/14 Javascript
js中typeof的用法汇总
2013/12/12 Javascript
jQuery学习笔记之 Ajax操作篇(一) - 数据加载
2014/06/23 Javascript
js函数与php函数的区别实例浅析
2015/01/12 Javascript
基于BootStrap Metronic开发框架经验小结【七】数据的导入、导出及附件的查看处理
2016/05/12 Javascript
js匿名函数作为函数参数详解
2016/06/01 Javascript
JavaScript 链式结构序列化详解
2016/09/30 Javascript
NODE.JS跨域问题的完美解决方案
2016/10/20 Javascript
vuex+axios+element-ui实现页面请求loading操作示例
2020/02/02 Javascript
如何在vue-cli中使用css-loader实现css module
2021/01/07 Vue.js
[00:36]DOTA2风云人物相约完美“圣”典 12月17日不见不散
2016/11/30 DOTA
[01:11:32]VG vs FNATIC 2019国际邀请赛小组赛 BO2 第二场 8.15
2019/08/17 DOTA
Python使用urllib2获取网络资源实例讲解
2013/12/02 Python
实例解析Python设计模式编程之桥接模式的运用
2016/03/02 Python
python制作爬虫爬取京东商品评论教程
2016/12/16 Python
python 实现语音聊天机器人的示例代码
2018/12/02 Python
python 普通克里金(Kriging)法的实现
2019/12/19 Python
SmartBuyGlasses荷兰:购买太阳镜和眼镜
2020/03/16 全球购物
北京大学自荐信范文
2014/01/28 职场文书
新闻发布会新闻稿
2015/07/17 职场文书
2015年市场营销工作总结
2015/07/23 职场文书
结婚喜宴迎宾词
2015/08/10 职场文书
优秀学生主要事迹怎么写
2015/11/04 职场文书
python3实现Dijkstra算法最短路径的实现
2021/05/12 Python
javascript的var与let,const之间的区别详解
2022/02/18 Javascript
ubuntu如何搭建vsftpd服务器
2022/12/24 Servers