日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区

您的位置:首頁技術文章
文章詳情頁

vue項目實現(xiàn)多語言切換的思路

瀏覽:25日期:2022-11-21 10:21:30

Web 項目多語言(i18n,即國際化)是比較常見的需求,常規(guī)的做法大概有以下幾種:

每種語言單獨開發(fā)頁面,適用于 CMS 之類的網站 多語言文本和頁面結構分離,運行時動態(tài)替換。適用于單頁應用(SPA) 直接用網頁翻譯插件,機器翻譯。這種效果不太理想,同時有一些局限性(后面會講到)

問題

每一種方案都有各自的優(yōu)點和局限性,具體項目應該根據(jù)實際情況選擇。最近在工作中碰到的需求是要在現(xiàn)有的項目基礎上快速推出多語言版本。項目是基于 Vue.js 開發(fā)的,已經迭代過很多版本了。其實一開始是有規(guī)劃多語言的,也引進了 vue-i18n 插件。這個插件就是上面第二種方案,用 JSON 文件管理多語言的文本資源,在 Vue 組件模板里通過鍵名引用文本。但是要管理這些英文鍵名比較麻煩,命名就很頭疼。而且閱讀代碼的時候也很難從鍵名快速識別出對應的中文。后面發(fā)現(xiàn) VS Code 有相關的插件,可以顯示出對應的中文,但是代碼找起來還是有點麻煩。再加上產品的多語言版本一直沒有提上日程,時間久了就嫌麻煩,慢慢地就直接在模板里寫中文了。

結果,該來的還是來了。老板突然說最近要推出英文版,后續(xù)還有其他語言。一開始的想法是直接用 Chrome 瀏覽器自帶的 Google 翻譯功能,怎么快怎么來。但經過一番測試,發(fā)現(xiàn)了不少問題。首先機翻的效果肯定是要打折扣的,但這還在接受范圍內。最關鍵的是會影響到功能使用。什么問題呢?由于項目是用 Vue.js 開發(fā)的單頁應用,頁面內容完全是用 JS 動態(tài)渲染的。有些對話框內的文字 Google 翻譯就忽略了。另外,Google 翻譯只處理了 DOM 文本節(jié)點,input輸入框內的文字(包括placeholder)被忽略了。最嚴重的問題是,經過 Google 翻譯處理后的 DOM 元素,竟然失去了 Vue 響應式特性,數(shù)據(jù)變化后 DOM 內的文字不會更新了!

如果要繼續(xù)采用瀏覽器 Google 翻譯的方案,就要解決這幾個問題。通過調試發(fā)現(xiàn) Google 翻譯用的 JS 腳本是嵌入到瀏覽器 VM 里的,通過 HTTP 調用翻譯服務,然后修改 DOM 元素。JS 腳本是壓縮混淆過的,格式化后也很難看。想要找到更新 DOM 的代碼,然后用自己的邏輯去覆蓋?眼睛都看瞎了,還是算了。

vue項目實現(xiàn)多語言切換的思路

鑒于以上原因,瀏覽器自帶的 Google 翻譯方案基本不考慮了。

現(xiàn)在只剩下第二種方案了,語言配置文件和頁面結構分離。前面提過,vue-i18n用得不徹底,如果把所有組件重新規(guī)范化,工作量太大了。有沒有辦法不修改現(xiàn)有代碼,也能實現(xiàn)文本翻譯呢?很自然地就想到了 Google 翻譯的思路,直接對頁面渲染結果進行翻譯。自己翻譯的優(yōu)勢就是,可以精細地控制 DOM 操作,比如可以把輸入框里的文本和placeholder也翻譯出來。同時,經過研究發(fā)現(xiàn),Vue 組件通過數(shù)據(jù)綁定渲染出來的 DOM 元素,包含的文本內容不能直接通過 innerHTML或者innerText修改,這樣會導致響應式失效。解決辦法是操作它的子元素,也就是文本節(jié)點(nodeType為3的節(jié)點),修改它的 textContent屬性。

多語言配置映射表

