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

为vite-plugin-ssr集成react-i18next:i18next未初始化问题

在vite-plugin-ssr中解决react-i18next未初始化报错并实现预加载翻译

我有一个基于vite-plugin-ssr的React项目,尝试通过react-i18next添加国际化支持。已完成i18n实例配置,并在服务端和客户端渲染代码中通过I18nextProvider提供实例,但使用<Trans>组件时出现以下错误:

i18n: hasLoadedNamespace: i18next was not initialized ['en']
i18next::translator: key "hello" for languages "en" won't get resolved as namespace "translation" was not yet loaded This means something IS WRONG in your setup. You access the t function before i18next.init / i18next.loadNamespace / i18next.changeLanguage was done. Wait for the callback or Promise to resolve before accessing it!!! 

需要解决两个核心问题:

  1. 确保i18next完全初始化后再进行SSR渲染(无法使用useEffect等待)
  2. 构建时预加载翻译资源,避免客户端渲染时的内容闪烁

解决方案

1. 预加载翻译资源(构建时打包)

替换动态加载资源的方式,改用vite的import.meta.glob在构建时预加载所有翻译文件,避免运行时动态加载的延迟。

修改i18n.ts:

// i18n.ts
import i18n from 'i18next'
import { initReactI18next } from 'react-i18next'
import LanguageDetector from 'i18next-browser-languagedetector'

// 预加载所有locales下的json文件,eager模式会在构建时加载
const localeFiles = import.meta.glob('../locales/**/*.json', { eager: true })
const resources: Record<string, Record<string, any>> = {}

// 解析预加载的文件,整理成i18n需要的resources格式
for (const filePath in localeFiles) {
  const match = filePath.match(/../locales/([a-z-]+)/([a-z]+)\.json/)
  if (match) {
    const [, language, namespace] = match
    if (!resources[language]) {
      resources[language] = {}
    }
    resources[language][namespace] = localeFiles[filePath].default
  }
}

const i18nInstance = i18n
  .use(initReactI18next)
  .use(LanguageDetector)
  .init({
    resources, // 直接传入预加载的资源
    fallbackLng: 'en',
    debug: true,
    interpolation: {
      escapeValue: false, // React已处理XSS
    },
  })

export default i18nInstance
// 导出初始化Promise,供渲染函数等待
export const i18nInitPromise = i18nInstance

2. 服务端渲染:等待初始化并同步语言

在服务端渲染前,必须等待i18n初始化完成,并确保当前语言的翻译资源已加载,同时将初始语言传递给客户端,避免hydrate不匹配。

修改_default.page.server.tsx:

// _default.page.server.tsx
import i18n, { i18nInitPromise } from './i18n'
import ReactDOMServer from 'react-dom/server'
import { I18nextProvider } from 'react-i18next'

async function render(pageContext: PageContextServer) {
  // 等待i18n初始化完成
  await i18nInitPromise

  // 获取当前语言(可根据请求头、路由参数等调整逻辑)
  const currentLang = pageContext.request?.headers['accept-language']?.split(',')[0] || 'en'
  // 切换语言并等待对应资源加载完成
  await i18n.changeLanguage(currentLang)

  const { Page, pageProps } = pageContext
  const pageHtml = ReactDOMServer.renderToString(
    <PageShell pageContext={pageContext}>
      <I18nextProvider i18n={i18n}>
        <Page {...pageProps} />
      </I18nextProvider>
    </PageShell>
  )

  // 将初始语言注入到HTML,供客户端读取
  return {
    documentHtml: `
      <!DOCTYPE html>
      <html lang="${currentLang}">
        <head>
          <meta charset="UTF-8">
          <title>Vite SSR I18n</title>
        </head>
        <body>
          <div id="page-view">${pageHtml}</div>
          <script>window.__INITIAL_LANG__ = "${currentLang}";</script>
        </body>
      </html>
    `,
  }
}

export default render

3. 客户端渲染:同步服务端语言并等待初始化

客户端渲染时,先等待i18n初始化,再使用服务端传递的初始语言,避免客户端检测语言导致的内容闪烁和hydrate错误。

修改_default.page.client.tsx:

// _default.page.client.tsx
import i18n, { i18nInitPromise } from './i18n'
import { hydrateRoot } from 'react-dom/client'
import { I18nextProvider } from 'react-i18next'

async function render(pageContext: PageContextClient) {
  // 等待i18n初始化完成
  await i18nInitPromise
  // 使用服务端传递的初始语言,避免客户端重新检测
  const initialLang = (window as any).__INITIAL_LANG__ || 'en'
  await i18n.changeLanguage(initialLang)

  const { Page, pageProps } = pageContext
  hydrateRoot(
    document.getElementById('page-view')!,
    <PageShell pageContext={pageContext}>
      <I18nextProvider i18n={i18n}>
        <Page {...pageProps} />
      </I18nextProvider>
    </PageShell>
  )
}

export default render

关键说明

  • 预加载资源:通过import.meta.glob({ eager: true })让vite在构建时将所有翻译文件打包进代码,彻底避免运行时动态加载的延迟和闪烁。
  • 等待初始化:i18n的init方法返回Promise,必须等待其resolve后才能使用翻译功能,否则会触发未初始化的报错。
  • 服务端-客户端语言同步:将服务端确定的语言传递给客户端,确保hydrate时内容一致,同时减少客户端的语言检测步骤。

内容的提问来源于stack exchange,提问作者Malcolm Crum

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 00:00:46