Vite3 - Vue3 - TS - Pinia - Naive UI 項目實戰
因爲項目是給設在新加坡、馬來西亞的海外自提站使用的後臺,所以國際化的配置必不可少。需要切換語言的內容主要分爲兩部分:
-
我們自己添加的文案內容,需要用到 vue-i18n-next;
-
naive ui 的組件,可以使用
n-config-provider
配置。
vue-i18n-next
安裝
vue3 中使用 i18n 需要安裝的是 vue-i18n v9[3] 的版本:
npm install vue-i18n@9
複製代碼
配置 i18n
創建 src\lang\index.ts,使用 createI18n
創建 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:默認爲
true
,但如果使用的是 Composition API 則需要定義爲false
,否則會報類似下面的錯誤:
- locale:當前要展示的語言。值爲之前用戶的語言選擇,從瀏覽器緩存中讀取。如果緩存中沒有數據,則通過
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:不同語言對應的翻譯文件:
中文翻譯包:
// 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>
中使用
下面以側邊導航菜單中,對每條導航的名稱的配置爲例。從 vue-i18n 中導入 useI18n
,然後進行調用生成 i18n
實例,再從裏面解構得到 t
方法,在第 6 行進行使用 —— t(route.meta.title)
,route.meta.title
是我在路由裏配置的內容,其值就是在語言翻譯文件中定義的那些 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 >{{ $t('sousuo') }}</slot>
</n-button>
</template>
複製代碼
這裏之所以可以直接使用 $t
是因爲有個叫做 globalInjection
的配置項默認爲 true
,幫我們全局注入了 $t
方法,如果設置爲 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'),
// ...
})
}
複製代碼
至此,項目裏我們自己添加的內容的國際化就配置完成了,接下來是對 naive ui 組件的國際化配置。
naive ui 組件
對於 naive ui 的組件,默認情況下均爲英語,如果想改爲中文,需要在根組件 src\App.vue 中使用 n-config-provider 對語言進行配置,將用於設置全局語言的 locale
設爲從 naive ui 導入的zhCN
,設置全局日期語言的 date-locale
爲從 naive ui 導入的 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
複製代碼
當 language
爲 'en'
時,locale
和 dateLocale
就爲 null
;當 language
爲 'zh-Hans'
時,locale
和 dateLocale
就爲 zhCN
和 dateZhCN
。而 language
的值由前面已經介紹過了的 getLanguage()
獲取,首先是從瀏覽器緩存讀取用戶之前的選擇,而用戶是通過下面封裝的組件進行語言選擇的。
切換語言控件
我選擇結合使用 naive ui 的 Popselect(彈出選擇)和 Icon(圖標)組件來實現切換語言的控件:
<!-- src\components\LangSelect\LangSelect.vue -->
<template>
<div>
<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 = '中文'
// 獲取當前語言
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
的默認值根據存儲在 pinia 的 appStore
的 state language
決定。這裏第 13 行使用 pinia 提供的 storeToRefs
處理是爲了在解構獲取 language
時保留其響應式,其實用 vue3 的 toRefs
也可以。
切換時觸發的 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) {
// 獲取當前語言
const appStore = useAppStore()
const { language } = storeToRefs(appStore)
i18n.global.locale.value = lang // 修改 i18n
language.value = lang // 修改 pinia
localCache.setItem(LANGUAGE, lang) // 修改緩存
}
複製代碼
圖標的使用
順便說明下 naive ui 中圖標的使用,圖標選自 naive ui 文檔推薦的 xicons 圖標庫,使用前需要根據所選用的圖標做對應的安裝:
npm i -D @vicons/ionicons5
複製代碼
然後進行引入,並作爲 <n-icon>
的默認插槽內容使用:
<!-- 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__
的值由默認的 true
改爲了 false
,其餘兩項則維持默認值。
效果
至此,本項目的國際化配置就完成了。最後來看一下效果(目前只做了部分文案的初步翻譯):
關於本文
作者:亦黑迷失
https://juejin.cn/post/7174423843891576840
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/7j-Z80NXP--lYyyEE5WhCA