You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 13:39:53