你也許不知道的Vuejs - 最佳實踐(3)

you-may-not-know-vuejs.png

by yugasun from https://yugasun.com/post/you-may-not-know-vuejs-15.html
本文可全文轉載,但需要保留原作者和出處。

相信大多數(shù)使用 Vue 項目都會面臨國際化的問題,而 vue-i18n 便是國際化的不二之選,它用起來非常簡單,但是同時也會帶來一些問題和挑戰(zhàn)。本篇是個人在項目上國際化時一些經(jīng)驗的總結,希望能在國際化的道路上幫到你。

基本使用

vue-i18n 官方文檔介紹的很清楚,先在 src/lang 目錄下分別創(chuàng)建三個文件 index.js,zh.jsen.js,
然后在 index.js 中創(chuàng)建 i18n 實例并導出,供 Vue 使用,代碼如下:

import Vue from 'vue';
import VueI18n from 'vue-i18n';

import zhMsg from './zh';
import enMsg from './en';

Vue.use(VueI18n);

const messages = {
  zh: {
    ...zhMsg,
  },
  en: {
    ...enMsg,
  },
};

const i18n = new VueI18n({
  locale: 'zh',
  messages,
});

export default i18n;

接著編寫語言包文件:

// zh.js
export default {
  index: {
    header: {
      title: 'Vue-I18n 示例',
      subtitle: '你將學會如何使用 vue-i18n',
    },
  },
};

// en.js
export default {
  index: {
    header: {
      title: 'Vue-I18n Demo',
      subtitle: 'You will learn how to use vue-i18n',
    },
  },
};

接下來在入口文件 src/main.js 文件中引入:

// ...
import i18n from './lang';

// ...
new Vue({
  i18n,
  router,
  el: '#app',
  template: '<App/>',
  components: { App },
});

到這里就配置好了,于是我們就可以在組件中使用了,使用方法如下 src/views/index.vue;

<h1>{{ $t('index.header.title') }} </h1>
<h3>{{ $t('index.header.subtitle') }} </h3>

看起來是不是很簡單,引入 vue-i18n 后,它會在 Vue 實例上掛載它提供的國際化的 api,比如 $t、$tc、$td 等格式化函數(shù)。使用時我們只需要在組件中直接調用就行,比如:this.$t。

$t 接受兩個參數(shù),第一個為我們在語言包中訪問的屬性路徑,各級屬性用 . 符號鏈接,第二個參數(shù)可以動態(tài)傳參,比如我們將 index.header.subtitle 的模板修改為 You will learn how to use {name},那么我們可以像下面這樣使用:

