Code前端首页关于Code前端联系我们

Vue Router 搭配 i18n 怎么做多语言路由适配?

terry 5小时前 阅读数 8 #Vue
文章标签 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,组件里要获取 langid,同时渲染对应语言的产品名称:

<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 结合做路由适配,本质是让语言切换驱动路由路径变化,路由变化又触发内容和标题的国际化渲染,关键步骤是:

  1. 设计带语言标识的路由结构(加前缀或动态参数);
  2. 用路由守卫统一处理语言检测和 i18n 配置;
  3. 组件内通过 useI18n 和路由参数实现内容联动;
  4. 语言切换时,根据当前路由信息生成新语言的路由并跳转。

只要把这几个环节打通,再处理好路由匹配、缓存、异步加载这些细节,多语言路由适配就稳了,要是项目里还有更复杂的场景(比如子域名区分语言、动态路由嵌套),原理也是一样的,核心是让语言、路由、内容始终保持同步~

版权声明

本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。

发表评论:

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

热门