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 相关文章推荐
javascript实现 在光标处插入指定内容
May 25 Javascript
鼠标放在图片上显示大图的JS代码
Mar 26 Javascript
form表单中去掉默认的enter键提交并绑定js方法实现代码
Apr 01 Javascript
Javascript学习笔记之 函数篇(二) : this 的工作机制
Jun 24 Javascript
jquery操作checkbox示例分享
Jul 21 Javascript
javascript制作坦克大战全纪录(2)
Nov 27 Javascript
jQuery中:reset选择器用法实例
Jan 04 Javascript
JavaScript中setUTCMilliseconds()方法的使用详解
Jun 12 Javascript
js模拟淘宝网的多级选择菜单实现方法
Aug 18 Javascript
JS通过位运算实现权限加解密
Aug 14 Javascript
微信小程序实现简单评论功能
Nov 28 Javascript
详解VUE单页应用骨架屏方案
Jan 17 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
php代码中使用换行及(\n或\r\n和br)的应用
2013/02/02 PHP
php使用mb_check_encoding检查字符串在指定的编码里是否有效
2013/11/07 PHP
Yii2分页的使用及其扩展方法详解
2016/05/23 PHP
PHP读取、解析eml文件及生成网页的方法示例
2017/09/04 PHP
CodeIgniter框架数据库基本操作示例
2018/05/24 PHP
Laravel框架路由和控制器的绑定操作方法
2018/06/12 PHP
PHP如何防止XSS攻击与XSS攻击原理的讲解
2019/03/22 PHP
有关PHP 中 config.m4 的探索
2020/08/26 PHP
为jquery.ui.dialog 增加“自动记住关闭时的位置”的功能
2009/11/24 Javascript
javascript实现2048游戏示例
2014/05/04 Javascript
jQuery操作元素css样式的三种方法
2014/06/04 Javascript
浅谈jQuery中setInterval()方法
2015/07/07 Javascript
谈谈js中的prototype及prototype属性解释和常用方法
2015/11/25 Javascript
用JavaScript获取页面文档内容的实现代码
2016/06/10 Javascript
vue2 中如何实现动态表单增删改查实例
2017/06/09 Javascript
利用Jasmine对Angular进行单元测试的方法详解
2017/06/12 Javascript
vue 2.0项目中如何引入element-ui详解
2017/09/06 Javascript
vue实现树形菜单效果
2018/03/19 Javascript
postman自定义函数实现 时间函数的思路详解
2019/04/17 Javascript
vue element-ui table组件动态生成表头和数据并修改单元格格式 父子组件通信
2019/08/15 Javascript
使用 Element UI Table 的 slot-scope方法
2019/10/10 Javascript
Python中itertools模块用法详解
2014/09/25 Python
Python连接phoenix的方法示例
2017/09/29 Python
Python制作词云的方法
2018/01/03 Python
Python控制Firefox方法总结
2019/06/03 Python
Python读写文件基础知识点
2019/06/10 Python
对python中的控制条件、循环和跳出详解
2019/06/24 Python
用Cython加速Python到“起飞”(推荐)
2019/08/01 Python
pytorch模型预测结果与ndarray互转方式
2020/01/15 Python
Python for循环通过序列索引迭代过程解析
2020/02/07 Python
python使用paramiko实现ssh的功能详解
2020/03/06 Python
Python如何输出警告信息
2020/07/30 Python
python用tkinter实现一个gui的翻译工具
2020/10/26 Python
捷克浴室和厨房设备购物网站:SIKO
2018/08/11 全球购物
协商一致解除劳动合同协议书
2014/09/14 职场文书
MySQL实战记录之如何快速定位慢SQL
2022/03/23 MySQL