$t('index.header.subtitle', {name: 'vue-i18n})

小技巧

獲取鍵值集合

相信大家已經(jīng)知道 vue-i18n 如何使用了,建議通讀 官方文檔,自己動手配置熟悉下。

大多數(shù)情況下,我們只需要使用 $t 就可以滿足我們的需求了。但是當我們的的語言包層級越來越深時,你會發(fā)現(xiàn)屬性路徑越來越長,而且在某些組件內,需要書寫很多遍,比如上面的 index.header.titleindex.header.subtitle,它們的前綴都是 index.header,在這里寫兩遍我們能接受,如果是100遍呢?作為一名懶惰的程序員,怎么能接受同樣的事情做3遍呢,更何況是100遍,簡直不敢想象!

其實我們可以先獲取前綴的對象集合,然后通過這個集合對象來訪問,節(jié)省臃長的前綴的重復書寫,使用的時候我們需要先在 src/views/index.vue 組件的 computed 中定義國際化集合:

export default {
  name: 'Index',
  computed: {
    indexMsg() {
      return this.$t('index.header')
    }
  },
};

接下來修改下模板中的書寫方式:

<h1>{{ indexMsg.title }}</h1>
<h3>{{ indexMsg.subtitle }} </h3>

注意:這里必須定義為計算屬性,不然在切換語言時,視圖國際化將無法自動更新。

多元化的消息

曾經(jīng)在做一個登錄錯誤信息提示的時候,遇到個需求:用戶多次輸入錯誤后,會提示多長時間后重試,但是接口只是返回個剩余秒數(shù),需要根據(jù)這個秒數(shù)計算出剩余的時分秒,當大于1小時時,提示 請 xx 小時后重試,當小于1小時時,提示 請 xx 分 xx 秒后重試。一般碰到這種需求,我們是肯定需要結合模板變量來實現(xiàn),最簡單的方式就是直接定義兩個國際化鍵值,比如 msg1,msg2,然后通過計算出的小時數(shù)來做 if 判斷就行。

但是當項目越來越龐大,項目中類似的需求越來越多的時候,你會發(fā)現(xiàn)你的語言包的鍵值對越來越多,到了最后,取個屬性名就要想半天,不知道大家有沒有跟我類似的痛點,所以我對于這種類似需求用 $tc 函數(shù)來實現(xiàn)的。

$tc 函數(shù)允許我們一個國際化鍵值可以通過管道符 | 來分割多元化的信息,如下:

export default {
  // ...
  login: {
    tips: '請{minute}分{second}秒后再重試 | 請{hour}小時后再重試',
  },
};

然后在模板中使用:

<p>{{ $tc('login.tips', 1, {minute: 30, second: 29}) }}</p>
<p>{{ $tc('login.tips', 2, {hour: 1}) }}</p>

對于上面的需求,我只需要根據(jù)接口返回的秒數(shù),計算下 hour 值,然后寫一個三目運算就解決了:

hour >= 1 ? this.$tc('login.tips', 2, {hour: 1}) : this.$tc('login.tips', 1, {minute: 30, second: 29})

可選數(shù)組消息

其實對于可選的國際化消息需求,我們還可以通過數(shù)組來實現(xiàn),比如我們的某個訂單狀態(tài)需要國際化,0-5 分別對應不同的狀態(tài),我們只需要定義簡單的數(shù)組就可以搞定了。

首先定義語言包:

export default {
  //...
  order: {
    status: [
      '待付款',
      '待發(fā)貨',
      '已發(fā)貨',
      '已簽收',
      '已取消',
    ],
  },
};

然后我們只需要根據(jù)不同狀態(tài),來讀取鍵值就行:

<span>{{ $t(`order.status[orderStatus]`) }}</span>

<script>
export default {
  name: 'Index',
  data() {
    return {
      orderStatus: 3,
    };
  },
  //...
};
</script>

當然這里也可以通過多元化的方式來實現(xiàn),大家可以自行嘗試下。

模塊化

隨著項目越來越大,你會發(fā)現(xiàn) src/lang/zh.js 文件越來越臃腫,每次改個國際化文案,需要在上千行的對象中,找半天對應的鍵值對,而且那深不可測的屬性層級,著實讓人眼花繚亂。

既然 src/lang/zh.jsjs 文件,我們當然就可以對其進行拆分,然后把不同的模塊拆分成獨立的 js 文件進行維護,是不是找起來會輕松好多,比如我們嘗試將實例中的 zh.js 產(chǎn)分成 src/lang/zh-modules/index.js、src/lang/zh-modules/login.jssrc/lang/zh-modules/order.js 三個模塊文件:

// index.js
export default {
  index: {
    header: {
      title: 'Vue-I18n 示例',
      subtitle: '你將學會如何使用 vue-i18n',
    },
  },
};


// login.js
export default {
  login: {
    tips: '請{minute}分{second}秒后再重試 | 請{hour}小時后再重試',
  },
};

// order.js
export default {
  order: {
    status: [
      '待付款',
      '待發(fā)貨',
      '已發(fā)貨',
      '已簽收',
      '已取消',
    ],
  },
};

然后在 src/lang/zh.js 文件中一次引入:

import index from './zh-modules/index';
import login from './zh-modules/login';
import order from './zh-modules/order';

export default {
  ...index,
  ...login,
  ...order,
};

這樣就變得清晰很多,下次產(chǎn)品需要你修改登錄相關的文案,你只需要修改 login.js 模塊就行。

但是按模塊拆分后,有人就會有疑問了,我這么拆分后,語言模塊文件會隨著語言種類成倍數(shù)增加,應該怎么辦?

這里就說說我的解決辦法,就是將語言文件按模塊合并。所謂 天下大勢,合久必分,分久必合。我們將相同的模塊按文件合并起來就可以了,比如 index 模塊:

const zh = {
  index: {
    header: {
      title: 'Vue-I18n 示例',
      subtitle: '你將學會如何使用 vue-i18n',
    },
  },
};
const en = {
  index: {
    header: {
      title: 'Vue-I18n Demo',
      subtitle: 'You will learn how to use vue-i18n',
    },
  },
}
export default {
  zh,
  en,
};

然后只需要在 src/lang/index.js 引入,稍作修改就行:

import Vue from 'vue';
import VueI18n from 'vue-i18n';

import indexMsg from './modules/index';
import loginMsg from './modules/login';
import orderMsg from './modules/order';

Vue.use(VueI18n);

const messages = {
  zh: {
    ...indexMsg.zh,
    ...loginMsg.zh,
    ...orderMsg.zh,
  },
  en: {
    ...indexMsg.en,
    ...loginMsg.en,
    ...orderMsg.en,
  },
};

const i18n = new VueI18n({
  locale: 'zh',
  messages,
});

export default i18n;

此時你會發(fā)現(xiàn)我們已經(jīng)不需要 src/lang/zh.jssrc/lang/en.js 文件了。而且這里有個好處是,我們在讓翻譯幫忙的事后,只需要在同一個文件中對我們的相同鍵值進行修改和編輯了,是不是方便很多,趕緊動手嘗試下吧~

總結

本文只是個人在開發(fā)過程中,關于國際化方案的經(jīng)驗總結,我相信大家也有很多提高效率的方案。希望本篇能夠解決你在開發(fā)中遇到的一些疑惑和痛點,也非常歡迎評論或來信交流你的優(yōu)化方案。

源碼在此

專題目錄

You-May-Not-Know-Vuejs

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內容