前端Vue3国际化开发 :使用vue-i18n库和Element Plus 组件实现

发布于:2025-06-13 ⋅ 阅读:(15) ⋅ 点赞:(0)

前端Vue3国际化开发 :使用vue-i18n库和Element Plus 组件实现

剖析 Vue3 + Element Plus 国际化(i18n)实现原理

本文将解析基于 Vue3Element Plus 的国际化架构实现,从核心概念到实际应用,带您全面了解多语言支持在现代化前端应用中的实现方式。

国际化(i18n)核心概念

国际化(Internationalization,简称 i18n)是指设计软件应用时,使其能适应多种语言和地区需求的过程。在 Vue 生态中,主要通过 vue-i18n 库实现(Vue I18n 是 Vue.js 的国际化插件),配合 Element Plus 的国际化支持,可构建完整的国际化应用。

国际化实现架构图

在这里插入图片描述

实现效果

效果一:Element Plus组件的国际化、使用vue-i8n插件实现页面的国际化

前端国际化开发效果

效果二:国际化语言本地持久化存储

语言本地持久化存储

具体步骤(Vue3 + Element Plus + i18n)

第一步:安装i8n

Vue I18n

Vue i18n插件在Vue3中需要使用v9版本,这一点需要注意!!
(我当时写项目的时候没有注意到这个一点,直接安装的最新版,目前为止还没发现什么问题,大家自己选择)

npm install vue-i18n@9   # Vue3 专用版本
或者
yarn add vue-i18n@9
或者
pnpm add vue-i18n@9

在这里插入图片描述
第二步:创建自定义语言资源文件
1、创建src/locales/en.js

export const language = {
	en: {
		// 部门
		sys_dept_list00001: 'Department Name',
		sys_dept_list00002: 'Person in Charge Name',
		sys_dept_list00003: 'Department Head',
		// 实验室
		管理: 'Manage',
		人员信息: 'Personnel Information',
		序号: 'NO.',
		姓名: 'Name',
		学工号: 'Student ID',
		账号: 'Account',
		操作: 'Action',
		编辑: 'Edit',
		}
	}

2、创建src/locales/zh-cn.js

	'zh-cn': {
			// 部门
			sys_dept_list00001: '部门名称',
			sys_dept_list00002: '负责人名称',
			sys_dept_list00003: '部门负责人',
			// 实验室管理
			管理: '管理',
			人员信息: '人员信息',
			序号: '序号',
			姓名: '姓名',
			学工号: '学工号',
			操作: '操作',
			编辑: '编辑',`在这里插入代码片`
			}

(我一开始用的编号,后来发现代码可读性有点差,不利于后期维护,索性全用中文了!)
在这里插入图片描述
(可以分两个对照文档,一份中文,一份英文,我是直接放一起了,大家自行选择!)
第三步:合并 Element Plus 语言包和自定义语言包并在main.ts挂载

import { icons as epIcons } from '@iconify-json/ep'
import { icons as nfIcons } from '@iconify-json/nf'
import { addCollection } from '@iconify/vue'
import { createApp } from 'vue'

import '!/public/style/index.css'
import '@unocss/reset/tailwind-compat.css'
import 'virtual:uno.css'
import '@nofar-core/shared/integrations/element'
import 'animate.css'

import { setupDirectives } from '@nofar-core/directives'
import { setupModules } from '@nofar-core/shared/plugins'
import App from '@nofar-core/shared/views/App.vue'

import 'vue3-draggable-resizable/dist/Vue3DraggableResizable.css'

import ElementPlus, { ElConfigProvider, useLocale } from 'element-plus'
import en from 'element-plus/es/locale/lang/en' // 英文语言包
import zhCn from 'element-plus/es/locale/lang/zh-cn' // 中文语言包

import { createPinia } from 'pinia'
import { createI18n } from 'vue-i18n'
import Vue3DraggableResizable from 'vue3-draggable-resizable'

import { language } from '@nofar-core/shared/locales' // 引入语言包

