1. 
          

          1. 新聞動(dòng)態(tài)

            Vue 項目前端多語(yǔ)言方案

            網(wǎng)站建設 發(fā)布者:ou3377 2021-12-15 09:08 訪(fǎng)問(wèn)量:170

            前端的國際化是一個(gè)比較常見(jiàn)的需求。但網(wǎng)上關(guān)于這一方面的直接可用的方案卻不多。最近剛做了一版基于Vue.js的多語(yǔ)言實(shí)現,在此簡(jiǎn)單作一小結。

            一、通常有哪些內容需要處理

            總的來(lái)說(shuō),一個(gè)Web應用中,需要做多語(yǔ)言切換的內容常見(jiàn)的包括如下方面:

            1、模板中的內容,如Vue.js的<template>標簽中的文字內容

            2、JS代碼中的文字內容

            3、圖片中的文案內容

            4、頁(yè)面title

            5、第三方組件中的文案(比如,我的項目中用到了Vux的組件)

            6、后端接口中需要展示到前端的數據內容

            7、后端接口返回的錯誤提示

            二、基本思路

            1、首先,需要確定以什么樣的方式來(lái)獲取到當前應該展示何種語(yǔ)言

            我采用的是用URL傳遞?lang=en或者?lang=zh-CN這樣的傳遞參數的形式。這樣做的好處在于可以通過(guò)鏈接指定用哪種語(yǔ)言。但是,只依賴(lài)于地址欄參數也是不方便的。比如,在頁(yè)面跳轉的時(shí)候,這個(gè)地址欄參數可能就丟失了。這會(huì )導致你在頁(yè)面跳轉之后就不知道該用哪種語(yǔ)言展示了。而理想的的方式應該是,進(jìn)入某個(gè)頁(yè)面的時(shí)候帶有這個(gè)參數(這個(gè)時(shí)候就獲取到該使用何種語(yǔ)言了),等再跳轉到其它頁(yè)面的時(shí)候就不必再帶這個(gè)lang參數了,因為此時(shí)你已經(jīng)知道該用哪種語(yǔ)言了。所以,應該在一進(jìn)入第一個(gè)頁(yè)面的時(shí)候就把這個(gè)參數存下來(lái),比如,存在localstorage中,存在vuex的state中。

            這里,就引出來(lái)一個(gè)語(yǔ)言判斷的優(yōu)先級問(wèn)題。

            因為地址欄里可能有lang參數,localstorage中可能也有相關(guān)的存儲字段(因為上次訪(fǎng)問(wèn)過(guò)本應用),你可能還想設置默認的降級語(yǔ)言,等等。其優(yōu)先級應該如何處理呢?

            正確的優(yōu)先級應該是:

            先看地址欄參數中有沒(méi)有;

            再看localstorage中有沒(méi)有;

            然后再通過(guò)navigator.language獲取瀏覽器默認語(yǔ)言,看是否是你的應用所支持的語(yǔ)言,若是,則采用之;

            最后才是使用回退語(yǔ)言(例如,比較通用的英語(yǔ))。

            當然,你可以根據你的需求來(lái)做一些簡(jiǎn)化。

            2、其次,采用什么工具來(lái)解決語(yǔ)言轉換和打包的問(wèn)題?

            (1)i18n相關(guān)工具的選擇——由誰(shuí)來(lái)提供多語(yǔ)言轉換函數(通常是$t)?

            目前國際化通用方式多數基于i18n,我們也無(wú)需再去造輪子了。但就i18n的具體使用上,有很多不同的NPM模塊。比如vuex-i18n、vue-i18n、simplest-i18n等。因為多數復雜一點(diǎn)的項目都會(huì )上vuex,所以復雜一點(diǎn)的項目選擇vuex-i18n會(huì )比vue-i18n更方便。

            而simplest-i18n這個(gè)很小眾的模塊,其實(shí)也有它的好處。它支持下面這樣的寫(xiě)法:

            在模板中:

            <span>$t('真實(shí)姓名', 'Real Name')</span>

            或者在JS中:

            this.$t('真實(shí)姓名''Real Name')

            即將語(yǔ)言寫(xiě)在一起,$t函數的每一個(gè)參數都是一種語(yǔ)言,一目了然,還是比較方便閱讀的。對小項目來(lái)說(shuō),不失為一種選擇。

            其基本使用如下:

            t.js文件:

            import i18n from 'simplest-i18n';
            import getLang from '../../getLang';

            const t = i18n({
              locale: getLang.lang, // 當前語(yǔ)言
              locales: getLang.langs // 支持的語(yǔ)言列表
            });
            export default t;

            然后在應用的入口文件中對Vue.js進(jìn)行擴展:

            import t from './t';
            Vue.$t = Vue.prototype.$t = t;

            這樣就把$t這個(gè)方法掛載到了Vue.js的全局。Vue實(shí)例中也可以通過(guò)this.$t訪(fǎng)問(wèn)到,使用上還是非常簡(jiǎn)單的。

            但是,對于大項目來(lái)說(shuō),把語(yǔ)言包都寫(xiě)在代碼里面,對維護并不友好。而且,靠它也解決不了我所用到的Vux組件的多語(yǔ)言化的問(wèn)題。

            所以最終,我選擇了vuex-i18n作為基礎。

            (2)組織和處理語(yǔ)言包的工具——語(yǔ)言包怎么組織,怎么打包處理?

            對于這個(gè)問(wèn)題,我首先需要解決Vux第三方組件的多語(yǔ)言化問(wèn)題。

            首先,在語(yǔ)言包的組織方面,比較常見(jiàn)的是寫(xiě)成JSON配置文件。不過(guò),我最終采用了Yaml這種格式,它支持將多語(yǔ)言字段寫(xiě)在一起。比如:

            config.yml

            confirm:
              zh-CN: 確認
              en: confirm

            而不是像下面那樣將一個(gè)字段的多語(yǔ)言拆成幾處,比如:

            confirm: 確認
            confirm: confirm

            這樣帶來(lái)的好處就是,可以方便地對照一個(gè)字段的不同語(yǔ)言版本,而且要修改或刪除某一個(gè)字段時(shí),也可以在一處完成,無(wú)需切換。況且,Yaml文件的語(yǔ)法也更加簡(jiǎn)單明了,省去了JSON文件必須寫(xiě)雙引號、不可以出現注釋等諸多麻煩。

            其次,在語(yǔ)言包的打包方面,我找到了vux-loader。它可以和現有的webpack配置結合,不僅能完成Vux組件多語(yǔ)言配置的打包,還允許在自定義的Vue組件中使用<i18n>標簽。比如,在自定義組件中我可以這么寫(xiě):

            <i18n>
            confirm:
              zh-CN: 確認
              en: confirm
            <i18n>

            打包時(shí),vux-loader會(huì )將<i18n>標簽中的多語(yǔ)言配置信息導出至我們所配置的一個(gè)Yaml文件中,而把<i18n>標簽從我們的自定義組件中移除。

            那么,對于Yaml文件如何處理呢?可以用json-loader和yaml-loader。它們可以將Yaml文件轉換成我們所需要的json格式,方便在JS函數中使用,就像這樣:

            const componentsLocales = require('json-loader!yaml-loader!../../locales/components.yml'); // 這就得到了一個(gè)語(yǔ)言包的json格式

            3、如何通知后端接口返回何種語(yǔ)言的數據?

            因為涉及到許多接口都要通知后端采用哪種語(yǔ)言,所以,我選擇了使用header頭的方式。在axios的interceptor中給請求統一添加了header頭:Accept-Language, 并把這個(gè)值的內容設置成前端所獲得應使用的語(yǔ)言(如,zh-CN 或 en 等)。這樣,就集中在一處把這個(gè)問(wèn)題處理掉了。

            三、具體實(shí)踐中的一些細節

            1、獲取當前應該采用何種語(yǔ)言的getLang模塊的實(shí)現

            import { getQueryObj } from '../utils/url';
            import { setItem, getItem } from '../utils/storage';

            const langs = ['zh-CN''en']; // 支持哪些語(yǔ)言
            const defaultLang = 'en'// 默認語(yǔ)言,暫時(shí)并沒(méi)有對外拋出

            function getLang() {
              let queries = getQueryObj();
              let storeLang = getItem('lang');
              let rawLang;
              let flag = false;

              if (queries && queries['lang']) {
                rawLang = queries['lang'];
                setItem('lang', rawLang);
              } else {
                rawLang = storeLang || navigator.language;
              }

              langs.map(item => {
                if (item === rawLang) {
                  flag = true;
                }
              });
              return flag ? rawLang : defaultLang;
            }

            const lang = getLang(langs, defaultLang);

            export default {
                lang, // 獲取到當前語(yǔ)言
                langs // 所支持的語(yǔ)言列表
            }

            2、Vux組件的多語(yǔ)言包的配置

            可以從Vux的官方github中找到src/locales/all.yml拷貝過(guò)來(lái)(同一目錄下的src/locales/zh-CN.yml、src/locales/en.yml分別是其中文部分和英文部分),根據你自己的需要略作修改即可。

            然后在你的應用的應用的入口文件中引入:

            const vuxLocales = require('json-loader!yaml-loader!../../locales/all.yml');

            3、vux-loader的配置

            webpack.dev.conf.js中:

            resolve(vuxLoader.merge(devWebpackConfig, {
                plugins_dir: [
                    'vux-ui',
                    {
                        name'i18n',
                        vuxStaticReplacefalse,
                        staticReplacefalse,
                        extractToFiles'src/locales/components.yml',
                        localeList: ['en','zh-CN']
                    }
                ]
            }))

            webpack.prod.conf.js中:

            resolve(vuxLoader.merge(buildWebpackConfig, {
                plugins_dir: [
                    'vux-ui',
                    {
                        name'i18n',
                        vuxStaticReplacefalse,
                        staticReplacefalse,
                        extractToFiles'src/locales/components.yml',
                        localeList: ['en','zh-CN']
                    }
                ]
            }))

            其中的localeList: ['en','zh-CN']就是指定你的應用支持哪幾種語(yǔ)言。

            extractToFiles: 'src/locales/components.yml'就是指定你的自定義組件中所用到的那些<i18n>標簽中的語(yǔ)言包信息,應該導出到哪個(gè)Yaml文件中。也就是說(shuō),你在各個(gè)自定義組件中使用的<i18n>標簽中的語(yǔ)言包信息都會(huì )被vux-loader集中抽取到這個(gè)文件中。

            然后在應用的入口文件中引入這個(gè)語(yǔ)言包文件:

            const componentsLocales = require('json-loader!yaml-loader!../../locales/components.yml');

            4、自定義組件內外文案的多語(yǔ)言化

            (1)對于自定義組件內部的文案的多語(yǔ)言化信息,寫(xiě)在組件的<i18n>標簽中即可。同時(shí),為了避免不同的自定義組件中多語(yǔ)言字段的命名沖突,在每個(gè)字段的名字前面加上以組件名-式的前綴。

            (2)對于頁(yè)面的標題、一些錯誤提示等文案,它們是出現在組件之外的,因此不適合寫(xiě)在組件的<i18n>標簽中,所以我們單獨新建一個(gè)global.yml來(lái)存放這些全局性的多語(yǔ)言信息。這些內容直接寫(xiě)在global.yml中即可,并且,為了表面與其它的語(yǔ)言包字段相沖突,我們在每個(gè)字段的前面加上global-前綴。

            然后在應用的入口文件中引入這個(gè)語(yǔ)言包文件:

            const componentsLocales = require('json-loader!yaml-loader!../../locales/global.yml');

            5、vuex-i18n的實(shí)現

            在src/store/index.js文件中:

            import VuexI18n from 'vuex-i18n';

            export default new Vuex.Store中增加:

                i18n: VuexI18n.store

            在應用的入口文件中:

            import VuexI18n from 'vuex-i18n';
            import getLang from '../../getLang';

            Vue.use(VuexI18n.plugin, store);

            const vuxLocales = require('json-loader!yaml-loader!../../locales/all.yml');
            const componentsLocales = require('json-loader!yaml-loader!../../locales/components.yml');

            const finalLocales = {
              'en'Object.assign(vuxLocales['en'], componentsLocales['en']),
              'zh-CN'Object.assign(vuxLocales['zh-CN'], componentsLocales['zh-CN'])
            }

            for (let i in finalLocales) {
              Vue.i18n.add(i, finalLocales[i])
            }

            Vue.i18n.set(globalVars.lang);

            6、圖片的多語(yǔ)言化

            對于圖片中的文案信息,多語(yǔ)言化主要有這么兩種方式:一是根據不同的語(yǔ)言展示不同的圖片;二是盡將文字從圖片背景中分離出來(lái),采用文字層加背景圖片層的方式,這樣文字層就可以作為普通文本來(lái)實(shí)現多語(yǔ)言化了。都比較簡(jiǎn)單,不再贅述。

            7、在當前頁(yè)面通過(guò)按鈕切換當前語(yǔ)言后,如何更新當前頁(yè)面的內容?

            如果你的應用并不需要在頁(yè)面內部切換語(yǔ)言版本,那么直接通過(guò)URL中傳入不同的lang參數就可以了,并不涉及到此問(wèn)題。

            第一種方式:刷新頁(yè)面

            <button @click="changeLang('zh-CN')">中文</button>
            <button @click="changeLang('en')">英文</button>
            changeLang(lang){
                location.href = this.$utils.url.replaceParam(this.$router.history.current.path, 'lang', lang);
            },

            第二種方式:watch當前頁(yè)data中lang字段的變化,通過(guò)v-if局部刷新某些相關(guān)組件:

            data(){
                return {
                    langthis.$i18n.locale()
                }
            }

            changeLang(lang){
                this.$i18n.set(lang);
                this.lang = this.$i18n.locale();
            },

            watch: {
                lang(newVal, oldVal) {
                    if(newVal === oldVal) {
                        return;
                    }

                    // 在這里通過(guò)改變某個(gè)標志位 結合 v-if 來(lái)觸發(fā)某個(gè)局部組件的重新渲染
                }
            }

            第三種方式:結合vuex派發(fā)全局的語(yǔ)言狀態(tài),接收到狀態(tài)變化時(shí)進(jìn)行更新,或者自己簡(jiǎn)單地改寫(xiě)vuex-i18n的實(shí)現。這種方式相對復雜一些。

            具體根據自己的業(yè)務(wù)需求選擇。

            8、Yaml中特殊字符的轉義

            對于一些包含特殊字符的yaml鍵值,比如[、]等,需要進(jìn)行轉義。轉的方式是給鍵值加上單引號引起來(lái)。

            如果你的語(yǔ)言包信息中有單引號,則必須連續使用兩個(gè)單引號轉義。例如:

            str: 'labor''s day'



            關(guān)鍵字: Vue 項目

            文章連接: http://www.gostscript.com/wzjss/820.html

            版權聲明:文章由 晨展科技 整理收集,來(lái)源于互聯(lián)網(wǎng)或者用戶(hù)投稿,如有侵權,請聯(lián)系我們,我們會(huì )立即刪除。如轉載請保留

            双腿国产亚洲精品无码不卡|国产91精品无码麻豆|97久久久久久久极品|无码人妻少妇久久中文字幕
                1.