Vue3+ts+element-plus实现管理后台前端项目国际化
element-plus
element-plus/element-plus: Element Plus 是一个基于 Vue 3 的组件库,提供了丰富且易于使用的 UI 组件,用于快速搭建企业级桌面和移动端的前端应用。
项目地址:https://gitcode.com/gh_mirrors/el/element-plus
免费下载资源
·
- 假设已经存在一个Vue3.x+ts+sass项目;
- 由于是基于Vue3.x+ts搭建的示例项目,所以其它部分相关依赖使用的是next版本。
安装依赖
// 导入依赖库
npm install vuex vue-i18n js-cookie element-plus -S
//导入编译依赖
npm install @types/js-cookie -S -D
准备语言包
以中英文为例,创建自定义语言包en.ts和zh-cn.ts,维护应用使用的文字资源。
// src/locales/en.ts
export default {
name: 'English',
welcomeMsg: 'Welcome to Your Vue.js + TypeScript App',
setting: {
test1: 'test1',
test2: 'test2',
note1: 'Full function'
},
elPagination: {
per: 'Per page',
unit: 'item',
curPage: 'Current page: '
},
hello: {
test1: 'hello test1',
test2: 'hello test2'
}
}
// src/locales/zh-cn.ts
export default {
name: '中文',
welcomeMsg: '欢迎来到你的 Vue.js + TypeScript 应用程序!',
setting: {
test1: '测试1',
test2: '测试2',
note1: '完整功能'
},
elPagination: {
per: '每页',
unit: '条',
curPage: '当前页: '
},
hello: {
test1: '你好,测试1',
test2: '你好,测试2'
}
}
准备持久化方法
将用户偏爱设置(所选语言)持久化到cookie中,这里借助js-cookie封装的cookie操作工具。
key值维护
// src/constant/key.ts
class Keys {
static languageKey = 'vue3-typescript-admin-languageKey'
}
export default Keys
cookies封装
// src/utils/cookies.ts
import Keys from '@/constant/key'
import Cookies from 'js-cookie'
export const getLanguage = () => Cookies.get(Keys.languageKey)
export const setLanguage = (language: string) => Cookies.set(Keys.languageKey, language)
导入语言信息
导入生成Message语言包
// src/locales/index.ts
// 导入element-plus中英文语言包
import elementEnLocale from 'element-plus/lib/locale/lang/en'
import elementZhLocale from 'element-plus/lib/locale/lang/zh-cn'
// 导入自定义语言包
import enLocale from './en'
import zhLocale from './zh-cn'
const messages = {
en: {
...enLocale,
...elementEnLocale
},
'zh-cn': {
...zhLocale,
...elementZhLocale
}
}
获取当前语言
用户语言偏好设置,我们持久化到cookie中,通过getLanguage获取。
// src/locales/index.ts
import { getLanguage } from '@/utils/cookies'
export const getLocale = () => {
// 读取cookie存入的当前语言
const cookieLanguage = getLanguage()
// 如果有返回当前语言
if (cookieLanguage) {
return cookieLanguage
}
// 如果没有,获取系统语言
const language = navigator.language.toLowerCase()
// 获取messages 语言 遍历
const locales = Object.keys(messages)
for (const locale of locales) {
// 如果messsage 包里面有系统语言返回
if (language.indexOf(locale) > -1) {
return locale
}
}
// 默认语言 简体中文
return 'zh-cn'
}
创建i18n实例
// src/locales/index.ts
import { createI18n } from 'vue-i18n'
// 创建i18n实例
const i18n = createI18n({
locale: getLocale(),
messages: messages
})
export default i18n
使用i18n实例
一种方式直接在main.js中通过app.use(i18n)使用,一种方式是通过管理插件的方式与其他插件一起引入;后一种方式其实最终也是通过app.use(i18n)使用的,只是与其他插件一起管理起来,便于多人开发时,大家一起修改mian.js容易产生代码冲突。
方式一
// src/main.ts
import i18n from '@/locales'
const app = createApp(App)
app.use(i18n)
方式二
结合webpack提供的require.context接口特性,实现多插件动态导入。在src/plugins下维护所有插件,通过require.context获取所有插件,遍历并使用插件,导出loadAllPlugins方法,并在main.js中调用,实现加载所有插件。以下代码中包含对element-plus的引入。
- 准备插件
// src/plugins/i18n.ts
import i18n from '@/locales'
export default function loadComponent (app: any) {
app.use(i18n)
}
// src/plugins/element.ts
import ElementPlus from 'element-plus'
import 'element-plus/lib/theme-chalk/index.css'
import i18n from '@/locales'
export default function loadComponent (app: any) {
// 注意,此处的i18n: i18n.global.t是必须的,如果不设置,切换语言的时候,将会对element-plus组件无效。
app.use(ElementPlus, { size: 'small', i18n: i18n.global.t })
}
- 加载插件
// src/plugins/index.ts
import { createApp } from 'vue'
/**
* @description 加载所有 Plugins,导出loadAllPlugins
* @param {ReturnType<typeofcreateApp>} app 整个应用的实例
*/
export function loadAllPlugins (app: ReturnType<typeof createApp>) {
// 获取当前目录下所哟.ts文件
const files = require.context('.', true, /\.ts$/)
// 遍历,过滤并加载插件
files.keys().forEach(key => {
if (typeof files(key).default === 'function') {
if (key !== './index.ts') files(key).default(app)
}
})
}
- 在main.js调用,loadAllPlugins,实现插件的加载
// src/main.ts
import { createApp } from 'vue'
import App from './App.vue'
// import router from './router'
// import { store } from './store'
import { loadAllPlugins } from './plugins'
const app = createApp(App)
loadAllPlugins(app)
app.mount('#app')
// app.use(store).use(router).mount('#app')
至此,已经将导入了多语言,并知道如何使用i18n,实现基本的国际化,但是在实际应用中,我们希望的是能够通过切换入口,自由切换语言,并保证所有模块都显示目标语言。接下来将实现语言切换组件,展示如何使用组件及i18n提供的语言,并提供具体示例。
语言切换组件
Vuex的状态管理
由于语言切换组件需要依赖Vuex的状态管理,才能实现触发切换语言之后同步更新应用所有模块的语言,因此,我们首先来实现Vuex的相关逻辑。这里使用vuex4.x 分包(ts 类型推断type版),重点在于让vuex支持type 枚举类型推断。
- state
// src/store/modules/app/state.ts
// 驱动应用的数据源,包含了全部的应用层级状态,唯一数据源。
import { getLocale } from '@/locales'
export interface AppState {
language: string
}
export const state: AppState = {
language: getLocale()
}
// src/store/modules/app/mutation-types.ts
export enum AppMutationTypes {
SET_LANGUAGE = 'SET_LANGUAGE',
}
- mutation
// src/store/modules/app/mutations.ts
// 维护mutation用于更改Vuex的store中的language状态
import { MutationTree } from 'vuex'
import { AppState } from './state'
import { AppMutationTypes } from './mutation-types'
import { setLanguage } from '@/utils/cookies'
export type Mutations<S = AppState> = {
[AppMutationTypes.SET_LANGUAGE](state: S, language: string): void
}
export const mutations: MutationTree<AppState> & Mutations = {
[AppMutationTypes.SET_LANGUAGE] (state: AppState, language: string) {
state.language = language
setLanguage(state.language)
}
}
- action
// src/store/modules/app/action-types.ts
export enum AppActionTypes {
ACTION_SET_LANGUAGE = 'ACTION_SET_LANGUAGE',
}
// src/store/modules/app/actions.ts
// Action 类似于 mutation,不同在于:
// Action 提交的是 mutation,而不是直接变更状态。
// Action 可以包含任意异步操作。
import { ActionTree, ActionContext } from 'vuex'
// eslint-disable-next-line import/no-cycle
import { RootState } from '@/store'
import { AppState } from './state'
import { Mutations } from './mutations'
import { AppMutationTypes } from './mutation-types'
import { AppActionTypes } from './action-types'
type AugmentedActionContext = {
commit<K extends keyof Mutations>(
key: K,
payload: Parameters<Mutations[K]>[1],
): ReturnType<Mutations[K]>
} & Omit<ActionContext<AppState, RootState>, 'commit'>
export interface Actions {
[AppActionTypes.ACTION_SET_LANGUAGE](
{ commit }: AugmentedActionContext,
language: string
): void
}
export const actions: ActionTree<AppState, RootState> & Actions = {
[AppActionTypes.ACTION_SET_LANGUAGE] ({ commit }, language: string) {
commit(AppMutationTypes.SET_LANGUAGE, language)
}
}
- 让vuex支持type 枚举类型推断
// src/store/modules/app/index.ts
// 让vuex支持type枚举类型推断
import {
Store as VuexStore,
CommitOptions,
DispatchOptions,
Module
} from 'vuex'
import { RootState } from '@/store'
import { state } from './state'
import { mutations, Mutations } from './mutations'
import { actions, Actions } from './actions'
import type { AppState } from './state'
export { AppState }
export type AppStore<S = AppState> = Omit<VuexStore<S>, 'getters' | 'commit' | 'dispatch'>
& {
commit<K extends keyof Mutations, P extends Parameters<Mutations[K]>[1]>(
key: K,
payload: P,
options?: CommitOptions
): ReturnType<Mutations[K]>
} & {
dispatch<K extends keyof Actions>(
key: K,
payload: Parameters<Actions[K]>[1],
options?: DispatchOptions
): ReturnType<Actions[K]>
};
export const store: Module<AppState, RootState> = {
state,
mutations,
actions
}
- 动态加载模块
// src/store/index.ts
// 导入模块
// 使用vuex的createStore创建store实例
// 导出useStore方法,返回值Store,其他地方需要用到store的一定要使用useStore()
import { createStore, createLogger } from 'vuex'
import { store as app, AppStore, AppState } from '@/store/modules/app'
export interface RootState {
app: AppState
}
export type Store = AppStore<Pick<RootState, 'app'>>
// Plug in logger when in development environment
const debug = process.env.NODE_ENV !== 'production'
const plugins = debug ? [createLogger({})] : []
export const store = createStore({
plugins,
modules: {
app
}
})
export function useStore (): Store {
return store as Store
}
实现组件
<!-- src/components/lang_select/Index.vue -->
<template>
<div>
<el-dropdown>
<svg
class="icon"
aria-hidden="true"
font-size="20px"
:class="{'svg-color': isWhite}"
>
<use xlink:href="#icon-language" />
</svg>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item
v-for="item in languages"
:key="item.value"
:disabled="language===item.value"
>
<span @click="handleSetLanguage(item.value)">{{ item.name }}</span>
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
</template>
<script lang="ts">
import { useStore } from '@/store'
import { computed, defineComponent, reactive, toRefs } from 'vue'
import { AppActionTypes } from '@/store/modules/app/action-types'
import { useI18n } from 'vue-i18n'
import { ElMessage } from 'element-plus'
type Language = {
name: string
value: string
}
export default defineComponent({
props: {
isWhite: {
type: Boolean,
default: false
}
},
setup () {
const store = useStore()
const { locale } = useI18n()
const state = reactive({
languages: [{ name: 'en', value: 'en' }, { name: '中文', value: 'zh-cn' }] as Array<Language>,
handleSetLanguage: (lang: string) => {
locale.value = lang
store.dispatch(AppActionTypes.ACTION_SET_LANGUAGE, lang)
ElMessage({
message: 'Switch Language Success',
type: 'success'
})
}
})
const language = computed(() => {
return store.state.app.language
})
return {
...toRefs(state),
language
}
}
})
</script>
<style lang="scss" scoped>
.svg-color{
fill: #409EFF;
}
</style>
使用组件
<!-- src/views/About.vue -->
<!-- 在About界面中切换语言之后,查看Home界面和所使用的HelloWorld组件,显示的也是目标语言的文字 -->
<template>
<div class="about">
<LangSelect :isWhite="true" class="set-language" />
<div>{{ t("name") }} </div>
<div>{{ t("setting.test1") }}</div>
<div>{{ t("setting.test2") }}</div>
<div class="block">
<span class="demonstration">{{ t("setting.note1") }}</span>
<el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange"
:current-page="currentPage" :page-sizes="[100, 200, 300, 400]" :page-size="100"
layout="total, sizes, prev, pager, next, jumper" :total="400">
</el-pagination>
</div>
</div>
</template>
<script lang="ts">
import { defineComponent, reactive, ref, toRefs } from 'vue'
import LangSelect from '@/components/lang_select/Index.vue'
import { useI18n } from 'vue-i18n'
export default defineComponent({
components: {
LangSelect
},
setup() {
const { t } = useI18n()
const currentPage = ref(4)
const methods = reactive({
handleSizeChange(val: Number) {
// console.log(`每页 ${val} 条`)
console.log(t('elPagination.per') + val + t('elPagination.unit'))
},
handleCurrentChange(val: Number) {
// console.log(`当前页: ${val}`)
console.log(t('elPagination.curPage') + val)
}
})
return {
t,
currentPage,
...toRefs(methods)
}
}
})
</script>
<style scoped lang="scss">
.about {
height: 100vh;
width: 100%;
}
.set-language {
color: #000;
top: 3px;
font-size: 18px;
right: 0px;
cursor: pointer;
}
</style>
<!-- src/views/Home.vue -->
<template>
<div class="home">
<img alt="Vue logo" src="../assets/logo.png">
<HelloWorld :msg="t('welcomeMsg')" />
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import HelloWorld from '@/components/HelloWorld.vue' // @ is an alias to /src
import { useI18n } from 'vue-i18n'
export default defineComponent({
name: 'Home',
components: {
HelloWorld
},
setup() {
const { t } = useI18n()
return {
t
}
}
})
</script>
<!-- src/components/HelloWorld.vue -->
<template>
<div class="hello">
<h1>{{ msg }}</h1>
<div>
<h1>{{ t("hello.test1") }}</h1>
<h1>{{ t("hello.test2") }}</h1>
</div>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import { useI18n } from 'vue-i18n'
export default defineComponent({
name: 'HelloWorld',
props: {
msg: String
},
setup() {
const { t } = useI18n()
return {
t
}
}
})
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped lang="scss">
</style>
总结
- 安装依赖
- 准备语言包
- 导入语言包
- 使用cookie持久化用户语言偏好
- 当前选择locale
- 创建i18n实例
- Element-plus插件中设置i18n
- 加载i18n和element-plus插件
- 使用Vuex进行语言切换状态管理
- 编写语言切换组件
- 使用语言切换组件
GitHub 加速计划 / el / element-plus
23.88 K
15.38 K
下载
element-plus/element-plus: Element Plus 是一个基于 Vue 3 的组件库,提供了丰富且易于使用的 UI 组件,用于快速搭建企业级桌面和移动端的前端应用。
最近提交(Master分支:1 个月前 )
c1863f50
3 个月前
b55163fd
3 个月前
更多推荐
已为社区贡献2条内容
所有评论(0)