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 jQuery插件练习
Dec 24 Javascript
JQuery动态给table添加、删除行 改进版
Jan 19 Javascript
ExtJS中文乱码之GBK格式编码解决方案及代码
Jan 20 Javascript
jQuery+ajax实现无刷新级联菜单示例
May 21 Javascript
JS封装的选项卡TAB切换效果示例
Sep 20 Javascript
Vue.js仿微信聊天窗口展示组件功能
Aug 11 Javascript
JS控制鼠标拒绝点击某一按钮的实例
Dec 29 Javascript
linux 后台运行node服务指令方法
May 23 Javascript
微信小程序搜索功能(附:小程序前端+PHP后端)
Feb 28 Javascript
微信小程序实现的canvas合成图片功能示例
May 03 Javascript
浅谈Vue中render中的h箭头函数
Nov 07 Javascript
Vue项目打包压缩的实现(让页面更快响应)
Mar 10 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
《魔兽争霸3:重制版》翻车了?你想要的我们都没有
2019/11/07 魔兽争霸
第二节 对象模型 [2]
2006/10/09 PHP
PHP similar_text 字符串的相似性比较函数
2010/05/26 PHP
redis 队列操作的例子(php)
2012/04/12 PHP
ThinkPHP模型详解
2015/07/27 PHP
WordPress中重置文章循环的rewind_posts()函数讲解
2016/01/11 PHP
php面向对象程序设计中self与static的区别分析
2019/05/21 PHP
nodejs npm包管理的配置方法及常用命令介绍
2014/06/05 NodeJs
jQuery检测鼠标左键和右键点击的方法
2015/03/17 Javascript
jQuery实现自动滚动到页面顶端的方法
2015/05/22 Javascript
jquery插件validation实现验证身份证号等
2015/06/04 Javascript
JavaScript实现自动生成网页元素功能(按钮、文本等)
2015/11/21 Javascript
JavaScript中splice与slice的区别
2017/05/09 Javascript
基于Cookie常用操作以及属性介绍
2017/09/07 Javascript
利用nvm管理多个版本的node.js与npm详解
2017/11/02 Javascript
在Vue中使用Compass的方法
2018/03/02 Javascript
详解基于mpvue的小程序markdown适配解决方案
2018/05/08 Javascript
React 使用recharts实现散点地图的示例代码
2018/12/07 Javascript
Vue实现购物车的全选、单选、显示商品价格代码实例
2019/05/06 Javascript
[04:03]2014DOTA2西雅图国际邀请赛 LGD战队巡礼
2014/07/07 DOTA
[01:20:47]DOTA2-DPC中国联赛 正赛 Ehome vs Magma BO3 第一场 1月19日
2021/03/11 DOTA
使用Python开发windows GUI程序入门实例
2014/10/23 Python
Python中字典创建、遍历、添加等实用操作技巧合集
2015/06/02 Python
在Python的Django框架中为代码添加注释的方法
2015/07/16 Python
详解Python的Django框架中的中间件
2015/07/24 Python
python 网络爬虫初级实现代码
2016/02/27 Python
python爬虫自动创建文件夹的功能
2018/08/01 Python
python logging添加filter教程
2019/12/24 Python
python GUI库图形界面开发之PyQt5计数器控件QSpinBox详细使用方法与实例
2020/02/28 Python
Django模型中字段属性choice使用说明
2020/03/30 Python
python opencv实现直线检测并测出倾斜角度(附源码+注释)
2020/12/31 Python
用OpenCV进行年龄和性别检测的实现示例
2021/01/29 Python
捷克钓鱼用品网上商店:Parys.cz
2018/06/15 全球购物
Dr.Jart+美国官网:韩国药妆品牌
2019/01/18 全球购物
给女儿的表扬信
2014/01/18 职场文书
中秋节国旗下演讲稿
2014/09/13 职场文书