摘要:因為兩次的開發維護體驗產生了對比,使我產生了不小的興趣假設一個簡單的頁面需要多語言。兩個簡單的區別就是和替換的區別。樣式模式其實就是簡單的切換。當修改的某個值時,會觸發對應的,并發射信號通知節點去更新。
i18n是什么?i18n(其來源是英文單詞internationalization的首末字符i和n,18為中間的字符數)是“國際化”的簡稱。
前言第一次接觸多語言是用野生javascript寫H5應用的時候,那時候寫了一大堆的累贅重復的代碼用來切換頁面的多語言,之后自然發現很難維護啦。至于到第二次開發另一個H5應用的時候,用了vue做了一個SPA。多語言自然用了官方的vue-i18n。
因為兩次的開發維護體驗產生了對比,使我產生了不小的興趣:假設一個簡單的頁面需要多語言。當然用不著vue,但是也不想用jquery怎么辦?如果要開發類似的i18n庫,我該如何實現?
于是花了三天(應該也是兩個月前了)寫了這個工具庫n-i18n,以后寫多語言頁面的工作量就可以減少啦~
分析簡單分析后,發現可以參考vue-i18n的配置。但是由于沒有實現也沒有必要實現模板引擎。因此其實可以將配置參數放在DOM節點的dataset(data-i18n)屬性上。遍歷讀取有該dataset的節點。解析里面配置的參數后,就可以讀取該節點應該綁定多語言里的哪個文本,配置什么參數和數據。
在實際開發中。多語言有時候往往不止切換單純的文本。有時候可能是切換HTML,甚至切換圖片,樣式(比如background-image)的情況出現。因此渲染模式也被我分為了$t; $h; $m; $c四種模式,分別對應文本模式、HTML模式、圖片模式、樣式模式。
實現難點或者說有趣的點在于:
如何準確尋找到有指定dataset的所有DOM節點?
巧妙利用正則解析dataset中的多樣配置。
多種模式如何準確渲染和組合渲染?
實現依賴動態數據,數據改變便更新對應的DOM節點。
基礎實現代碼參考:https://github.com/Gotjoy/n-i18n/blob/master/src/i18n-a.js
1. 如何準確尋找到有指定dataset的所有DOM節點?利用遞歸一層層遍歷節點樹,符合要求的節點就保存在一個map里,留待之后對其的操作的索引。這里的name其實是默認的i18n這個字符串,當然也可以配置其他字符串,然后就可以在節點中配置屬性如data-i18n=""。
(function _trace(parent) { const children = parent.children; for (let i = 0, len = children.length; i < len; i++) { const child = children[i]; if (child.dataset[name]) { map[`${name}#${++tid}`] = child; } if (child.children.length > 0) { _trace(child); } } }(this.$mount));2. 巧妙利用正則解析dataset中的多樣配置
首先利用字符串截取操作的api來解析配置雖然也可以,但是會相當啰嗦,翻看許多優秀框架的源碼,都是一般傾向于用正則去解析。比如說我會存在以下四種配置,那么該如何去解析data-i18n里面的配置文本從而拿到自己感興趣的信息呢?
在這里有兩個及其重要的正則,代碼稍后亮相。
baseRe正則負責匹配如上的"message.hello"($1)和{msg: "偉大的渺小~", msg2: "Until the day!"}($2)
confRe正則負責進一步匹配{msg: "偉大的渺小~", msg2: "Until the day!"}文本中key($1)和value($2)
正則的試驗推薦這個網站,多去嘗試https://regexr.com。當然正則我不會詳細介紹了,畢竟也是一個很深厚的學問。
經過正則的處理,已經拿到了全部感興趣的信息。接下來就是可以利用這些信息去讀取多語言配置里lang的數據并且更新DOM節點了。
const baseRe = /$[t|h|c|m]([""](.*?)[""],*s*(.*))/g; const confRe = /(w+):s*[""](.+?)[""]/g; let base = ""; let conf = Object.create(null); c.replace(baseRe, (match, $1, $2) => { base = $1; if ($2) { $2.replace(confRe, (match, $1, $2) => { conf[$1] = $2; }); } });
const lang = { en: { message: { hello: "hello world! {msg2}" } }, zh: { message: { hello: "你好,世界! {msg}" } } };
細心的同學可能會發現一個問題了,如何以a.b.c形式獲取對象屬性這個不難。一個遍歷即可,簡單實現的話只有value不是原始值就繼續往里面走就可以了。
function getValueBy (obj, keystr) { const keyset = keystr.split("."); for (let i = 0, len = keyset.length; i < len; i++) { let v = obj[keyset[i]]; if (v || _.isPrimitive(v)) { obj = v; } } return _.isPrimitive(obj) ? obj : ""; }
找到數據了后,配置文本lang中占位的{msg}的替換利用動態生成正則new RegExp("{" + keys[i] + "}", "g");全局替換即可。
3. 圖片模式和樣式模式以上講的是文本模式和HTML模式。兩個簡單的區別就是innerText和innerHTML替換的區別。但是圖片模式和樣式模式怎么實現?
首先容我啰嗦幾句,為什么我會創造出這兩種模式呢?因為有時候設計稿中的某些圖片的特殊文本也是多語言的,藝術字體(什么高光,花式漸變、浮雕等等)不可能用代碼實現,這時候每個多語言對應切個圖片就好了,然后利用圖片模式切換就好了。樣式模式也是差不多的應用場景了。
圖片模式簡單實現方法就是路徑的替換(當然前提是一定要對多語言圖片命名和存放位置都進行強約束)。樣式模式其實就是簡單的切換class。
// class渲染 function render$c (v, c) { const locale = this.$locale; const langs = Object.keys(this.$messages); for (let i = 0, len = langs.length; i < len; i++) { if (langs[i] !== locale) { _.removeClass(v, `${langs[i]}-${c.base}`); } } _.addClass(v, `${locale}-${c.base}`) } // 圖片渲染 function render$m (v, c) { const locale = this.$locale; const langs = Object.keys(this.$messages).join("|"); const nameRe = new RegExp("(/(" + langs + "))?/[^/]+(?=.[^/]*$)", "g"); const src = v.getAttribute("src"); const path = src.replace(nameRe, `/${locale}/${c.base}`); v.setAttribute("src", path); }4. 多種模式如何準確渲染
多種模式混合使用的時候,如何區分并準確渲染?這個只需要合理斷開配置文本,并分別運用在該節點上即可。需要注意的是,斷開配置時應當判斷分號是否不在文本里,否則容易誤傷友軍。
const dataI18n = 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,遍歷配置的中data并進行觀察。重點是在里面的setter。當修改data的某個值時,會觸發對應的setter,并發射信號通知DOM節點去更新。
代碼參考:https://github.com/Gotjoy/n-i18n/blob/master/src/i18n-b.js
總結造輪子是個學習探索的過程,希望大家可以喜歡這篇文章。當然還有如果n-i18n這個工具對你們有所啟發或者幫助,那就更好了~
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/107527.html
摘要:本國際化方案僅針對技術棧,且不會涉及服務端國際化內容。引入多語言環境的數據雖然我只用到了文本翻譯的功能,以為就不需要加載這些數據,但后來發現這是必須的步驟。 前言 最近新接了一個項目,從0開始做,需要做多語言的國際化,今天搞了一下,基本達到了想要的效果, 在這里簡單分享下: showImg(https://segmentfault.com/img/bVbuiJB); 背景國際化方案國際...
摘要:本國際化方案僅針對技術棧,且不會涉及服務端國際化內容。引入多語言環境的數據雖然我只用到了文本翻譯的功能,以為就不需要加載這些數據,但后來發現這是必須的步驟。 前言 最近新接了一個項目,從0開始做,需要做多語言的國際化,今天搞了一下,基本達到了想要的效果, 在這里簡單分享下: showImg(https://segmentfault.com/img/bVbuiJB); 背景國際化方案國際...
摘要:在寫一個的過程中難免會遇到要做國際化的時候也就是需要根據不同的地區展示不同的文案對于簡單的文本直接用一個或者或者一個變量就能搞定但是有時候需要在一句話中加入變量就比較麻煩或者說比較惡心了比如這樣的情況有個人喜歡了你應運而生其作用是將模板 在寫一個APP的過程中, 難免會遇到要做國際化的時候. 也就是需要根據不同的地區, 展示不同的文案. 對于簡單的文本, 直接用一個xml或者json或...
閱讀 1647·2019-08-30 15:55
閱讀 972·2019-08-30 15:44
閱讀 865·2019-08-30 10:48
閱讀 2024·2019-08-29 13:42
閱讀 3179·2019-08-29 11:16
閱讀 1234·2019-08-29 11:09
閱讀 2053·2019-08-26 11:46
閱讀 611·2019-08-26 11:44