Vite3+Vue3+ts+pinia+NaiveUI項目實戰(zhàn)--國際化配置
作者:?亦黑迷失 https://juejin.cn/post/7174423843891576840
因為項目是給設(shè)在新加坡、馬來西亞的海外自提站使用的后臺,所以國際化的配置必不可少。需要切換語言的內(nèi)容主要分為兩部分:
- 我們自己添加的文案內(nèi)容,需要用到 vue-i18n-next;
-
naive ui 的組件,可以使用
n-config-provider配置。
安裝
vue3 中使用 i18n 需要安裝的是 vue-i18n v9[1] 的版本:
npm install vue-i18n@9
配置 i18n
創(chuàng)建 src\lang\index.ts,使用 createI18n 創(chuàng)建 i18n 實例:
//?src\lang\index.ts
import?{?createI18n?}?from?'vue-i18n'
import?{?LANG_VALUE?}?from?'@/common/enum'
import?zhHans?from?'./zh-Hans'
import?en?from?'./en'
const?i18n?=?createI18n({
??legacy:?false,
??locale:?getLanguage(),
??messages:?{
????[LANG_VALUE.Zh]:?zhHans,
????[LANG_VALUE.En]:?en
??}
})
export?default?i18n
下面對傳入 createI18n() 的配置對象做些說明:
-
legacy:默認(rèn)為
true,但如果使用的是 Composition API 則需要定義為false,否則會報類似下面的錯誤:

