交流QQ群:555913397
有什么问题可以加群大家一起交流
1.安装i18n
yarn add vue-i18n
2.配置i18n
目录结构
index.ts i18n配置文件
import type { App } from 'vue'
import { createI18n, I18n } from 'vue-i18n'
import elementZhcnLocale from 'element-plus/lib/locale/lang/zh-cn'
import elementEnLocale from 'element-plus/lib/locale/lang/en'
import { useConfig } from '@/store/config'
export var i18n: I18n<IAnyObj, unknown, unknown, false>
//语言包
const assignLocale: IAnyObj = {
'zh-cn': [elementZhcnLocale],
en: [elementEnLocale]
}
export async function loadLang(app: App) {
const config = useConfig()
const locale = config.lang.defaultLang
//加载框架语言包
const lang = await import(`./globs-${locale}.ts`)
const message = lang.default ?? {}
/*
* 0、加载页面语言包 import.meta.glob 的路径不能使用变量
* import.meta.glob 匹配到的文件默认是懒加载的,通过动态导入实现,并会在构建时分离为独立的 chunk。如果你倾向于直接引入所有的模块(例如依赖于这些模块中的副作用首先被应用),
* 你可以传入 { eager: true } 作为第二个参数:
* 1、vue3 setup 内只能使用 useI18n({messages:{}}) 来动态载入当前页面单独的语言包,不方便使用
* 2、直接载入所有 /@/lang/pages/语言/*.ts 文件,若某页面有特别大量的语言配置,可在其他位置单独建立语言包文件,并在对应页面加载语言包
*/
if (locale == 'zh-cn') {
//
let path1 = import.meta.glob('./pages/zh-cn/**/*.ts', { eager: true })
let path = await getLangFileMessage(path1, locale)
assignLocale[locale].push(path)
} else if (locale == 'en') {
assignLocale[locale].push(getLangFileMessage(import.meta.glob('./pages/en/**/*.ts'), locale))
}
const messages: IAnyObj = {
[locale]: {
...message
}
}
// 合并语言包(含element-puls、页面语言包)
Object.assign(messages[locale], ...assignLocale[locale])
i18n = createI18n({
locale: locale,
legacy: false,//组合式api
globalInjection: true, //挂载$t,$d等到全局
fallbackLocale: config.lang.fallbackLang,
messages
})
app.use(i18n)
return i18n
}
/**
*
* @param mList 语言文件集合
* @param locale 本地语言名称
*/
function getLangFileMessage(mList: any, locale: string) {
interface IMsg {
[key: string]: any
}
let msg: IMsg = {}
locale = '/' + locale
for (let path in mList) {
if (mList[path].default) {
//获取文件名 ps 从/zh-cn +1的位置开始截取到.ts前
let fileName = path.slice(path.lastIndexOf(locale) + (locale.length + 1), path.lastIndexOf('.'))
//如果还有子文件夹
if (fileName.indexOf('/') > 0) {
let fileNameTemp = fileName.split('/')
//一级子目录
if (fileNameTemp.length == 2) {
//如果msg对象内没有该key,便初始化,否则调用会报错
if (msg[fileNameTemp[0]] === undefined) msg[fileNameTemp[0]] = []
msg[fileNameTemp[0]][fileNameTemp[1]] = handleMsglist(mList[path].default)
}
//二级子目录
else if (fileNameTemp.length == 3) {
if (msg[fileNameTemp[0]] === undefined) msg[fileNameTemp[0]] = []
if (msg[fileNameTemp[0]][fileNameTemp[1]] === undefined) msg[fileNameTemp[0]][fileNameTemp[1]] = []
msg[fileNameTemp[0]][fileNameTemp[1]][fileNameTemp[2]] = handleMsglist(mList[path].default)
} else {
msg[fileName] = handleMsglist(mList[path].default)
}
} else {
msg[fileName] = handleMsglist(mList[path].default)
}
}
}
return msg
}
function handleMsglist(mlist: IAnyObj) {
let newMlist: any = []
for (const key in mlist) {
if (key.indexOf('.') > 0) {
let keyTemp = key.split('.')
if (typeof newMlist[keyTemp[0]] === 'undefined') {
newMlist[keyTemp[0]] = []
} else {
newMlist[keyTemp[0]][keyTemp[1]] = mlist[key]
}
} else {
newMlist[key] = mlist[key]
}
}
return newMlist
}
export function editDefaultLang(lang: string): void {
const config = useConfig()
config.setLang(lang)
/*
* 语言包是按需加载的,比如默认语言为中文,则只在app实例内加载了中文语言包
* 查阅文档无数遍,无耐接受当前的 i18n 版本并不支持动态添加语言包(或需要在 setup 内动态添加,无法满足全局替换的需求)
* 故 reload;如果您有已经实现的无需一次性加载全部语言包且无需 reload 的方案,请一定@我
*/
location.reload()
}
main.ts
import { createApp } from 'vue'
import '@/style.css'
import App from '@/App.vue'
import router from '@/router/index'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import * as Icons from '@element-plus/icons'
import { appendFile } from 'fs'
import pinia from './store'
import { loadLang } from './lang'
const app = createApp(App).use(router).use(pinia)
//注册语言包
const i18n = await loadLang(app)
//Todo 这里i18n.global.t 类型实例化过深,且可能无限。待解决
app.use(ElementPlus, { i18n: i18n.global.t })
// 循环注册所有图标
for (const name in Icons) {
//name 为icon名称,也是组件名称。使用时:<Edit />,
//也可以按需增加前缀或者后缀
// eg: app.component(`eurake-name-${name}`,(Icons as any)[name])
// 使用时:<eurake-name-Edit />
// 当然你也可以写一个方法把首字母大写的图标名称改成小写等你想要的任意格式
app.component(name, (Icons as any)[name])
}
app.mount('#app')
语言文件,其他需要自定义语言文件照着格式写就可以了
globs-en.ts
export default {
id: 'ID',
state: 'State',
home: 'Home',
'Return to home page': 'Return to home page',
'Back to previous page': 'Back to previous page',
complete: 'Complete',
'switch language': 'Switch language',
layout: {
seting: 'seting',
'Exit full screen': 'Exit full screen',
'Full screen is not supported': 'Your browser does not support full screen. Please change your browser and try again~',
},
edit: 'Edit',
add: 'Add',
info: 'View details',
'weigh-sort': 'Drag sorting',
delete: 'Delete',
refresh: 'Refresh',
operate: 'Operate',
Confirm: 'Confirm',
Cancel: 'Cancel',
Save: 'Save',
Upload: 'Upload',
Retry: 'Retry',
Reminder: 'Warm Reminder',
'Save and edit next item': 'Save and edit next item',
'quick Search Placeholder': 'Fuzzy search by {fields}',
'Please select field': 'Please select {field}',
'Please input field': 'Please input {field}',
'Please enter the correct field': 'Please enter the correct {field}',
updatetime: 'Modify time',
createtime: 'Creation time',
'Fuzzy query': 'Fuzzy query',
Disable: 'Disable',
Enable: 'Enable',
'Click Select': 'Click select',
'Edit selected row': 'Edit selected row',
'Delete selected row': 'Delete selected row',
'Are you sure to delete the selected record?': 'Sure to delete the selected record?',
shrink: 'Shrink',
open: 'Open',
'All submenus': 'All submenus',
'Shrink all': 'Shrink all',
'Expand all': 'Expand all',
'Expand generic search': 'Expand Generic Search',
search: 'Search',
Reset: 'Reset',
to: 'To',
'Link address': 'Link address',
none: 'None',
unknown: 'Unknown',
weigh: 'weigh',
'No route found to jump~': 'No route found to jump~',
}
globs-zh-cn.ts
export default {
id: 'ID',
state: '状态',
home: '首页',
'Return to home page': '返回首页',
'Back to previous page': '返回上一页',
complete: '完成',
'switch language': '切换语言',
layout: {
seting: '设置',
'Exit full screen': '退出全屏',
'Full screen is not supported': '您的浏览器不支持全屏,请更换浏览器再试~',
},
edit: '编辑',
add: '添加',
info: '查看详情',
'weigh-sort': '拖动以排序',
delete: '删除',
refresh: '刷新',
operate: '操作',
Confirm: '确认',
Cancel: '取消',
Save: '保存',
Upload: '上传',
Retry: '重试',
Reminder: '温馨提示',
'Save and edit next item': '保存并编辑下一项',
'quick Search Placeholder': '通过{fields}模糊搜索',
'Please select field': '请选择{field}',
'Please input field': '请输入{field}',
'Please enter the correct field': '请输入正确的{field}',
updatetime: '修改时间',
createtime: '创建时间',
'Fuzzy query': '模糊查询',
Disable: '禁用',
Enable: '启用',
'Click Select': '点击选择',
'Edit selected row': '编辑选中行',
'Delete selected row': '删除选中行',
'Are you sure to delete the selected record?': '确定删除选中记录?',
shrink: '收缩',
open: '展开',
'All submenus': '所有子菜单',
'Shrink all': '收缩所有',
'Expand all': '展开所有',
'Expand generic search': '展开通用搜索',
search: '搜索',
Reset: '重置',
to: '至',
'Link address': '链接地址',
none: '无',
unknown: '未知',
weigh: '权重',
'No route found to jump~': '没有找到可以跳转的路由~',
}
如何使用
#引用i18n
import { useI18n } from 'vue-i18n'
const { t } = useI18n()
#模板中
<span>{{ t(`menu.${menu.title}`) }}</span>
#代码中
message: t('adminLogin.Please enter an account')
#自定义语言文件 比如 /zh-cn/401.ts
t('401.Please enter an account')
#自定义语言文件 比如 /zh-cn/user/group.ts
t('user.group.Please enter an account')