Vue Router 搭配 i18n 怎么做多语言路由适配?
做国际化项目时,不仅页面文字要多语言,路由路径、页面标题甚至动态路由参数都得跟着语言变,比如英文站的 “/about” 对应中文站的 “/关于我们”,用户切换语言时路由还得同步跳转,这时候把 Vue Router 和 i18n 结合起来做适配,就能解决这类问题,下面从需求逻辑、配置方法到踩坑技巧,一步步拆解怎么做。
为什么非得给路由做多语言适配?
先想清楚场景:要是做面向全球用户的产品,不同地区用户习惯用自己语言的 URL(比如德国人更熟悉 /de/ueber-uns
而不是 /en/about
),从业务和体验看,至少有这几个好处:
- SEO 友好:搜索引擎更青睐语言和 URL 匹配的页面,英文内容配英文路径,爬虫抓取时权重更高;
- 用户认知统一:用户切换语言后,路径和页面内容语言一致,不会出现“路径是英文,内容是中文”的割裂感;
- 多语言路由管理:像电商产品详情页,不同语言下路径结构统一(如
/:lang/product/:id
),后期维护路由规则更方便。
先把基础环境搭起来(Vue + Router + I18n)
得先把依赖和基础配置跑通,假设用 Vue 3 + Vue Router 4 + Vue I18n 9(主流组合),步骤大概这样:
装依赖
打开终端执行:
npm install vue-router@4 vue-i18n@9
初始化 Vue I18n
在 i18n.js
里配置语言包,比如先搞中英文:
import { createI18n } from 'vue-i18n' const messages = { en: { route: { home: 'Home', about: 'About', product: 'Product' }, // 其他页面文字... }, zh: { route: { home: '首页', about: '关于我们', product: '产品' }, // 其他页面文字... } } const i18n = createI18n({ legacy: false, // Vue 3 要设为 false locale: 'en', // 默认语言 messages }) export default i18n
配置 Vue Router 基础路由
在 router.js
里先写通用路由结构,
import { createRouter, createWebHistory } from 'vue-router' import Home from './views/Home.vue' import About from './views/About.vue' const routes = [ { path: '/', name: 'Home', component: Home }, { path: '/about', name: 'About', component: About } ] const router = createRouter({ history: createWebHistory(), routes }) export default router
让路由路径跟着语言“变”的核心逻辑
现在要让路由路径能根据语言切换,比如英文是 /about
,中文是 /关于我们
,得从路由结构设计和导航逻辑入手。
给路由加“语言前缀”
常见做法是给所有路由加 /:lang
前缀,/en/about
、/zh/关于我们
,这时候得调整路由配置,同时处理语言检测:
改路由配置
把路由改成带语言参数的结构,
const routes = [ { path: '/:lang', name: 'Home', component: Home, // 用 meta 存多语言标题的 key meta: { title: 'route.home' } }, { path: '/:lang/about', name: 'About', component: About, meta: { title: 'route.about' } } ]
导航守卫里处理语言
在路由全局守卫 beforeEach
里,检测 URL 里的 lang
参数是否是支持的语言(en/zh),然后设置 i18n 的 locale:
router.beforeEach((to, from, next) => { const lang = to.params.lang || 'en' // 没有传 lang 就默认英文 if (['en', 'zh'].includes(lang)) { i18n.global.locale.value = lang // 设置当前语言 next() } else { // 不支持的语言跳转到默认语言首页 next({ name: 'Home', params: { lang: 'en' } }) } })
这样用户访问 /en/about
时,自动把语言设为 en;访问 /zh/关于我们
时设为 zh。
动态生成多语言路由表
如果路由很多,每个都写 /:lang/xxx
太麻烦,可以预先定义不同语言的路由表,再根据当前语言合并。
// 英文路由 const enRoutes = [ { path: '/en', name: 'Home', component: Home }, { path: '/en/about', name: 'About', component: About } ] // 中文路由 const zhRoutes = [ { path: '/zh', name: 'Home', component: Home }, { path: '/zh/关于我们', name: 'About', component: About } ] // 合并路由 const routes = [...enRoutes, ...zhRoutes] const router = createRouter({ history: createWebHistory(), routes })
然后在语言切换时,通过 router.push
跳转到对应语言的路由,这种方式适合路由结构差异大的场景,但维护成本稍高,得保证不同语言路由的 name
一致。
页面里怎么让内容和路由联动?
路由适配后,页面组件里要同时处理文字国际化和路由参数。
用 useI18n 渲染多语言文字
在组件里导入 useI18n
,用 t
函数渲染文字:
<template> <div> <h1>{{ t('route.about') }}</h1> <p>{{ t('about.desc') }}</p> <!-- 语言切换按钮 --> <button @click="switchLang">切换语言</button> </div> </template> <script setup> import { useI18n } from 'vue-i18n' import { useRouter } from 'vue-router' const { t } = useI18n() const router = useRouter() const switchLang = () => { const newLang = i18n.global.locale.value === 'en' ? 'zh' : 'en' // 根据当前路由 name 跳转到新语言的路由 router.push({ name: router.currentRoute.value.name, params: { lang: newLang } }) } </script>
这里点击“切换语言”时,会根据当前路由的 name
('About'),替换 lang
参数,跳转到对应语言的路由(如 /zh/关于我们
)。
动态路由参数的处理
比如产品详情页,路径是 /:lang/product/:id
,组件里要获取 lang
和 id
,同时渲染对应语言的产品名称:
<template> <div> <h1>{{ t('product.detail') }} - {{ productId }}</h1> <p>{{ t('product.name', { name: productName }) }}</p> </div> </template> <script setup> import { useRoute, useRouter } from 'vue-router' import { useI18n } from 'vue-i18n' import { computed } from 'vue' const route = useRoute() const { t } = useI18n() // 获取路由参数 const productId = route.params.id // 假设从接口拿多语言产品名,这里简化 const productName = computed(() => { return i18n.global.locale.value === 'en' ? 'Vue Router Guide' : 'Vue 路由指南' }) </script>
这样不管是英文还是中文路径,都能通过路由参数拿到产品 ID,再结合语言渲染内容。
语言切换时,路由怎么“同步跳”?
用户点语言切换按钮后,不仅要改 i18n 的 locale,还得让路由路径变,核心是根据当前路由信息,生成新语言的路由路径。
方法:用 router.push 动态生成新路由
在语言切换函数里,先判断当前语言,再拼新语言的路由:
// 语言切换函数(在组件或全局工具里) const switchLanguage = () => { const currentLang = i18n.global.locale.value const newLang = currentLang === 'en' ? 'zh' : 'en' const currentRoute = router.currentRoute.value // 根据当前路由的 name 和 params 生成新路由 router.push({ name: currentRoute.name, params: { ...currentRoute.params, lang: newLang } }) }
比如当前在 /en/product/123
,name 是 'ProductDetail',params 是 { lang: 'en', id: '123' },切换后会变成 /zh/product/123
,i18n 的 locale 也会在路由守卫里被设置为 zh(参考前面的 beforeEach
逻辑)。
路由元信息(meta)的多语言玩法
路由的 meta 可以存页面标题、权限等信息,多语言场景下,用 meta 存 i18n 的 key,再全局设置页面标题:
路由里配 meta.title
修改路由配置,给每个路由加 meta:
const routes = [ { path: '/:lang', name: 'Home', component: Home, meta: { title: 'route.home' } // 对应 i18n 里的 key }, { path: '/:lang/about', name: 'About', component: About, meta: { title: 'route.about' } } ]
全局守卫里设置 document.title
在 router.beforeEach
里,用 i18n 的 t 函数把 meta.title 转成对应语言的标题:
router.beforeEach((to, from, next) => { const lang = to.params.lang || 'en' if (['en', 'zh'].includes(lang)) { i18n.global.locale.value = lang // 设置页面标题 if (to.meta.title) { document.title = i18n.global.t(to.meta.title) } next() } else { next({ name: 'Home', params: { lang: 'en' } }) } })
这样用户访问 /en/about
时,页面标题是 “About”;访问 /zh/关于我们
时,标题是 “关于我们”,和路径、内容语言完全一致。
避坑:这些问题很容易栽跟头!
做路由和 i18n 结合时,几个高频坑得提前避:
路由匹配优先级乱了
如果同时有 /about
(无语言前缀)和 /:lang/about
(有前缀)的路由,Vue Router 会优先匹配更具体的路由,所以要把语言前缀的路由放在最前面,或者用正则限制 lang
参数(path: '/:lang(en|zh)/about'
),避免路由冲突。
页面刷新后语言“丢了”
单页应用刷新时,内存里的 i18n locale 会重置,解决方法是把语言存到 localStorage/cookie,页面加载时先读存储:
// i18n.js 里初始化时 const storedLang = localStorage.getItem('appLang') || 'en' const i18n = createI18n({ legacy: false, locale: storedLang, messages }) // 语言切换时同步存到 localStorage const switchLang = () => { const newLang = ... localStorage.setItem('appLang', newLang) // 再跳转路由... }
异步加载语言包时路由“卡壳”
如果语言包很大(比如几十种语言),用异步加载(如 import()
)时,路由跳转可能因为语言包没加载完导致白屏,可以结合 Vue 的 Suspense 组件 做加载态,或者在路由守卫里等待语言包加载完成再放行:
// 模拟异步加载语言包 const loadLang = async (lang) => { const messages = await import(`./locales/${lang}.js`) i18n.global.setLocaleMessage(lang, messages.default) } router.beforeEach(async (to, from, next) => { const lang = to.params.lang || 'en' if (!i18n.global.availableLocales.includes(lang)) { await loadLang(lang) // 加载未缓存的语言包 } i18n.global.locale.value = lang next() })
核心逻辑是“语言-路由-内容”联动
把 Vue Router 和 i18n 结合做路由适配,本质是让语言切换驱动路由路径变化,路由变化又触发内容和标题的国际化渲染,关键步骤是:
- 设计带语言标识的路由结构(加前缀或动态参数);
- 用路由守卫统一处理语言检测和 i18n 配置;
- 组件内通过
useI18n
和路由参数实现内容联动; - 语言切换时,根据当前路由信息生成新语言的路由并跳转。
只要把这几个环节打通,再处理好路由匹配、缓存、异步加载这些细节,多语言路由适配就稳了,要是项目里还有更复杂的场景(比如子域名区分语言、动态路由嵌套),原理也是一样的,核心是让语言、路由、内容始终保持同步~
版权声明
本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。