-
locale:當(dāng)前要展示的語言。值為之前用戶的語言選擇,從瀏覽器緩存中讀取。如果緩存中沒有數(shù)據(jù),則通過
navigator.language獲取瀏覽器使用的語言:
//?src\lang\index.ts
import?{?localCache?}?from?'@/utils'
export?function?getLanguage()?{
??const?chooseLanguage?=?localCache.getItem(LANGUAGE)
??if?(chooseLanguage)?return?chooseLanguage
??//?如果沒有選擇語言
??const?language?=?navigator.language.toLowerCase()
??const?locales?=?[LANG_VALUE.En,?LANG_VALUE.Zh]
??for?(const?locale?of?locales)?{
????if?(language.indexOf(locale)?>?-1)?{
??????return?locale
????}
??}
??return?LANG_VALUE.Zh
}
因為 locale 值在多個文件中有使用到,所以我使用了 ts 新增類型 enum(枚舉類型)來保存:
//?src\common\enum.ts
export?enum?LANG_VALUE?{
??En?=?'en',
??Zh?=?'zh-Hans'
}
- messages:不同語言對應(yīng)的翻譯文件:
中文翻譯包:
//?src\lang\zh-Hans.ts
export?default?{
??baoguochuku:?'包裹出庫',
??sousuo:?'搜索'
}
英文翻譯包:
//?src\lang\en.ts
export?default?{
??baoguochuku:?'Outbound',
??sousuo:?'Search'
}
在 main.ts 引入
//?src\main.ts,省略其它代碼
import?i18n?from?'./lang/index'
app.use(i18n)
在文件中使用
vue 文件
-
在
<script lang="ts" setup>中使用
下面以側(cè)邊導(dǎo)航菜單中,對每條導(dǎo)航的名稱的配置為例。從 vue-i18n 中導(dǎo)入 useI18n,然后進(jìn)行調(diào)用生成 i18n 實例,再從里面解構(gòu)得到 t 方法,在第 6 行進(jìn)行使用 —— t(route.meta.title),route.meta.title 是我在路由里配置的內(nèi)容,其值就是在語言翻譯文件中定義的那些 key,比如 'baoguochuku' 等:
<!-- src\components\Navigation\Navigation.vue,省略其它代碼 -->
<script lang="ts" setup>
import { useI18n } from 'vue-i18n'
const { t } = useI18n()
const menuOptions: MenuOption[] = modulesRoutes.map(route => ({
label: () => t(route.meta.title),
// ...
}))
</script>
這里注意下 label 的值得寫成 getter 的形式,如果是寫成 label: t(route.meta.title) 這樣直接賦值,則切換語言時是沒有效果的。
-
在
<template>中使用
使用方法與之前在 vue2 中一樣:
<template>
<n-button>
<slot name="submitBtnText">{{ $t('sousuo') }}</slot>
</n-button>
</template>
這里之所以可以直接使用 $t 是因為有個叫做 globalInjection 的配置項默認(rèn)為 true,幫我們?nèi)肿⑷肓?$t 方法,如果設(shè)置為 false:
//?src\lang\index.ts
const?i18n?=?createI18n({
??//?...
??globalInjection:?false
})
則會報錯:
ts 文件
在 ts 文件中,則需要引入我們之前在 src\lang\index.ts 配置生成的 i18n 實例,然后通過 i18n.global.t() 來定義文案:
//?src\service\request\index.ts
import?i18n?from?'@/lang'
if?(!axios.isCancel(error))?{
??dialog.error({
????title:?i18n.global.t('tishi'),
????//?...
??})
}
至此,項目里我們自己添加的內(nèi)容的國際化就配置完成了,接下來是對 naive ui 組件的國際化配置。
naive ui 組件對于 naive ui 的組件,默認(rèn)情況下均為英語,如果想改為中文,需要在根組件 src\App.vue 中使用 n-config-provider 對語言進(jìn)行配置,將用于設(shè)置全局語言的 locale 設(shè)為從 naive ui 導(dǎo)入的zhCN ,設(shè)置全局日期語言的 date-locale 為從 naive ui 導(dǎo)入的 dateZhCN:
<!-- src\App.vue -->
<script setup lang="ts">
import { storeToRefs } from 'pinia'
import useAppStore from '@/stores/app'
const appStore = useAppStore()
const { locale, dateLocale } = storeToRefs(appStore)
</script>
<template>
<n-config-provider
:locale="locale"
:date-locale="dateLocale"
>
<RouterView />
</n-config-provider>
</template>
locale 和 dateLocale 作為由 pinia 管理的 getter 定義在 src\stores\app.ts:
//?src\stores\app.ts
import?{?defineStore?}?from?'pinia'
import?{?zhCN,?dateZhCN?}?from?'naive-ui'
import?{?LANG_VALUE?}?from?'@/common/enum'
import?{?getLanguage?}?from?'@/lang'
interface?IAppState?{
??language:?string
??//?...
}
const?useAppStore?=?defineStore('app',?{
??state:?():?IAppState?=>?({
????language:?getLanguage(),
????//?...
??}),
??getters:?{
????locale(state)?{
??????switch?(state.language)?{
????????case?LANG_VALUE.En:
??????????return?null
????????case?LANG_VALUE.Zh:
??????????return?zhCN
????????default:
??????????break
??????}
????},
????dateLocale(state)?{
??????switch?(state.language)?{
????????case?LANG_VALUE.En:
??????????return?null
????????case?LANG_VALUE.Zh:
??????????return?dateZhCN
????????default:
??????????break
??????}
????}
??},
??//?...
})
export?default?useAppStore
當(dāng) language 為 'en' 時,locale 和 dateLocale 就為 null;當(dāng) language 為 'zh-Hans' 時,locale 和 dateLocale 就為 zhCN 和 dateZhCN。而 language 的值由前面已經(jīng)介紹過了的 getLanguage() 獲取,首先是從瀏覽器緩存讀取用戶之前的選擇,而用戶是通過下面封裝的組件進(jìn)行語言選擇的。
我選擇結(jié)合使用 naive ui 的 Popselect(彈出選擇)和 Icon(圖標(biāo))組件來實現(xiàn)切換語言的控件:
<!-- src\components\LangSelect\LangSelect.vue -->
<template>
<div class="lang-select">
<n-popselect
v-model:value="value"
:options="options"
trigger="click"
@update:value="handleUpdateValue"
>
<n-icon>
<Language />
</n-icon>
</n-popselect>
</div>
</template>
value 和 options 定義如下:
<!-- src\components\LangSelect\LangSelect.vue -->
<script lang="ts" setup>
import { ref } from 'vue'
import { storeToRefs } from 'pinia'
import useAppStore from '@/stores/app'
import { LANG_VALUE } from '@/common/enum'
import type { SelectOption, SelectGroupOption } from 'naive-ui'
const ENGLISH = 'English'
const ZHONG_WEN = '中文'
// 獲取當(dāng)前語言
const appStore = useAppStore()
const { language } = storeToRefs(appStore)
// 彈出選擇 Popselect
type valueType = string | null
const value = ref<valueType>(getValue())
function getValue() {
switch (language.value) {
case LANG_VALUE.En:
return ENGLISH
case LANG_VALUE.Zh:
return ZHONG_WEN
default:
return ZHONG_WEN
}
}
const options: Array<SelectOption | SelectGroupOption> = [
{
label: ENGLISH,
value: ENGLISH
},
{
label: ZHONG_WEN,
value: ZHONG_WEN
}
]
</script>
選項 options 就兩個:'English' 或 '中文'。value 的默認(rèn)值根據(jù)存儲在 pinia 的 appStore 的 state language 決定。這里第 13 行使用 pinia 提供的 storeToRefs 處理是為了在解構(gòu)獲取 language 時保留其響應(yīng)式,其實用 vue3 的 toRefs 也可以。
切換時觸發(fā)的 handleUpdateValue 定義如下:
<!-- src\components\LangSelect\LangSelect.vue -->
<script lang="ts" setup>
import { setLocale } from '@/lang'
let lang: langType
function handleUpdateValue(value: valueType) {
switch (value) {
case ENGLISH:
lang = LANG_VALUE.En
break
case ZHONG_WEN:
lang = LANG_VALUE.Zh
break
default:
break
}
setLocale(lang)
}
</script>
將切換后的 value 傳給定義于 src\lang\index.ts(也就是前面配置 i18n 的文件) 的 setLocale() 處理:
//?src\lang\index.ts
export?function?setLocale(lang:?langType)?{
??//?獲取當(dāng)前語言
??const?appStore?=?useAppStore()
??const?{?language?}?=?storeToRefs(appStore)
??i18n.global.locale.value?=?lang?//?修改?i18n
??language.value?=?lang?//?修改?pinia
??localCache.setItem(LANGUAGE,?lang)?//?修改緩存
}
圖標(biāo)的使用
順便說明下 naive ui 中圖標(biāo)的使用,圖標(biāo)選自 naive ui 文檔推薦的 xicons 圖標(biāo)庫,使用前需要根據(jù)所選用的圖標(biāo)做對應(yīng)的安裝:
npm i -D @vicons/ionicons5
然后進(jìn)行引入,并作為 <n-icon> 的默認(rèn)插槽內(nèi)容使用:
<!-- src\components\LangSelect\LangSelect.vue -->
<script lang="ts" setup>
import { Language } from '@vicons/ionicons5'
</script>
<template>
<n-icon>
<Language />
</n-icon>
</template>
解決控制臺警告
做到這一步,如果打開瀏覽器控制臺,可能會看到如下警告:

解決辦法是在 vite.config.ts 添加如下配置:
export?default?defineConfig({
??//?...
??define:?{
????__VUE_I18N_FULL_INSTALL__:?true,
????__VUE_I18N_LEGACY_API__:?false,
????__INTLIFY_PROD_DEVTOOLS__:?false
??}
})
因為項目里都是使用 Composition API,無需啟用對選項式 api 的支持,所以 __VUE_I18N_LEGACY_API__ 的值由默認(rèn)的 true 改為了 false,其余兩項則維持默認(rèn)值。
至此,本項目的國際化配置就完成了。最后來看一下效果(目前只做了部分文案的初步翻譯):

參考資料
[1]vue-i18n v9: https://github.com/intlify/vue-i18n-next
前端大學(xué) 公眾號 祝 您:2023 年暴富!萬事如意! 分享前端干貨,點贊就是最大的支持,
比心??
