在Vue应用里做多语言,很多人第一反应是引入vue-i18n,但对于轻量项目或内部系统,用框架自带的provides与inject就能完成模块化变量配置。其核心思路是在根组件通过provides抛出一个包含语言映射的对象,后代组件用inject接收,从而实现跨层级共享而无需props逐级传递。

一、为什么用provides做多语言配置
传统的多语言方案往往把语言包集中在一个大文件,所有组件引入同一个JSON。这种做法在组件树较深时,依然要依靠全局单例或状态库。Vue的provides/inject是依赖注入机制,父级组件提供数据,任意后代组件注入使用,特别适合“一次配置,多层消费”的语言变量场景。
模块化变量配置则是指把不同语言拆成独立文件,比如zh.js、en.js,再通过一个index统一汇出。这样新增语言只需加文件,不改原逻辑。provides把汇总后的语言对象交给应用,组件侧完全不感知文件结构,只关心自己要取的key。
1.1 与vue-i18n的差异
vue-i18n提供格式化、复数、日期等完整能力,但体积和概念成本更高。provides方案仅利用原生API,没有额外依赖,适合文案简单、无需复杂语法的项目。下表列出主要区别:
| 维度 | provides方案 | vue-i18n |
|---|---|---|
| 依赖 | 无 | 需安装 |
| 响应式切换 | 需自行处理 | 内置 |
| 模块化 | 手动引入 | 支持按需 |
二、模块化语言包设计
我们先把语言变量按模块拆分。例如把按钮相关放btn.js,提示相关放tip.js,每种语言各一份。这样做能让翻译人员按模块对接,也方便代码评审。
下面以中文和英文为例,展示模块化文件内容。注意每个文件导出必须保持key一致,否则inject后取到的会是undefined。
// lang/zh/btn.js
export default {
submit: '提交',
cancel: '取消'
}
// lang/zh/tip.js
export default {
empty: '暂无数据',
error: '出错了'
}
// lang/en/btn.js
export default {
submit: 'Submit',
cancel: 'Cancel'
}
// lang/en/tip.js
export default {
empty: 'No data',
error: 'Error'
}
2.1 汇总语言包
通过一个index文件把模块合并,并按语言归类。这样provides时只需抛出一个语言字典,组件用inject取对应语言的字典即可。
// lang/index.js
import zhBtn from './zh/btn'
import zhTip from './zh/tip'
import enBtn from './en/btn'
import enTip from './en/tip'
export const zh = { ...zhBtn, ...zhTip }
export const en = { ...enBtn, ...enTip }
export const messages = { zh, en }
三、根组件用provides注入
在根组件中使用provides,把一个响应式语言标识和语言包对象提供出去。这里用Vue的reactive或ref来保证切换语言时后代能更新。
import { reactive, provide } from 'vue'
import { messages } from './lang'
export default {
setup() {
const i18n = reactive({
locale: 'zh',
messages
})
provide('i18n', i18n)
return {}
}
}
上面的代码把i18n对象通过provides指令(即provide函数)挂载到注入树。后代组件调用inject('i18n')就能拿到这个响应式对象。如果后续修改i18n.locale,由于对象是reactive的,所有引用处都会重新计算。
3.1 实现语言切换
切换逻辑很简单,只需改locale字段。我们可以写一个切换函数放在根组件methods或通过provide额外抛出一个切换方法。
import { reactive, provide } from 'vue'
import { messages } from './lang'
export default {
setup() {
const i18n = reactive({ locale: 'zh', messages })
const setLocale = (lang) => {
if (messages[lang]) i18n.locale = lang
}
provide('i18n', i18n)
provide('setLocale', setLocale)
return {}
}
}
四、子组件inject使用
任意后代组件通过inject拿到i18n和setLocale,就能渲染对应语言并触发切换。下面示例是一个按钮组件,显示提交文案并带切换中英文功能。
import { inject, computed } from 'vue'
export default {
setup() {
const i18n = inject('i18n')
const setLocale = inject('setLocale')
const t = computed(() => i18n.messages[i18n.locale])
return { t, setLocale, i18n }
},
template: `
<div>
<button>{{ t.submit }}</button>
<button @click="setLocale('en')">EN</button>
<button @click="setLocale('zh')">中</button>
</div>
`
}
这里用computed包裹取语言包,保证locale变化后t自动更新。注意template里写HTML标签时要按Vue模板规则,我们示例中用尖括号书写,在真实单文件组件里不需转义;若在文章代码块展示则如上面转义为<和>。
4.1 避免常见坑
第一个坑是provides的值如果不是响应式对象,切换语言不会生效。必须用reactive或ref包裹。第二个坑是模块化文件key不一致,比如中文有submit英文漏了,切换后按钮空白。建议用脚本校验各语言key集合是否相同。
第三个坑是在setup外直接用this.inject,老写法容易和组合式API混用出错。统一用inject函数并放在setup顶部,逻辑更清晰。只要守住这三点,provides指令实现的多语言方案就很稳。
五、总结实战要点
利用provides指令做多语言,本质是把语言包作为依赖注入到组件树。模块化切分让文件易维护,响应式对象让切换实时生效。相较于重型库,它更轻、更直观,适合中小项目。
实战中记得把语言汇总成messages字典,根组件provide响应式i18n,子组件inject后配合computed取词。遇到切换无效先查是否漏了reactive,遇到空白文案先查key是否对齐。照此套路,多语言支持的模块化变量配置就能平稳落地。
provides指令多语言支持模块化变量配置修改时间:2026-08-01 15:57:37