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前端网发表,如需转载,请注明页面地址。
code前端网



发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。