Vue.js中使用vue-i18n为路由添加本地化前缀的实现方案
实现URL带本地化前缀的最优方案
这个需求我之前在Vue 2项目里落地过,核心是不用把所有路由硬套成/:locale?的子路由,而是通过路由守卫动态处理locale前缀,同时调整i18n和路由的初始化逻辑,解决两者同时初始化的顺序问题。以下是分步实现:
1. 重构Locale工具模块,支持动态获取/设置
首先把你的locale.js从导出固定值改成可复用的工具函数,方便后续动态同步locale:
// @/services/locale.js import store from '@/vuex/index' const defaultLocale = 'en_US' const supportedLocales = ['en_US', 'ru_RU'] // 映射URL短码到完整locale(比如en → en_US) export const localeShortMap = { en: 'en_US', ru: 'ru_RU' } export const reverseLocaleMap = { en_US: 'en', ru_RU: 'ru' } // 获取初始化locale(兼容登录用户/本地存储/默认值) export function getInitialLocale() { let locale if (store.getters['auth/authenticated']) { locale = store.getters['auth/currentUser'].locale || defaultLocale } else { locale = localStorage.getItem('locale') || defaultLocale } // 确保是支持的locale,避免非法值 return supportedLocales.includes(locale) ? locale : defaultLocale } // 同步locale到本地存储(若用户登录可同步到Vuex) export function setLocale(locale) { if (supportedLocales.includes(locale)) { localStorage.setItem('locale', locale) // 可选:如果用户已登录,更新Vuex中的用户locale // if (store.getters['auth/authenticated']) { // store.dispatch('auth/updateUserLocale', locale) // } } }
2. 调整i18n初始化,支持动态修改locale
修改i18n.js,让实例可以动态更新locale,同时用新的工具函数获取初始值:
// @/services/i18n.js import Vue from 'vue' import VueI18n from 'vue-i18n' import { getInitialLocale } from '@/services/locale' Vue.use(VueI18n) const fallbackLocale = 'en_US' export const i18n = new VueI18n({ locale: getInitialLocale(), fallbackLocale, }) // 加载语言包(可改成异步加载优化性能,见下文) i18n.setLocaleMessage('ru_RU', require('@/lang/ru_RU.json')) i18n.setLocaleMessage('en_US', require('@/lang/en_US.json'))
3. 路由守卫处理Locale前缀解析与重定向
这是核心步骤:在路由全局前置守卫里解析URL的locale前缀,同步到i18n,同时自动重定向无前缀的URL到带前缀的版本。修改你的路由配置文件(比如@/router/index.js):
// @/router/index.js import Vue from 'vue' import VueRouter from 'vue-router' import { i18n } from '@/services/i18n' import { getInitialLocale, localeShortMap, reverseLocaleMap, setLocale } from '@/services/locale' Vue.use(VueRouter) // 你的原有路由配置不用改,比如: const routes = [ { path: '/profile', name: 'Profile', component: () => import('@/views/Profile.vue') }, { path: '/settings', name: 'Settings', component: () => import('@/views/Settings.vue') }, // ...其他路由 ] const router = new VueRouter({ mode: 'history', base: process.env.BASE_URL, routes, }) // 全局前置守卫处理locale router.beforeEach((to, from, next) => { let targetLocale = to.params.locale // 1. 解析URL中的locale参数 if (targetLocale) { // 把短码转换成完整locale(比如en → en_US) targetLocale = localeShortMap[targetLocale] || targetLocale // 校验是否是支持的locale,不支持则用默认 if (!localeShortMap[targetLocale] && !Object.values(localeShortMap).includes(targetLocale)) { targetLocale = getInitialLocale() } } else { // URL无locale前缀,从本地存储/默认值获取 targetLocale = getInitialLocale() } // 2. 同步locale到i18n和本地存储 if (i18n.locale !== targetLocale) { i18n.locale = targetLocale setLocale(targetLocale) } // 3. 重定向无前缀的URL到带前缀的版本 if (!to.params.locale) { const shortLocale = reverseLocaleMap[targetLocale] // 保留原URL的query和hash return next({ path: `/${shortLocale}${to.path}`, query: to.query, hash: to.hash, replace: true, // 重定向不留下历史记录 }) } next() }) export default router
4. 优化路由链接生成(自动带Locale前缀)
为了避免手动写/en/profile这种路径,自定义一个全局的locale-router-link组件,自动拼接当前locale前缀:
<!-- @/components/LocaleRouterLink.vue --> <template> <router-link :to="localizedTo"> <slot></slot> </router-link> </template> <script> import { reverseLocaleMap } from '@/services/locale' export default { props: ['to'], computed: { localizedTo() { const shortLocale = reverseLocaleMap[this.$i18n.locale] if (typeof this.to === 'string') { // 处理字符串路径 return `/${shortLocale}${this.to.startsWith('/') ? this.to : `/${this.to}`}` } else { // 处理对象形式的路由参数 return { ...this.to, path: `/${shortLocale}${(this.to.path || '').startsWith('/') ? this.to.path : `/${this.to.path || ''}`}`, } } }, }, } </script>
然后在main.js全局注册:
import LocaleRouterLink from '@/components/LocaleRouterLink.vue' Vue.component('locale-router-link', LocaleRouterLink)
之后用<locale-router-link to="/profile">我的主页</locale-router-link>就会自动生成当前locale的链接,比如/en/profile或/ru/profile。
可选优化:异步加载语言包
如果语言包体积较大,改成异步加载减少初始包大小:
// @/services/i18n.js 添加异步加载函数 export async function loadLocaleMessages(locale) { const messages = await import(`@/lang/${locale}.json`) i18n.setLocaleMessage(locale, messages.default) }
然后在路由守卫里调用:
// router/index.js 中的beforeEach改成异步 router.beforeEach(async (to, from, next) => { // ...前面的locale解析逻辑 // 如果当前locale的语言包未加载,先异步加载 if (!i18n.getLocaleMessage(targetLocale)) { await loadLocaleMessages(targetLocale) } // ...后面的同步和重定向逻辑 })
内容的提问来源于stack exchange,提问作者Victor
相关产品推荐
相关产品推荐

