目前,DJI官网,支持8种语言,还在不断的增加。
多语言系统关键构成部分包括三个部分,录入,存储词条并提供接口,后台程序调用并显示。核心系统,我们称之为翻译中心,使用key来与词条对应,开发时,我们在页面上需要调用多语言的地方写入key,然后,渲染页面时,获取页面上的key,在加上本次请求的对应语言后,向翻译中心发起请求,获得响应后,替换key,并显示对应文本到页面。
在sails中,集成的是node-i18n这个多语言模块,但是,这个模块的功能较弱,不能适应我们当前的多语言需求,故而,我们开发了自己的多语言模块。 有以下下几个关键问题:
1、node是异步执行的
JS是异步执行的,这个对于前端来说,是家常便饭。问题出在什么地方呢?如果,是ruby这类同步执行的语言,那么好了,渲染模板,逐条的翻译即可,反正,你必须要完成了请求,拿到了翻译,才会执行下一条。但是,node不一样了,它不会等你的,发出请求,不等响应,直接去翻译下一条了。意思就是你的页面渲染都已经完成了,但是,可是,你的翻译还在路上呢。用户,看到的就是key,不是文本。这怎么办?苦苦的思索,也找了好久,终于,在阳台上来回踱步的时候,想到了一个办法。什么? 问题的关键在于什么?想想看。我必须,首先拿到页面上翻译的key才能够,去拿到对应的翻译。我提前是不知道的,只有页面可以告诉我。
第一个办法是,建一个key的配置文件,然后跟页面使用route关联起来,这样就可以读配置拿到翻译了。但是,额外工作太多,不够丝滑,一定要以最自然的方式来开发,这是原则。这种尝试在实际工作中非常多,只有,以丝滑为基本原则,不断加以改进,在日后的开发和维护中,才能够加倍收益。
第二办法来了,什么?灵感来自于模板引擎的一个方法,compileFIle,这个方法可以编译指定的模板,但是不渲染出去,那如果我提前编译一次,是不是就可以拿到页面key了呢?又不会渲染出去。拿到key后,再并发的调用翻译接口,就能以比同步快得多的速度,拿到翻译,存入I18n对象。最后,再调用回调函数,render模板,这个时候,再去调用翻译,就是直接从内存里去了,不再是异步的了,页面翻译自然,华丽的展示在万千用户的眼前。
2、当前的请求的语言是什么?
如何知道,当前用户请求的是什么语言的页面呢?英文?中文?韩文?,(不考虑修改子域名)对于一个非ajax得get请求,请求头前端基本改不了,但有三个东西可以利用,页面路径,cookie,请求参数。
对于,页面路径,我们可以在路径上携带语言信息,如 /cn/support, /kr/support … 优点是对seo是有利的 ,而且,容易辨识定位问题, 对于,cookie ,可以将语言信息写入cookie,切换语言时改变cookie,不允许从路径中改变,好处是有效统一语言切换入口,缺点是,cookie可能被禁用,不利于多语言seo。 请求参数,同路径,但是基本不怎么用,对seo同样不友好。综 上所述,出于,seo,页面缓存(后面会提到)的考虑,我们选择了第一种方案,在页面路径上携带语言信息。
好了,现在服务器拿到了语言信息,是不是万事大吉了呢?当然不是,有一个这样的变量 I18n.locale 对应从路径中取出的语言信息,开发时没有什么问题的,要英文,要中文,高高兴兴拿去测试,那么问题来了?为什么,有时候我明明请求的中文,却返回英文. 找bug吧,抽风?网络传输错误?语言提前有误?缓存有误? 还是,你用了全局变量。注意,全局变量,在多用户的环境下,必须慎用。我 发了一个请求 /cn/phantom-4, node程序也拿到了当前语言 cn , 正要去那翻译呢。几乎同时,请求 /de/phantom-4来了,locale瞬间被改变了,现在拿到的将是德语翻译,用户一看,就开始怀疑人生了。怎么办?
这个问题,在没有答案之前,几乎令我抓狂,有没有一种变量,生命周期是在这个请求内,而且,可以被共享。没有?有?当然有,你观察一下就会发现,各个中间件里传递着一个神奇的变量,req,对于每一个请求而言,这个对象都是不同的。来吧,改造一下,把所有依赖于当前请求的属性,方法,重新绑定到req对象上。虽然,调整很大,但是总算可用了,可是上线后又出现了类似问题,这尼玛,就不得不怀疑人生了。我错了?
还是我被骗了,几乎把断点从开始打到结束,终于发现了,诡异!我使用了一个语言变量,req.locale, 它本来也没啥问题,但是,前面表现正常的它,后面竟然变量被改写了,我找遍了我的代码,发现并没有改变过这个变量呀,奇怪了?难道是?我忽然想起,node-I18n貌似也使用了这个变量,它判断语言跟我们可不一样,查了查源码,果然有这句。换了一个变量后,发现一切只有那么正常了,再也不抽风了。
小结
1、以丝滑为原则,不断改进代码。不论是,helper方法,数据接口,必须让上层调用,直接,简单,灵活。 2、多用户应用,务必慎用全局变量,req,contxt,this之类的才是更好的选择。
系列文章: sails js 在 DJI 官网的应用(一)—— 前言 & 概述 sails js 在 DJI 官网的应用(二)—— 多语言
下一章节将会向大家讲述缓存的实现与管理: sails js 在 DJI 官网的应用(三)—— 缓存
DJI 官网团队持续招人中 ,欢迎NB的前端/全栈工程师们,加入我们一起奋斗,打造一流的技术团队, 来吧朋友,国内绝对一流的工资、福利,甚至期权,等你来拿,简历可发送至 fei.pan@dji.com
其实还是没扯到 sails
@magicdawn 我们只是以 sails 作为底层技术框架,不会对sails 这门语言做过多的介绍,讲语言本身身就是个过时的事情, 可能是标题误导,见谅
@felling 很好,我赞赏这种有一说一的文章,比抛书包强 自豪地采用 CNodeJS ionic
用 Sails 可以试试 ThinkJS,不管是性能还是易用性,ThinkJS 都完胜 Sails