跟 Google 翻譯不同之處在于,我們采用靜態(tài)翻譯,也就是通過多語言配置文件映射。 vue-i18n 是每種語言準備一個 JSON 文件,屬性名用英文,用命名空間(多層級對象)的方式避免命名沖突。我直接簡化了,用一個 JS 對象存儲所有語言版本,鍵名就是頁面用到的中文。隨著日積月累的開發(fā)迭代,這些中文散落在幾百個文件里……我的做法是用 VS Code 全局正則搜索,把查找結果復制出來,寫一個 JS 方法把這些字符串處理成 JS 對象。

vue項目實現(xiàn)多語言切換的思路

匹配中文的正則(不夠全面,有些還夾雜了其他符號):

[A-Z]*[u4e00-u9fa5][,,!! 0-9a-zA-Zu4e00-u9fa5]*

將結果復制到翻譯工具翻譯,再寫一個函數(shù)把這些文本合并成對象,并保存到labels.js文件中備用。

var kv = dist.reduce((acc,cur, index) => {acc[cur]=en[index] || cur;return acc;},{})

對象的結構大致如下:

// labels.jsexport default { 客戶性名: { en: ’Customer Name’, }, // 動態(tài)文本,后面會講到 ’剩余{0}臺礦機未登記’: { en: ’{0} unregistered’, }, xxxx: { en: ’XXX’, }}

操作 DOM

跟 Google 翻譯類似,我們也采取事后更新 DOM 的方式來進行翻譯。由于是單頁應用,隨著用戶的操作,會不停地更新 DOM。一開始的想法是監(jiān)聽整個 body的變化,在回調里再更新 DOM。監(jiān)聽 DOM 變化有一個原生的 API 可用,就是 MutationObserver。