const pinia = createPinia()

const app = createApp(App)
app.use(pinia)
const appLangStr = sessionStorage.getItem('app-lang')
const appLang = appLangStr ? JSON.parse(appLangStr) : null
const savedLocale = appLang?.currentLang || 'zh-cn'  // 默认值

const initialLocale = ref(savedLocale)
// 创建 i18n 实例
const i18n = createI18n({
	legacy: false,
	// locale: 'en', // 默认语言
	locale: initialLocale.value || 'zh-cn', // 初始语言
	messages: {
		en: {
			// welcome: 'Welcome',
			...language.en, // 英文语言包
			...en // Element Plus 的英文语言包
		},
		'zh-cn': {
			// welcome: '欢迎',
			...language['zh-cn'], // 中文语言包
			...zhCn // Element Plus 的中文语言包
		}
	}
})
// 添加语言切换方法
const changeLocale = (locale: 'zh-cn' | 'en') => {
	// i18n.global.locale = locale
	// // ElConfigProvider.config({
	// // 	locale: locale === 'zh-cn' ? zhCn : en
	// // })
	// useLocale(ref(locale === 'zh-cn' ? zhCn : en))
	i18n.global.locale.value = locale // 操作ref的value属性
	useLocale(computed(() => (locale === 'zh-cn' ? zhCn : en)))
}

// 使用 Element Plus 和 i18n
app.use(ElementPlus, {
	// locale: i18n.global.locale === 'zh-cn' ? zhCn : en
	locale: computed(() => (i18n.global.locale.value === 'zh-cn' ? zhCn : en))
	// locale: en
})
app.use(i18n)

// 挂载语言切换方法
app.config.globalProperties.$changeLocale = changeLocale
app.use(Vue3DraggableResizable)
setupModules(app)
setupDirectives(app)

addCollection(epIcons)
addCollection(nfIcons)

app.config.errorHandler = (err) => {
	console.error(err)
}

app.mount('#app')

上述代码实现详解
1、语言包导入与管理

// 引入 Element Plus 语言包
import en from 'element-plus/es/locale/lang/en' // 英文语言包
import zhCn from 'element-plus/es/locale/lang/zh-cn' // 中文语言包

//从 vue-i18n中导入用于创建 i18n 实例的方法
import { createI18n } from 'vue-i18n'

// 引入自定义语言包
import { language } from '@nofar-core/shared/locales'

// 创建 i18n 实例
const i18n = createI18n({
  legacy: false, // 使用 Vue3 的 Composition API 风格
  locale: initialLocale.value || 'zh-cn', // 初始语言
  messages: {
    en: {
      ...language.en, // 自定义英文语言包
      ...en // Element Plus 的英文语言包
    },
    'zh-cn': {
      ...language['zh-cn'], // 自定义中文语言包
      ...zhCn // Element Plus 的中文语言包
    }
  }
})

关键点解析:

  • legacy: false 表示使用 Vue3 的 Composition API 风格
  • 通过对象展开符 合并自定义语言包和 Element Plus 语言包
  • 语言包结构为键值对形式,例如:{ welcome: '欢迎', login: '登录' }

2、 语言状态初始化

// 从 sessionStorage 获取保存的语言设置
const appLangStr = sessionStorage.getItem('app-lang')
const appLang = appLangStr ? JSON.parse(appLangStr) : null

// 设置初始语言,默认为中文
const savedLocale = appLang?.currentLang || 'zh-cn'  
const initialLocale = ref(savedLocale)

关键点解析:

  • 使用 sessionStorage 持久化用户的语言选择
  • 初始语言默认为中文(‘zh-cn’)
  • 使用 Vue 的 ref 创建响应式语言状态

3、Element Plus 国际化集成

// 使用 Element Plus 并配置国际化
app.use(ElementPlus, {
  locale: computed(() => 
    i18n.global.locale.value === 'zh-cn' ? zhCn : en
  )
})

