Teambition从今年年初开始作国际化,当时的目标是两个季度后可以在中文之外支持英文和日文两个语言,所以团队花了一些时间来尝试一些适合Single
Page Web
App的多语言方案。本文是Teambition的前端工程师陈涌总结的一篇文章,推荐给每一位但愿打造全球化的网页应用的工程师看看=)前端
Teambition的全部产品线都是单页面应用, HTML 在前端渲染所以在须要支持中英文时, 多种语言都加载到前端去渲染.
在切换到这个方案之前, wang'ye的语言是在服务端跟代码一块儿进行转换的,下面写的是切换前遇到问题, 和切换过程咱们采起的方案.编程
最开始的方案当中, 在开发阶段, 代码编译, 语言替换, 都在服务端经过中间件完成,
当浏览器请求对应的代码时, 获取的就是已经编译好的 JavaScript, 已经对应的语言
所以模版也是在后端编译的, 而多语言的内容, 就是经过其中一个 i18n 中间件完成的.后端
好比, CoffeeScript, doT 源代码当中会有一些这样的内容, 用来标记多语言,
这类代码会在发送浏览器前会被处理, 编译成对应的中文版或者英文版:浏览器
errorMessage = "{{__essageMessage}}"
在编译上线的阶段, 整个编译过程咱们会对两种语言都执行一次, 生成两种语言的版本.
用户请求应用时, 拿到的就是预先编译好, 放在 CDN 上的对应语言的代码.前端工程师
开发阶段, 服务端编译, 由于 Node 自己性能问题, 加上应用体积增大, 响应的速度变得很慢.
加上前端调试广泛的作法就是改一行代码就刷新一次页面, 收到性能的影响就更大了.工具
除了性能, 还有个问题是资源打包, 在咱们尝试去掉中间件转而用前端工具编程代码的过程当中,
当语言是经过静态替换强制写入语言的, 意味着代码传输到客户端之前, 两种语言的源码文件已经存在,
因而, 上线以前就须要进行两次打包, 或者说就是打两份. 按上边说的, 会有一些性能上的麻烦.性能
更大的麻烦在于编译过程, 由于两种语言的存在, 出现了一些意外的复杂问题.
代码上线之前的编译, 大体的流程是:测试
1) CoffeeScript 先转换成 JavaScript,
2) 而后 JavaScript 通过 RequireJS 合并, 咱们也会留一个环境测试一下合并的代码,
3) 最后代码上线以前, 会通过压缩,, 最后压缩的代码再上线.ui
编译过程当中间有个调试过程, 这个调试过程, 基本上须要语言的, 也对应刚编译到 JavaScript 这一步.
这就须要在编译到 JavaScript 的时候, 就是上边的编译过程的第一步, 就已经作了多语言.
这以前还要加入一个步骤, 把最初的源代码编译出中英文两份.
最终, 开发目录就须要用到多个版本的项目代码:spa
1) 源码
2) 多语言的源码
3) 编译好的开发代码
4) 合并好的代码
5) 压缩准备上线的代码
注意, 从 2) 开始, 每一个源码都须要有中英文两份,
一般整个编译过程写下来, 上百行的 Gruntfile 是有了, 随着两种语言两倍的代码量, 整个更加复杂.
中间还有 RequireJS 之类的问题, 受到源码复杂的路径影响, 具体这里不展开.
其余还有 LESS, doT, Jade 几种语言的编译, 以及相对位置处理, 都比较繁琐..
固然解决方案基本的思路也是从不少已有的项目里学的, 就是语言放到前端去渲染.
用这个方案, 代码编译的过程就不用编译多个版本了, 编译过程简化..
同时修改一行语言, 就不用整个 CoffeeScript 文件都编译, 只要页面刷新就行了.
而后项目当中维护的文件跟着变少了, 相对维护多种语言的代码就轻松了不少
这个方案的思路是, 语言文件会被整理成两张 JSON 的表, 做为 RequireJS 模块一块儿打包.
前端代码运行过程当中检测浏览器的语言环境, 或者写在设置里的语言,
根据这个语言, 从表中查询内容, 用在页面渲染. 好比这样的代码:
lang.getText('error-message')
固然浏览器当中, 模版一旦渲染完成, 多语言的内容就应该立刻跟上, 不然用户看到当成乱码了.
这里可能有两种策略, 一种模仿后端, 在渲染模版时将语言做为数据传入.
另外一种, 就是经过 DOM 操做, 在模版渲染后经过 jQuery 抓取节点渲染语言.
大体出于两个考虑, 咱们选择了后一种:
1) 不想在模版引擎中嵌入太多逻辑, 并且前端的模版相对后端较弱, 频繁插入数据不那么方便,
2) 使用 DOM 操做就有可能在不进行页面刷新的状况下切换语言了, 更加灵活
因而为了语言的信息能在前端被 jQuery 正确识别, 咱们用了这样的代码来标记:
<span data-lang-text="error-message"></span>
对这样的代码, 咱们写了一个 CoffeeScript View 方法, 用来对当前 View 的 DOM 进行替换.
renderLocales: -> # 获取有 data-lang-* 属性的全部标签 # 逐个标签读取 # 取出属性当中的内容做为 key, 查询语言 # 按照 data-lang-* 的语义, 替换对应的语言
这当中的星号通常是上边的 text, 也有多是 placeholder, 甚至 value 或者其余内容.
极端的状况还多是中英文版本的图片, 那种状况就特殊问题特殊处理吧...
另外一个没有讨论的问题是, DOM 渲染的性能问题, 经过属性查询确实不认为是高性能的,
这里先不深刻, 由于实际应用中, 这部分操做数量不大, 性能方面还不影响用户体验.
到这里, 多语言渲染的问题基本完成了.