成人在线亚洲_国产日韩视频一区二区三区_久久久国产精品_99国内精品久久久久久久

您的位置:首頁技術(shù)文章
文章詳情頁

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

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

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

每種語言單獨(dú)開發(fā)頁面,適用于 CMS 之類的網(wǎng)站 多語言文本和頁面結(jié)構(gòu)分離,運(yùn)行時(shí)動(dòng)態(tài)替換。適用于單頁應(yīng)用(SPA) 直接用網(wǎng)頁翻譯插件,機(jī)器翻譯。這種效果不太理想,同時(shí)有一些局限性(后面會(huì)講到)

問題

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

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

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

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

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

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

多語言配置映射表

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

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

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

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

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

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

對(duì)象的結(jié)構(gòu)大致如下:

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

操作 DOM

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

mounted() { this.observeDOM(document.body);},methods: { observeDOM(el) { let mutationTimer; const vm = this; const observer = new MutationObserver(() => { // 類似于 debounce 的效果,多次調(diào)用合并為一次 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é)點(diǎn)的變動(dòng) subtree: true, // 監(jiān)視node所有后代的變動(dòng) attributes: true, // 監(jiān)視node屬性的變動(dòng) characterData: true, // 監(jiān)視指定目標(biāo)節(jié)點(diǎn)或子節(jié)點(diǎn)樹中節(jié)點(diǎn)所包含的字符數(shù)據(jù)的變化。 }; if (this.language === ’en’) { observer.observe(el, options); } },}

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

翻譯

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

const keys = Object.keys(words);// 提前緩存正則,避免重復(fù)執(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,會(huì)導(dǎo)致Vue響應(yīng)式失效 // 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); }); });}

改進(jìn)后的 DOM 操作

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

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

以上就是vue項(xiàng)目實(shí)現(xiàn)多語言切換的思路的詳細(xì)內(nèi)容,更多關(guān)于vue項(xiàng)目多語言切換的資料請(qǐng)關(guān)注好吧啦網(wǎng)其它相關(guān)文章!

標(biāo)簽: Vue
相關(guān)文章:
成人在线亚洲_国产日韩视频一区二区三区_久久久国产精品_99国内精品久久久久久久
福利一区福利二区| 亚洲精品国产第一综合99久久 | 中文字幕不卡在线| 福利一区二区在线观看| 91精选在线观看| 国产一区二区美女| 欧美日韩一区久久| 久久精品免费看| 欧美日韩一区国产| 久久精品国产亚洲a| 色婷婷久久综合| 一区二区视频在线| 日韩亚洲国产欧美| 依依成人综合视频| 国产精品一区二区你懂得| 夜夜精品视频一区二区| 91久久视频| 一区二区三区日韩在线观看| 国产欧美精品| 亚洲高清在线视频| 久久精品国产第一区二区三区最新章节| 亚洲一区二区三区中文字幕| 一区二区冒白浆视频| 亚洲国产精品一区二区久久恐怖片 | 亚洲一区日韩在线| 亚洲成av人**亚洲成av**| 亚洲中字在线| 日韩二区在线观看| 欧美日韩中文字幕精品| 国产一区二区精品久久99| 日韩精品在线一区| 91在线云播放| 亚洲婷婷国产精品电影人久久| 国产欧美日韩一区二区三区| 日韩中文字幕亚洲一区二区va在线| 在线视频亚洲一区| 国产精品亚洲专一区二区三区| 欧美一区二区三区免费视频 | 91无套直看片红桃| 日本一区二区三区免费乱视频| 国产精品草草| 一区二区三区欧美在线观看| 91久久线看在观草草青青| 国内精品第一页| 日韩精品一区二区在线观看| 欧美国产三区| 亚洲一区在线电影| 日本电影亚洲天堂一区| 国产一区二区在线视频| 精品久久久久久最新网址| 欧美一区三区二区在线观看| 日韩一区欧美一区| 色噜噜狠狠一区二区三区果冻| 国产在线麻豆精品观看| www国产精品av| 欧美日韩免费观看一区| 一区二区三区精品视频| 欧美在线free| www.亚洲精品| 亚洲欧美日韩电影| 欧美最猛黑人xxxxx猛交| 高清国产一区二区三区| 国产精品久久久久久久久免费相片 | 亚洲一区二区视频| 欧美日韩精品一区二区三区四区| 丰满放荡岳乱妇91ww| 国产清纯白嫩初高生在线观看91 | 亚洲精品日韩在线观看| 日本免费在线视频不卡一不卡二| 欧美一级淫片007| 国内精品久久国产| 蜜芽一区二区三区| 精品久久久久久亚洲综合网 | 久久久不卡影院| 国产亚洲精品自拍| 国产综合久久久久久久久久久久 | 欧美精品18+| 国产一区激情| 日韩精品高清不卡| 国产亚洲精品超碰| 亚洲一区二区三区涩| 国产伦理精品不卡| 亚洲欧洲精品一区二区精品久久久| 麻豆精品传媒视频| av一区二区久久| 亚洲图片欧美视频| 欧美岛国在线观看| 亚洲一区尤物| 成人激情电影免费在线观看| 亚洲综合色网站| 日韩欧美高清dvd碟片| 国内精品久久久久久久影视蜜臀| 天天色天天操综合| 久久久亚洲午夜电影| 免费日韩视频| 99久久久无码国产精品| 天天操天天色综合| 2024国产精品| 日本高清免费不卡视频| 国户精品久久久久久久久久久不卡| 奇米综合一区二区三区精品视频| 国产亚洲欧美激情| 欧美私模裸体表演在线观看| 国产精品国产三级欧美二区| 久久9热精品视频| 成人免费在线视频观看| 正在播放亚洲一区| 国产精品乱码一区二区三区| 92精品国产成人观看免费 | 午夜精品免费| 国产精品99久久不卡二区| 洋洋av久久久久久久一区| 久久一区二区三区四区| 欧美体内she精高潮| 一本色道久久99精品综合| 国产.欧美.日韩| 天涯成人国产亚洲精品一区av| 亚洲国产精品高清| 欧美美女网站色| 宅男噜噜噜66国产日韩在线观看| 成人综合在线观看| 日本欧美肥老太交大片| 亚洲精选免费视频| 国产日韩欧美高清在线| 3atv一区二区三区| 色婷婷av一区二区三区gif| 亚洲精品九九| 欧美激情成人在线| 国产精品亚洲一区二区三区在线| 午夜精彩视频在线观看不卡| 国产精品乱码一区二区三区软件| 欧美成人乱码一区二区三区| 色偷偷一区二区三区| 亚洲色诱最新| 国内一区二区三区在线视频| 成a人片国产精品| 精品一区二区三区在线播放视频| 亚洲va欧美va人人爽| 亚洲人成亚洲人成在线观看图片| 久久久久久久免费视频了| 777xxx欧美| 欧美做爰猛烈大尺度电影无法无天| 国产精品久久久对白| 亚洲香蕉网站| 欧美日韩国产亚洲一区| 97精品久久久久中文字幕| 丰满放荡岳乱妇91ww| 国产一区日韩二区欧美三区| 日韩精品一二三| 亚洲愉拍自拍另类高清精品| 欧美国产欧美亚州国产日韩mv天天看完整 | 日韩不卡一区二区三区| 亚洲一区二区三区影院| 亚洲男人的天堂一区二区| 国产欧美一区二区精品性色超碰 | 亚洲一区二区欧美| 综合亚洲深深色噜噜狠狠网站| 久久精品视频在线免费观看| 日韩精品一区二区三区中文精品| 555夜色666亚洲国产免| 欧美午夜影院一区| 日本韩国一区二区三区视频| 久久综合导航| 久久另类ts人妖一区二区| 亚洲视频二区| 国产欧美日韩| 亚洲久久一区二区| 99国产精品视频免费观看一公开| 亚洲高清在线播放| 亚洲激情专区| 日韩视频二区| 中文国产一区| 亚洲视频二区| 久久xxxx精品视频| 色婷婷av一区二区三区gif| 久久综合伊人77777麻豆| 国产伦精品一区二区三区照片91 | 国产一区二区三区黄| 精品国产亚洲一区二区三区在线观看 | 一区二区三区视频在线播放| 久久嫩草精品久久久精品一| 久久精品亚洲麻豆av一区二区| 欧美日韩一区二区国产| 欧美精品在线观看播放| 欧美欧美午夜aⅴ在线观看| 欧美日韩国产高清一区二区| 欧美日韩一级视频| 欧美日韩aaa| 日韩视频免费直播| 26uuu亚洲婷婷狠狠天堂| 国产午夜精品久久久久久久 | 一区二区精品在线观看| 妖精视频成人观看www| 亚洲欧美日韩精品一区二区| 久久精品一区| 欧美日韩大片一区二区三区| 在线看片一区| 久久亚洲色图| 欧美午夜精品久久久久久孕妇| 欧美一区二区黄色|