国际化:Element Plus 国际化与 vue-i18n 整合
Element Plus 国际化的两种方式
Element Plus 自身提供了国际化支持,组件内部的文字(如日期选择器的月份、分页器的"共 X 条"等)都可通过语言包切换。
方式一:全局配置(不推荐)
import ElementPlus from 'element-plus'
import zhCn from 'element-plus/es/locale/lang/zh-cn'
app.use(ElementPlus, {
locale: zhCn,
})
typescript
缺点:会将所有语言包全量导入,增大打包体积。
方式二:ElConfigProvider(推荐但有限制)
<template>
<el-config-provider :locale="currentLocale">
<App />
</el-config-provider>
</template>
<script setup lang="ts">
import { computed, ref } from 'vue'
import { ElConfigProvider } from 'element-plus'
import zhCn from 'element-plus/es/locale/lang/zh-cn'
import en from 'element-plus/es/locale/lang/en'
const language = ref('zh-cn')
const currentLocale = computed(() =>
language.value === 'zh-cn' ? zhCn : en
)
</script>
vue
局限:需要在需要国际化的组件中单独引入语言包,不适合全局统一管理。
整合方案:与 vue-i18n 合并
核心思路:将 Element Plus 的语言包合并到 vue-i18n 的 messages 中,实现统一管理。
为什么不能直接在 locales 文件中 import
// locales/zh-CN.js -- 这样行不通!
import elementLocale from 'element-plus/dist/locale/zh-cn.mjs'
export default {
hello: '你好',
...elementLocale, // unplugin-vue-i18n 插件会报错
}
javascript
原因:unplugin-vue-i18n 插件对 locales 文件有特殊处理,不支持在翻译文件中引入外部模块。
正确方案:在 i18n 模块中动态合并
// src/modules/i18n.ts
import { createI18n, type Locale } from 'vue-i18n'
export const availableLocales = ['zh-CN', 'en']
export const i18n = createI18n({
legacy: false,
locale: '',
messages: {},
})
const loadedLanguages: string[] = []
// ---- 自己的翻译文件 ----
const localesMap = Object.fromEntries(
Object.entries(
import.meta.glob('../../locales/*.js', { eager: true })
).map(([path, loadLocale]) => {
const localeKey = path.match(/\/([^/]+)\.js$/)?.[1] || ''
return [localeKey, loadLocale]
})
) as Record<string, { default: Record<string, any> }>
// ---- Element Plus 语言包 ----
// 注意:import.meta.glob 导入 node_modules 需要 Vite 4.0+
const elementPlusLocalesMap = Object.fromEntries(
Object.entries(
import.meta.glob(
'element-plus/dist/locale/*.mjs',
{ eager: true }
)
).map(([path, loadLocale]) => {
const localeKey = path.match(/\/([^/]+)\.mjs$/)?.[1] || ''
return [localeKey, loadLocale]
})
) as Record<string, { default: Record<string, any> }>
// 过滤出我们需要的语言
const filteredElementPlusLocalesMap = availableLocales.reduce<Record<string, { default: Record<string, any> }>>(
(acc, locale) => {
// Element Plus 使用 zh-CN 大写格式,需要 toLowerCase 匹配
const key = locale.toLowerCase()
if (elementPlusLocalesMap[key]) {
acc[locale] = elementPlusLocalesMap[key]
}
return acc
},
{}
)
function setI18nLanguage(locale: string) {
;(i18n.global.locale as any).value = locale
if (typeof document !== 'undefined') {
document.querySelector('html')?.setAttribute('lang', locale)
}
}
export async function loadLocaleMessages(locale: string) {
if (loadedLanguages.includes(locale)) {
setI18nLanguage(locale)
return
}
// 加载自己的翻译
const messages = await localesMap[locale]
if (messages) {
i18n.global.setLocaleMessage(locale, messages.default)
}
// 加载 Element Plus 翻译
const elementMessages = await filteredElementPlusLocalesMap[locale]
if (elementMessages) {
// 合并 Element Plus 的 messages
i18n.global.setLocaleMessage(locale, {
...i18n.global.getLocaleMessage(locale),
...elementMessages.default,
})
}
loadedLanguages.push(locale)
setI18nLanguage(locale)
}
export function install(app: any) {
app.use(i18n)
loadLocaleMessages('zh-CN')
}
export default install
typescript
关键实现细节
1. import.meta.glob 导入 node_modules
// Vite 4.0+ 支持导入 node_modules 中的文件
import.meta.glob('element-plus/dist/locale/*.mjs', { eager: true })
typescript
注意:Vite 2.x 不支持导入
node_modules,需升级到 4.0+。
2. 语言标识大小写问题
Element Plus 语言文件名为小写格式(如 zh-cn.mjs、en.mjs),而我们的项目使用驼峰格式(zh-CN)。匹配时需要统一转换:
const key = locale.toLowerCase() // 'zh-CN' → 'zh-cn'
typescript
3. 使用 reduce 过滤语言包
// 只保留 availableLocales 中指定的语言
const filtered = availableLocales.reduce(
(acc, locale) => {
const key = locale.toLowerCase()
if (elementPlusLocalesMap[key]) {
acc[locale] = elementPlusLocalesMap[key]
}
return acc
},
{} as Record<string, any>
)
typescript
为什么要过滤?因为 import.meta.glob('element-plus/dist/locale/*.mjs') 会匹配所有语言文件(阿拉伯语、俄语、法语等数十种),但我们只需要中文和英文。
4. 覆盖 Element Plus 默认翻译
如果需要修改 Element Plus 的默认翻译,在自己的 locales 文件中定义同路径的键值即可:
// locales/zh-CN.js
export default {
hello: '你好',
// 覆盖 Element Plus 的颜色选择器确认按钮
el: {
colorpicker: {
confirm: '确定'
}
}
}
javascript
加载顺序决定了覆盖关系:
// Element Plus 的翻译在前,自己的翻译在后
// 后面的会覆盖前面同路径的键值
{
...elementMessages.default, // Element Plus 官方翻译
...messages.default, // 我们自己的翻译(优先级更高)
}
typescript
验证整合效果
使用 Element Plus 的颜色选择器组件测试:
<template>
<el-color-picker v-model="color" />
</template>
vue
- 中文环境下:底部按钮显示"确定"
- 英文环境下:底部按钮显示"OK"
切换语言后,Element Plus 组件内的文字会同步变化,说明整合成功。
总结
- Element Plus 提供全局配置和 ElConfigProvider 两种方式,但都无法与 vue-i18n 统一管理
- 通过
import.meta.glob动态导入 Element Plus 语言包,合并到 vue-i18n 的 messages 中 - 需要 Vite 4.0+ 才支持导入
node_modules中的文件 - 使用
reduce过滤出所需语言,避免打包多余的语言包 - 自己的翻译放在合并对象后面,可以覆盖 Element Plus 的默认翻译
↑