关键点解析:

  • 使用 computed 创建响应式语言配置
  • 根据当前选择的语言动态返回对应的 Element Plus 语言包
  • 确保 Element Plus 组件(如日期选择器、表单等)显示正确的语言

4、语言切换功能实现

// 添加语言切换方法
const changeLocale = (locale: 'zh-cn' | 'en') => {
  // 更新 vue-i18n 的语言设置
  i18n.global.locale.value = locale
  
  // 更新 Element Plus 的语言设置
  useLocale(computed(() => (locale === 'zh-cn' ? zhCn : en)))
}

  // 挂载语言切换方法到全局
  app.config.globalProperties.$changeLocale = changeLocale

关键点解析:

  • i18n.global.locale.value 是响应式属性,更新后会触发界面重新渲染
  • useLocale 是 Element Plus 提供的用于更新组件语言的函数
  • changeLocale 方法挂载到全局,方便在任何组件中调用

第四步:语言持久化存储

// 在组件中使用语言状态管理
<script setup lang="ts">
import { ElDropdown, ElDropdownItem, ElDropdownMenu, ElMessage } from 'element-plus'
import { getCurrentInstance } from 'vue'
import { useLanguageStore } from '@nofar-core/shared/stores/language'

const {locale } = useI18n()
// 获取Pinia语言状态存储库
const languageStore = useLanguageStore()

// 获取当前Vue实例的代理对象
const { proxy } = getCurrentInstance() as any

// 处理语言切换
const handleCommand = async (command: string) => {
  locale.value = command // 更新本地响应式变量
  languageStore.setLanguage(command) // 保存到Pinia状态管理
  if (command === 'zh-cn') {
    proxy.$changeLocale('zh-cn') // 调用全局方法切换中文
    ElMessage.success('已切换为中文')
  } else if (command === 'en') {
    proxy.$changeLocale('en') // 调用全局方法切换英文
    ElMessage.success('Switched to English')
  }
}
</script>

注意:代码中使用了proxy.$changeLocale,这是在之前代码中挂载到app.config.globalProperties上的全局方法。

关键点解析:

  • 通过调用了proxy.$changeLocale,更新i18n的locale(这样使用i18n的地方会重新渲染),更新Element Plus的locale(这样Element Plus组件会显示对应语言)
  • 将当前语言保存到sessionStorage(键为’app-lang’),实现持久化。
  • 当应用重新加载(刷新或重新进入)时,main.ts会从sessionStorage中读取'app-lang'的值,并作为初始语言设置i18nElement Plus

Pinia状态管理(语言状态存储库定义 (stores/language.ts))

import { defineStore } from 'pinia';
export const useLanguageStore = defineStore('language', {
	persist: {
		key: 'app-lang',
		storage: sessionStorage,
		pick: ['currentLang']
	},
	state: () => ({
		currentLang: 'zh-cn' // 默认中文
	}),
	actions: {
		setLanguage(lang: string) {
			this.currentLang = lang
		},
		getApiLangParam() {
			return this.currentLang === 'zh-cn' ? 'Ch' : 'En'
		}
	}
})

第五步:vue页面中使用useI18n中的 $t函数式组件传递值
代码:

<script setup lang="ts">
	import { useI18n } from 'vue-i18n' // 引入 useI18n
	const { t } = useI18n()

	const querys = reactive([
		{
			prop: 'personName',
			labelKey: '人员名称',
			label: ''
		},
		{
			prop: ['startTime', 'endTime'],
			labelKey: '出入时间',
			label: '',
			type: 'date' as const,
			datePicker: {
				type: 'datetimerange' as const,
				valueFormat: 'YYYY-MM-DD HH:mm:ss',
				rangeSeparator: '-',
				startPlaceholder: t('开始日期'),
				endPlaceholder: t('结束日期')
			}
		},
	])

	const queryParams = reactive({
		userName: undefined,
		regionId: undefined,
		startTime: undefined,
		endTime: undefined
	})
	const pageParams = reactive({
		pageIndex: 1,
		pageSize: 10
	})
	const columns = computed(() => [
		{
			type: 'selection'
		},
		{
			prop: 'personName',
			label: t('人员名称')
		},
		{
			prop: 'cardNo',
			label: t('卡号')
		},
	])
