React-i18next: How to implement i18n React example on SSR?

Created on 6 Feb 2019  路  15Comments  路  Source: i18next/react-i18next

Question about react-i18next

Hi. The following React i18n example:
https://github.com/i18next/react-i18next/tree/master/example/react
works well by itself, I mean when ran locally using the given steps, but since Suspense is not yet supported by ReactDOMServer on server-side-rendering I was wondering if anyone had any ideas on how this same example would look like if is intended to be SSR, since it is failing on that condition?

image
Source: https://reactjs.org/docs/code-splitting.html

v10 (hooks)

Most helpful comment

@4lph4-Ph4un

  1. I enabled suspense-free mode on the SSR side:
react: {
    useSuspense: false
}
  1. On every request, I cloned the i18next instance and set the language to the user's.
const i18n = baseI18n.cloneInstance();
i18n.language = getPreferredLanguage(req);
  1. I wrapped my app in an I18nextProvider using that cloned instance:
<I18nextProvider i18n={i18n}>
    [...]
</I18nextProvider>
  1. I provided the props in the response:
const i18nState = await getInitialProps(i18n);

[...]

<script
    defer
    dangerouslySetInnerHTML={{
        __html: `window.__I18N_STATE__=${serialize(i18nState)}`
    }}
/>
  1. Lastly, I just followed the docs for the client-side.

I had some issues with getInitialProps not working with the XHR backend (because it was synchronous). So, I wrote my own version of it! Along with getPreferredLanguage that the Express backend would normally do for you: https://gist.github.com/odensc/216288159aaa2cb41fc924774fce5859 (TypeScript)

All 15 comments

On serverside most implementations will use a proper setup using i18next-express-middleware configured to load all languages, namespaces upfront -> therefore not run into the useTranslation, withTranslation run into throwing a Promise to trigger a Suspense

Thanks @jamuhl , I'm trying i18next-express-middleware out on my project, however I'm getting the following issue:

image

I think perhaps it is because of the latest version I installed using npm ("i18next-express-middleware": "^1.7.1",), that is different from the one used on the examples of i18next-express-middleware ("i18next-express-middleware": "1.0.5", );

you're using this on client?!? if i see the last bit of the printscreen right - "webpack plugin"

the express-middleware is a serverside node.js package...

---> consider using: https://github.com/isaachinman/next-i18next

you're using this on client?!? if i see the last bit of the printscreen right - "webpack plugin"

the express-middleware is a serverside node.js package...

---> consider using: https://github.com/isaachinman/next-i18next

Ok, I'll give it a try, thanks @jamuhl ! :) Since I wasn't involved too deep on the server-side rendering process we're using now, I think probably should've gone for the client side package since the beggining

I also don't understand how this could work? withTranslation and useTranslation both throw to Suspense if translations are loading but Suspense isn't supported for SSR yet (react-dom/server will throw error if you try to renderToString with a <Suspense /> component in tree). Am I to understand that v10 of this library no longer supports SSR until react supports Suspense with SSR?

@nicksrandall Suspense will be only triggered when you render the serverside JSX tree and not all translation files (namespaces) are loaded. If you run into that situation on the server - i would suggest preloading all translations on serverside using preload: ['lng1', 'lng2'] containing all supported languages and ns: ['ns1', 'ns2'] containing all namespaces...

Preloading all translations on serverside is not a bad thing...we do not create a empty i18n instance on request but a clone sharing the translations with the main instance -> so no memory pressure.

Do you have any guidance on how to implement SSR without using express? I used to be able to use loadNamespaces but according to the docs the only way now is to set the i18n instance using i18next-express-middleware?

@odensc there now is a suspense free mode

https://github.com/i18next/react-i18next/issues/735#issuecomment-463572416

not yet had time to document that. sorry

Do you have any guidance on how to implement SSR without using express? I used to be able to use loadNamespaces but according to the docs the only way now is to set the i18n instance using i18next-express-middleware?

Hi @odensc! Currently having same thing on my mind. Did you find any solutions?

@4lph4-Ph4un

  1. I enabled suspense-free mode on the SSR side:
react: {
    useSuspense: false
}
  1. On every request, I cloned the i18next instance and set the language to the user's.
const i18n = baseI18n.cloneInstance();
i18n.language = getPreferredLanguage(req);
  1. I wrapped my app in an I18nextProvider using that cloned instance:
<I18nextProvider i18n={i18n}>
    [...]
</I18nextProvider>
  1. I provided the props in the response:
const i18nState = await getInitialProps(i18n);

[...]

<script
    defer
    dangerouslySetInnerHTML={{
        __html: `window.__I18N_STATE__=${serialize(i18nState)}`
    }}
/>
  1. Lastly, I just followed the docs for the client-side.

I had some issues with getInitialProps not working with the XHR backend (because it was synchronous). So, I wrote my own version of it! Along with getPreferredLanguage that the Express backend would normally do for you: https://gist.github.com/odensc/216288159aaa2cb41fc924774fce5859 (TypeScript)

Nice! I'll have a look along that kind of path! :) Seems doable! Thank you very much!

Thanks for the code @odensc . I found i18next-node-fs-backend, and I am using that.

Although I am struggling getting the initial data out of my store. Maybe it's not initialised correctly, or maybe I can't get it out. Which version of i18next are you using? As for me i18n.reportNamespaces does not exist on my i18n object, or in the types.

I even tried simply copying the code here, and this didn't work either. Although I think there is something wrong with my config:

i18next
    // pass the i18n instance to react-i18next.
    .use(initReactI18next)
    // use node-fs instead of xhr backend
    .use(nodeFsBackend)
    .init({
        keySeparator: '__KEY__',
        defaultNS: 'translation',
        interpolation: {
            // react is already safe from xss
            escapeValue: false,
        },
        backend: {
            loadPath: path.resolve(__dirname, `/locales/{{lng}}/{{ns}}`),
            parse: (data: any) => data,
        },
        react: {
            useSuspense: false,
        },
    })

Then in my controller I do something like this:

    const lang = detectLanguageFromUser(req)
    const i18n = i18next.cloneInstance()
    i18n.language = lang
    await i18n.loadLanguages(lang)

And I am stuck here :D

Hi @bitttttten, I'm not quite sure how to solve your issue. I'm using v17.0.6 and reportNamespaces exists on my i18n instance.

It's just the types that are out of date.. if I use your code with (i18n as any).reportNamespaces.getUsedNamespaces() it works, otherwise TS complains with "Property 'reportNamespaces' does not exist on type 'i18n'.".

Thanks!

I just tell about my case:
1) One of module translate has been skiped on node
2) When some translates are missing react-i18next use suspense.
3) React DOM can`t use suspense and build tree on server
4) For some components I pass translates by a component like <T name="mycomponent.title" />
5) Some components did mount only on client and this was no error but another do crash on server

Just load all i18n resource on server =)

Was this page helpful?
0 / 5 - 0 ratings