mounted() { this.observeDOM(document.body);},methods: { observeDOM(el) { let mutationTimer; const vm = this; const observer = new MutationObserver(() => { // 類似于 debounce 的效果,多次調用合并為一次 clearTimeout(mutationTimer); mutationTimer = setTimeout(() => { if (!vm.mutationFromTrans) { translate(); vm.mutationFromTrans = true; setTimeout(() => { vm.mutationFromTrans = false; }, 300); } }, 100); }); const options = { childList: true, // 監(jiān)視node直接子節(jié)點的變動 subtree: true, // 監(jiān)視node所有后代的變動 attributes: true, // 監(jiān)視node屬性的變動 characterData: true, // 監(jiān)視指定目標節(jié)點或子節(jié)點樹中節(jié)點所包含的字符數(shù)據(jù)的變化。 }; if (this.language === ’en’) { observer.observe(el, options); } },}

但是試過之后發(fā)現(xiàn)這會導致無線循環(huán),因為沒有判斷 DOM 的變化來自用戶操作還是翻譯本身。所以代碼里后面加了判斷,但是結果依然不理想。這種操作代價太大了,頁面性能受了很大影響。而且還有個很明顯的問題,就是進入到新的界面會閃一下,從中文變成英文。這個體驗太糟糕了。后面有改進辦法。

翻譯

先來來看下翻譯的過程。翻譯就是從多語言配置對象里查找匹配的屬性名,獲取對應語言的屬性值。這對于靜態(tài)文本來說比較簡單,直接用屬性名就好了。但是對于動態(tài)的文本怎么處理呢?由于中英文表達方式不一樣,這種文本不能簡單地拆分成多個部分單獨處理,而是要在英文的表達方式里替換動態(tài)數(shù)據(jù)。我的做法是使用帶格式的鍵名,比如{0}這樣的占位符。在查找的時候,優(yōu)先匹配固定文本。因為大部分情況是固定文本,而且這種匹配是O(1)時間復雜度的,優(yōu)先判斷會提高性能。匹配失敗的時候才去提前構造好的正則列表里遍歷匹配,成功則提取正則匹配的group用于替換動態(tài)數(shù)據(jù)。如果失敗,說明沒有對應的翻譯,直接返回原始字符串就行了。

const keys = Object.keys(words);// 提前緩存正則,避免重復執(zhí)行消耗性能const regExps = keys.reduce((acc, key) => { // 模板型鍵名 if (key.indexOf(’{0}’) > -1) { const reg = new RegExp(key.replace(’{0}’, ’(.+)’)); acc.push({ expression: reg, key, }); } return acc;}, []);export function translate(el = document.body, lang = ’en’) { const kv = words; if (!el.querySelectorAll) { return; } const _trans = label => { const text = label?.trim?.(); if (!text) { return label; } if (kv[text]?.[lang]) { return kv[text]?.[lang]; } for (let index = 0; index < regExps.length; index++) { const regItem = regExps[index]; const m = text.match(regItem.expression); if (m) { return kv[regItem.key][lang].replace(’{0}’, m[1]); } } return text; }; [...el.querySelectorAll(’*’)].forEach(node => { // 不能直接修改node.innerText,會導致Vue響應式失效 // node.innerText = kv[node.innerText?.trim?.()] || node.innerText; if (node.nodeName === ’INPUT’ && node.type === ’text’) { node.value = _trans(node.value); node.placeholder = _trans(node.placeholder); } const textNodes = [...node.childNodes].filter(n => n.nodeType === 3); textNodes.forEach(textNode => { textNode.textContent = _trans(textNode.textContent); }); });}

改進后的 DOM 操作

前面提過,如果在 DOM 渲染后再執(zhí)行翻譯,頁面性能非常差。于是想到了 Vue 本身的渲染過程,能不能攔截 Vue 組件渲染過程,插入一些額外的邏輯呢?通過扒源碼發(fā)現(xiàn),Vue 原型上有個__patch__方法,每次更新 DOM 的時候都會執(zhí)行。就從這里入手, 重寫這個方法,對還沒掛載到文檔樹的 DOM 元素執(zhí)行翻譯操作。

const __patch__ = Vue.prototype.__patch__;Vue.prototype.__patch__ = function() { const elm = __patch__.apply(this, arguments); if (this.$store?.getters?.language) { translate(elm, this.$store?.getters?.language); } return elm;};

至此,基本完成了多語言翻譯。經過權衡對比,這個方案算是比較省時省力又能完成需求的了。當然,這種方案或多或少對頁面性能有一定影響,畢竟增加了 DOM 更新的時間。尤其是動態(tài)文本較多的情況,涉及到遍歷正則匹配,比較耗時。如果大家有更好的方案,歡迎留言!

以上就是vue項目實現(xiàn)多語言切換的思路的詳細內容,更多關于vue項目多語言切換的資料請關注好吧啦網其它相關文章!

標簽: Vue
相關文章:
日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区
国产精品福利在线观看播放| 在线视频亚洲欧美中文| 中文字幕免费一区二区| 亚洲狼人精品一区二区三区| 激情综合网五月| 午夜宅男久久久| 奇米狠狠一区二区三区| 精品精品国产三级a∨在线| 精品国产成人| 久久在线免费| 亚洲乱码视频| 国内不卡的一区二区三区中文字幕| se01亚洲视频| 免费在线看一区| 国产美女久久| 久久免费黄色| 日韩精品一区二区三区av | 日本不卡视频在线观看 | 快she精品国产999| 国产乱论精品| 日韩精品永久网址| 先锋亚洲精品| 精品中国亚洲| 久久国产精品亚洲77777| 久久wwww| 在线综合亚洲| 国产欧美三级| 午夜久久免费观看| 久久99久久人婷婷精品综合| 色婷婷精品视频| 一区二区三区四区精品视频| 久久伊人国产| 视频一区在线视频| 九九久久国产| 男女男精品视频网| 日本久久综合| 日韩**一区毛片| 欧美不卡高清一区二区三区| 亚洲精品乱码久久久久久蜜桃麻豆| 麻豆精品久久久| 久久av一区二区三区| 精品香蕉视频| 日本亚州欧洲精品不卡| 久久久精品网| 国产精品视频一区二区三区四蜜臂 | 青青国产精品| 日韩在线高清| 日本午夜精品久久久| 激情五月综合网| 国内精品麻豆美女在线播放视频| 免费的成人av| 欧美丝袜一区| 精品久久电影| 欧美一区自拍| 免费在线看一区| 激情欧美一区二区三区| 欧美精品第一区| 亚洲1区在线观看| 欧美69视频| 电影91久久久| 欧美一级久久| 亚洲天堂av资源在线观看| 青青久久av| 久久久久久网| 麻豆精品在线| 欧美一区二区三区久久精品| 久久高清国产| 国产亚洲激情| 欧美a级片一区| 成人精品亚洲| av在线资源| 欧美经典一区| 久久国产尿小便嘘嘘| 日韩三区四区| 亚洲一级大片| 蜜桃久久久久久| 免费日韩av片| 老色鬼久久亚洲一区二区| 欧美日韩国产综合网| 不卡在线一区| 午夜久久久久| 日韩一区二区久久| 国产一区欧美| 欧美不卡高清| 国模 一区 二区 三区| 久久一区二区三区喷水| 蜜臀久久精品| 丁香六月综合| 久久久久91| 午夜精品婷婷| 免费成人性网站| 日日摸夜夜添夜夜添国产精品| 亚洲a级精品| 97精品资源在线观看| 欧美精品中文字幕亚洲专区| 国产精品资源| 国产一区二区三区国产精品 | 韩国女主播一区二区三区| 精品午夜视频| 欧美羞羞视频| 欧美日韩在线网站| 伊人精品在线| 亚洲精品一区二区在线播放∴| 亚洲精品欧美| 国产精品一级在线观看| 精品伊人久久久| 日韩黄色大片| 欧美特黄一区| 亚洲精品大片| 免费一级欧美片在线观看网站| 国产suv精品一区二区四区视频 | 丝袜国产日韩另类美女| 亚洲精品日韩久久| 日韩和欧美一区二区| 国产精品伊人| 成人在线视频区| 国产国产精品| 亚洲影视一区二区三区| 三级久久三级久久久| 国产精品久久久久毛片大屁完整版| 国产精品chinese| 在线中文字幕播放| 在线视频精品| 亚洲tv在线| 精品国产亚洲日本| 美女网站一区| 日韩专区欧美专区| 国产精品99精品一区二区三区∴| 福利视频一区| 亚洲综合日本| 久久精品女人| 国产视频一区三区| 久久av影院| 一区视频在线| 精品伊人久久久| 狠狠久久婷婷| 国产精品观看| 欧美日韩精品免费观看视频完整| 青青草视频一区| 日本久久成人网| 91欧美精品| 国产精品久久久久av电视剧| 鲁大师影院一区二区三区| 久久xxx视频| 国产亚洲福利| 久久中文字幕一区二区三区| 亚洲欧美日韩高清在线| 你懂的国产精品永久在线| 99久久99久久精品国产片果冰 | 九九综合九九| 国产精品欧美在线观看| 美女网站一区| 久久精品一区二区国产| 蜜臀av一区二区在线免费观看 | 99视频精品视频高清免费| 亚洲精品综合| 9999国产精品| 日本一区二区三区视频在线看| 蜜桃成人精品| 欧美日本二区| 精品一区毛片| 国产成年精品| 久久激情av| 在线精品视频一区| 日本蜜桃在线观看视频| 天堂va在线高清一区| 欧美日韩视频| 日韩高清不卡| 国产一区2区| 国产日韩欧美高清免费| 免费在线看一区| 午夜国产精品视频| 日韩高清中文字幕一区二区| 国产精品传媒麻豆hd| 日韩中文av| 天堂成人国产精品一区| 久久麻豆精品| 国产综合色区在线观看| 精品视频一区二区三区在线观看 | 久久精品影视| 国产a久久精品一区二区三区| 欧美一区91| 亚洲精品在线二区| 视频一区二区三区在线| 在线亚洲激情| 99视频一区| 一本一道久久a久久精品蜜桃| 色婷婷久久久| 国产精品久久久久久久免费观看 | 欧美日韩一区二区三区四区在线观看 | 国产日韩一区二区三区在线 | 国产精品成人一区二区网站软件| 伊人久久一区| 亚洲三级观看| 免费观看在线色综合| 免费久久99精品国产自在现线| 亚洲免费中文| 久久夜色精品| 日韩一区二区三区在线看| 日韩在线网址|