vue-i18n — Vue 官网推荐的 i18n,切语言整页自己刷新
待复核vue-i18n 是 Vue 应用的国际化插件——把界面里所有文案按”用户挑的语言”切出来,并在切语言时让所有组件自动刷新显示新语种。日常类比:像餐厅菜单的”贴纸版”——同一份菜单背面贴着多语言贴纸,顾客指哪种语言,服务员(vue-i18n)就把对应贴纸翻到正面。
你不必到处写 if (lang === "zh")。组件里只写:
<script setup>import {useI18n} from "vue-i18n";const {t} = useI18n();</script><template><h1>{{ t("welcome") }}</h1></template>调一次 locale.value = "en",整个 app 里所有 t() 自动重算输出 —— 这就是它的核心便利。
不理解 vue-i18n,下面这些事都没法做:
- Vue 项目要做多语言时该选什么——Vue 官网与 Nuxt 文档都推荐它(Intlify 维护,非 vuejs 直属仓)
- 为什么
t("apple", {count: 5})会输出 “5 apples” 而不是 “5 apple”——背后是 ICU 风格复数 - 为什么切语言不需要刷新整页——locale 是 reactive ref,Vue 自己重跑模板
- 为什么生产环境要装 unplugin-vue-i18n——编译期预编译 messages,bundle 直接砍掉一半
vue-i18n 三个支柱,类比”翻译三角”:
- Composition API hook(useI18n):组件喊一声
useI18n()就拿到 t/d/n 三把刀。类比:进厨房直接拿配好的工具包,不用每次自带。 - ICU 风格 MessageFormat:复数、性别、选择都用
{count, plural, one {...} other {...}}这套写法。类比:用国际通用菜谱符号,不用每个厨师自己发明记号。 - Reactive locale:
locale是个 Vue ref,改它等于改一个响应式变量,整棵组件树自动重渲染。类比:餐厅墙上有个总开关,一拨切语言模式,所有桌子的菜单贴纸同时翻面。
合在一起:createI18n({locale, messages}) 注册一次 → 任意组件 useI18n() 取工具 → 改 locale.value 即可整页换语言。
案例 1:最小中英切换
Section titled “案例 1:最小中英切换”import {createI18n} from "vue-i18n";const i18n = createI18n({ legacy: false, // 关掉旧 Options API,走 Composition locale: "zh", fallbackLocale: "en", // 缺 key 时回退英文 messages: { zh: {welcome: "欢迎,{name}"}, en: {welcome: "Welcome, {name}"} }});app.use(i18n);<script setup>import {useI18n} from "vue-i18n";const {t, locale} = useI18n();</script><template> <h1>{{ t("welcome", {name: "Alice"}) }}</h1> <button @click="locale = locale === 'zh' ? 'en' : 'zh'">切换</button></template>按按钮 → 模板里改 locale(等同改 ref)→ 所有 t() 重算 → h1 立刻刷新,没有整页 reload。
案例 2:ICU 风格复数
Section titled “案例 2:ICU 风格复数”{"apple": "{count, plural, =0 {No apples} one {# apple} other {# apples}}"}<template> <p>{{ t("apple", {count: 0}) }}</p> <!-- No apples --> <p>{{ t("apple", {count: 1}) }}</p> <!-- 1 apple --> <p>{{ t("apple", {count: 5}) }}</p> <!-- 5 apples --></template>逐部分解释:{count, plural, ...} 是 ICU 风格语法,=0 精确匹配 0,one 是英语单数槽,other 是兜底,# 代表传入的数字。与 react-intl 同族,思路可共享,但不是无改粘贴——两边细节(链接消息、插件开关)仍有差。
案例 3:unplugin-vue-i18n 砍 bundle
Section titled “案例 3:unplugin-vue-i18n 砍 bundle”import VueI18nPlugin from "@intlify/unplugin-vue-i18n/vite";export default defineConfig({ plugins: [ VueI18nPlugin({ include: ["./src/locales/**"], compositionOnly: true, // 砍 Options API 路径 runtimeOnly: true // messages 编译期变 JS 函数 }) ]});类比:编译期把菜谱印成成品,运行时不用再带厨师(MessageCompiler)。效果:核心从 ~15 KB 降到 ~5 KB。代价:dev 阶段动态加 message 必须重启 vite。
- v8 升 v9 几乎是重写:v9 只支持 Vue 3 + Composition API,老 Vue 2 项目升 i18n 等于重做整个文案层;最好和 Vue 3 升级一起做。
- unplugin 配错 silent fail:
include写错路径时插件不抛错,translation 全回到 key 字面量;要开debug: true看编译日志才能定位。 - ICU 与 i18next 后缀不兼容:从 i18next 迁过来时
apple_one/apple_other这种后缀必须批量改成 ICU{count, plural, ...},没工具自动转。 - 子组件 locale 隔离需显式声明:默认子组件继承父 i18n,想让某个子组件用独立 locale 必须
useI18n({useScope: "local", inheritLocale: false}),否则改了不生效。
适用 vs 不适用场景
Section titled “适用 vs 不适用场景”适用:
- Vue 3 项目做多语言(事实标准,文档生态最齐)
- 团队需要 ICU 风格复数(与 react-intl / next-intl 同族,思路可对齐)
- 想要 reactive 切语言、不刷新页面
不适用:
- 跨框架团队(同时 Vue + React)→ 改用 i18next + 各家适配器
- 老 Vue 2 项目暂时不能升 Vue 3 → 留在 vue-i18n v8(不再大更新)
- React 专项目 → 用 react-intl 或 next-intl
- 需要编译期 macro 提取文案 → 用 lingui,vue-i18n 走运行时 + unplugin
历史小故事(可跳过)
Section titled “历史小故事(可跳过)”- 2014:Kazuya Kawaguchi(@kazupon)发布 vue-i18n v0.1,跟着 Vue 1.x 一起长起来。
- 2020:Vue 3 发布前后完全重写为 v9(vue-i18n-next 仓库),引入 Composition API + ICU MessageFormat,与 v8 不兼容。
- 2021:维护方组建 Intlify 团队,把核心抽成
@intlify/core独立包,理论上可适配 React/Svelte,实际只服务 Vue。 - 2023+:Vue 官网、Nuxt 文档默认推荐 vue-i18n,事实标准地位稳固,weekly downloads ~3M。
- i18n 标准比 i18n 库更重要:选 ICU 还是自家格式,决定能不能跨框架共享 messages。
- 响应式 locale 是 Vue 的杀手锏:切语言不刷新页面,是把”全局开关”做成 reactive 变量的自然结果。
- 运行时 vs 编译期 pipeline:先用运行时把功能跑通,再用 unplugin 上编译期优化,是 Vue/React 工具链共同套路。
- 官方推荐 = 默认胜利:技术差异不大时,“框架官网默认装哪个”几乎决定生态走向。
- 官方文档:vue-i18n.intlify.dev(v9 起的 Composition API 章节最实用)
- Intlify 仓库:intlify/vue-i18n-next
- ICU MessageFormat 规范:unicode.org/reports/tr35/tr35-messageFormat.html
- 编译期插件:@intlify/unplugin-vue-i18n
- i18next —— 跨框架的对位标准库
- react-intl —— React 阵营的同标准对手