当前位置: 首页 > Web前端 > HTML

编写简单的i18n库

时间:2023-04-02 15:50:16 HTML

什么是i18n?i18n(它的来源是英文单词internationalization的第一个和最后一个字符i和n,18是中间的字符数)是“internationalization”的缩写。前言第一次接触多语言是在使用狂野的javascript写H5应用的时候。当时为了多语言页面的切换写了很多繁琐重复的代码,自然很难维护。至于第二次开发另一个H5应用,用vue做了一个SPA。多语言自然是用官方的vue-i18n。因为对比了两次开发和维护的经历,我产生了很大的兴趣:假设一个简单的页面需要多种语言。当然你不需要vue,但是如果你不想使用jquery怎么办?如果我想开发一个类似的i18n库,我该如何实现呢?于是用了三天时间(应该是两个月前)写了这个工具库n-i18n,以后写多语言页面的工作量可以减少~简单分析后发现可以参考配置vue-i18n的。但是由于没有实现,也就不需要实现模板引擎了。因此,配置参数其实可以放在DOM节点的dataset(data-i18n)属性上。遍历以读取数据集的节点。解析里面配置的参数后,可以读到多语言中的哪些文本应该绑定到节点上,应该配置哪些参数和数据。在实际开发中。有时,多语言往往不仅仅是简单地切换文本。有时候可能是切换HTML的情况,甚至是切换图片和样式(比如background-image)。因此,渲染模式也分为四种模式:$t;$h;$米;$c,分别对应文本模式、HTML模式、图片模式、样式模式。实现的难点或有趣点是:如何准确找到指定数据集的所有DOM节点?巧妙地使用正则表达式来解析数据集中的各种配置。多种模式如何准确渲染并组合渲染?实现依赖于动态数据,当数据发生变化时相应的DOM节点也会随之更新。基本实现代码参考:https://github.com/Gotjoy/n-i18n/blob/master/src/i18n-a.js1。如何准确找到指定数据集的所有DOM节点?使用递归逐层遍历节点树,将满足要求的节点存储在一个map中,为后续操作进行索引。这里的名称实际上是默认的i18n字符串。当然也可以配置其他字符串,然后在节点中配置data-i18n=""等属性。(function_trace(parent){constchildren=parent.children;for(leti=0,len=children.length;i0){_trace(child);}}}(this.$mount));2.巧妙地使用正则表达式来解析数据集中的各种配置。首先,可以使用字符串拦截操作的api来解析配置,但是会比较冗长。看很多优秀框架的源码,一般都倾向于使用正则表达式来解析。比如我会有以下四种配置,那么如何解析data-i18n中的配置文本得到我感兴趣的信息呢?

这里有两个非常重要的正则表达式,代码稍后会出现。baseRe负责匹配上面的'message.hello'($1)和{msg:'greatlittle~',msg2:'Untiltheday!'}($2)confRe负责进一步匹配{msg:'greatlittle~'',msg2:'Untiltheday!'}文中key($1)和value($2)的常规测试推荐这个网站,多试试https://regexr.com。当然,正则化我就不详细介绍了,毕竟也是一门非常高深的学问。经过规律的处理,已经得到了所有感兴趣的信息。下一步是使用此信息读取多语言配置中的lang数据并更新DOM节点。constbaseRe=/\$[t|h|c|m]\(['"](.*?)['"]\,*\s*(.*)\)/g;constconfRe=/(\w+)\:\s*['"](.+?)['"]/g;letbase='';letconf=Object.create(null);c.replace(baseRe,(match,$1,$2)=>{base=$1;if($2){$2.replace(confRe,(match,$1,$2)=>{conf[$1]=$2;});}});constlang={en:{message:{你好:'你好世界!{msg2}'}},zh:{message:{hello:'你好,世界!{消息}'}}};细心的同学可能会发现一个问题,如何获取a.b.c形式的对象属性并不难。一次遍历就够了。如果实现起来简单,只要这个值不是原来的值,继续往里面走就行了。functiongetValueBy(obj,keystr){constkeyset=keystr.split('.');for(leti=0,len=keyset.length;iconstdataI18n=v.dataset[name].split(/;(?:\s*\$[t|h|c|m])/g);dataI18n.forEach(c=>{const_c=this.parse(c.trim());if(c.includes('$t')){this.render$t(v,_c);}if(c.includes('$h')){this.render$h(v,_c);}if(c.includes('$c')){this.render$c(v,_c);}if(c.includes('$m')){this.render$m(v,_c);}});进一步考虑如下应用场景,一些多语言数据依赖于后端的返回,在应用生命周期中不断更新。为了避免低效的人工操作,这些多语言数据应该是动态依赖的。当数据发生变化时,动态更新依赖于这些数据的DOM节点就足够了。我该怎么做呢。使用Object.defineProperty这个因为Vue而被大家熟知的API,遍历配置好的数据并观察。重点是里面的二传手。当数据的某个值被修改时,会触发对应的setter,并发送信号通知DOM节点进行更新。代码参考:https://github.com/Gotjoy/n-i18n/blob/master/src/i18n-b.js总结造轮子是一个学习和探索的过程,希望你能喜欢这篇文章。当然,如果n-i18n工具能对你有所启发或者帮助,那就更好了~