Vue3 i18n SSR下动态切换语言的Hydration不匹配问题
解决Quasar Vue3 SSR + vue-i18n URL参数切换语言的Hydration不匹配问题
问题根源在于服务端渲染时已使用目标语言(如pt)生成内容,但客户端初始化时默认使用了en-US,之后才通过路由中间件切换语言,导致客户端Hydration阶段预期的文本和服务端返回的文本不一致。
解决方案步骤:
1. 服务端渲染前提前设置Locale
在SSR入口文件中,解析URL中的locale参数,先设置i18n的locale再渲染页面,同时将该locale传递给客户端。
修改服务端入口(比如src/ssr/server.ts):
import { createSSRApp } from 'vue' import { createI18n } from 'vue-i18n' import { createMemoryHistory, createRouter } from 'vue-router' import App from '../App.vue' import routes from '../router/routes' import messages from '../i18n/messages' // 你的语言包文件 // 支持的有效语言列表 const VALID_LOCALES = ['en-US', 'pt', 'zh-CN'] export function createApp({ url }: { url: string }) { // 初始化i18n const i18n = createI18n({ legacy: false, locale: 'en-US', messages, }) // 解析URL中的locale参数 const pathSegments = new URL(url, 'http://localhost').pathname.split('/') const paramLocale = pathSegments[1] || 'en-US' const targetLocale = VALID_LOCALES.includes(paramLocale) ? paramLocale : 'en-US' // 提前设置i18n locale i18n.global.locale.value = targetLocale // 创建路由并跳转 const router = createRouter({ history: createMemoryHistory(), routes, }) router.push(url) const app = createSSRApp(App) .use(router) .use(i18n) return { app, router, i18n } } // 渲染HTML时注入初始化locale到客户端 export async function renderToString(url: string) { const { app, router, i18n } = createApp({ url }) await router.isReady() const appHtml = await renderToString(app) return ` <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Your App</title> </head> <body> <div id="q-app">${appHtml}</div> <!-- 传递服务端的locale给客户端 --> <script> window.__INITIAL_LOCALE__ = "${i18n.global.locale.value}" </script> <script src="/client.js"></script> </body> </html> ` }
2. 客户端初始化时复用服务端传递的Locale
修改客户端的i18n初始化文件(比如src/boot/i18n.ts),优先使用服务端注入的locale:
import { createI18n } from 'vue-i18n' import messages from '../i18n/messages' // 支持的有效语言列表 const VALID_LOCALES = ['en-US', 'pt', 'zh-CN'] // 从服务端注入的全局变量获取初始化locale const initialLocale = (window as any).__INITIAL_LOCALE__ || 'en-US' export const i18n = createI18n({ legacy: false, locale: VALID_LOCALES.includes(initialLocale) ? initialLocale : 'en-US', messages, })
3. 调整路由中间件逻辑
确保中间件仅在locale变化时更新,同时兼容服务端和客户端:
// i18/index.ts import { i18n } from './i18n' // 引入你的i18n实例 const VALID_LOCALES = ['en-US', 'pt', 'zh-CN'] export const routerTranslator = { async routeMiddleware(to, _from, next) { let paramLocale = to.params.locale as string | undefined const targetLocale = paramLocale && VALID_LOCALES.includes(paramLocale) ? paramLocale : 'en-US' // 仅当当前locale与目标locale不一致时更新,避免无效操作 if (i18n.global.locale.value !== targetLocale) { i18n.global.locale.value = targetLocale } next() }, }
关键说明:
- 服务端提前设置locale:确保SSR生成的HTML内容是目标语言,避免客户端Hydration时的文本 mismatch。
- 客户端复用服务端locale:让客户端初始化时的语言环境和服务端完全一致,Hydration过程就能匹配。
- 验证locale有效性:防止非法locale参数导致的语言包加载错误。
内容的提问来源于stack exchange,提问作者Pavarine
相关产品推荐
相关产品推荐