</script>
<template>
	<div class="h-full flex flex-col gap-pageGap">
		<NfPageHeader v-model="queryParams" :querys @query="resetAndQuery()" @reset="resetAndQuery()" />
		<div class="flex flex-1 flex-col gap-pageGap rounded-module bg-white p-4">
			<NfTable class="flex-1" height="auto" :columns :data row-key="id">
				<template #action="{ row }">
					<el-button type="primary" link @click="handleEdit(row)">{{t('查看详情')}}</el-button>
				</template>
			</NfTable>
			<NfPagination v-model="pageParams" :total="total" @change="getList()" />
		</div>
	</div>
	<EditDialog v-model="open" :form :region-list @submit="getList()" />
</template>

我这边用的是封装的组件,正常用form表单可以参考下面的:

<template>
	<div class="app-container h-full w-full flex flex-col">
		<el-form ref="queryRef" :model="queryParams" :inline="true" class="bg-#fff px-4 pt-6 rounded-lg mb-4">
			<el-form-item :label="t('设施名称')" prop="deviceName" label-width="auto">
				<el-input v-model="queryParams.deviceName" :placeholder="t('请输入设施名称')" clearable maxlength="30" @keyup.enter="handleQuery" />
			</el-form-item>
			<el-form-item :label="t('负责人')" prop="userName" label-width="auto">
				<el-input v-model="queryParams.userName" :placeholder="t('请输入负责人名称')" clearable maxlength="30" @keyup.enter="handleQuery" />
			</el-form-item>
	</div>
</template>
<script setup lang="ts">
	import { useI18n } from 'vue-i18n' // 引入 useI18n
	const { t } = useI18n()
		const rules = computed(() => {
		return {
			deviceName: [{ required: true, trigger: 'blur', message: t('设施名称不能为空') }],
			userName: [{ required: true, trigger: 'blur', message: t('负责人不能为空') }]
		}
	})
	</script>

效果展示:

第六步:Element Plus的国际化操作
Element Plus 提供了一个 Vue 组件 ConfigProvider 用于全局配置国际化的设置。官方地址https://element-plus.org/zh-CN/guide/i18n.html
只需要在 App.vue 中包一层配置组件即可!
代码:

App.vue
<script setup lang="ts">
	import { ElConfigProvider } from 'element-plus'
	import en from 'element-plus/es/locale/lang/en' // 英文语言包
	import zhCn from 'element-plus/es/locale/lang/zh-cn'
	import { useI18n } from 'vue-i18n' // 引入 useI18n
	import { RouterView } from 'vue-router'
	const { locale } = useI18n() // 获取当前语言
</script>

<template>
	<n-config-provider :theme-overrides="themeOverrides" class="h-full">
		<el-config-provider  :locale="locale === 'zh-cn' ? zhCn : en">
			<router-view></router-view>
		</el-config-provider>
	</n-config-provider>
</template>

效果展示:

第七步:热更新问题
比如:表单验证规则
如果按照reactive正常写代码,在进行语言切换时会发现表单的验证规则更新不及时!
如下:

	const rules = reactive({
			name: [{ required: true, message: t('请输入门禁名称'), trigger: 'blur' }],
			regionId: [{ required: true, message: t('请选择区域'), trigger: 'change' }],
		})

效果:
在这里插入图片描述

解决方案:使用computed

	const rules = computed(() => {
		return {
			name: [{ required: true, message: t('请输入门禁名称'), trigger: 'blur' }],
			regionId: [{ required: true, message: t('请选择区域'), trigger: 'change' }],
		}
	})

效果:
在这里插入图片描述

完整工作流程解析如下:

在这里插入图片描述

注意事项