Search code examples
reactjsserver-side-renderingvitereact-i18next

Adding react-i18next to vite-plugin-ssr project: i18next was not initialized


I have a relatively simple vite-plugin-ssr project that I am trying to add internationalization support to using react-i18next. I have set up my i18n instance:

// i18n.ts
i18n.use(initReactI18next) // passes i18n down to react-i18next
    .use(LanguageDetector)
    .use(resourcesToBackend((language, ns) => import(`../locales/${language}/${ns}.json`)))
    .init({
        fallbackLng: "en",
        debug: true,

        interpolation: {
            escapeValue: false // react already safes from xss => https://www.i18next.com/translation-function/interpolation#unescape
        }
    })
export default i18n

And I've imported and provided it with a I18nextProvider in my client and server rendered code:

// _default.page.server.tsx
import i18n from "./i18n";
async function render(pageContext: PageContextServer) {
  [...]
  const pageHtml = ReactDOMServer.renderToString(
    <PageShell pageContext={pageContext}>
      <I18nextProvider i18n={i18n}>
        <Page {...pageProps} />
      </I18nextProvider>
    </PageShell>
  )
  [...]
}
// _default.page.client.tsx
import i18n from "./i18n";
async function render(pageContext: PageContextClient) {
  const { Page, pageProps } = pageContext
  hydrateRoot(
    document.getElementById('page-view')!,
    <PageShell pageContext={pageContext}>
      <I18nextProvider i18n={i18n}>
        <Page {...pageProps} />
      </I18nextProvider>
    </PageShell>
  )
}

However, when I load my page that uses a <Trans> component, I get the following errors:

i18next: 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!!! 

How should I ensure i18next is loaded? I could imagine checking it in a useEffect and rendering nothing until the promise resolves, but I don't think useEffects are rendered in SSR (?). Ideally during build time I would look up all my translations and bake them in so no waiting or flickering occurred.

Here is a repo demonstrating the issue: https://github.com/crummy/vite-ssr-i18n


Solution

  • Seems like the <Trans> component does not use the provided i18n automatically. I resolved the issue by modifying my page from this:

    function Page() {
        return <h1><Trans>hello</Trans></h1>
    }
    

    To this:

    function Page() {
        const { t } = useTranslation();
        
        return <h1><Trans t={t}>hello</Trans></h1>
    }