Closed Christian-Schoenlein closed 2 years ago
Yes, that's the nature of client side loaded translations... On server side, the translations are ready, but on client side they need first to be loaded. So they are not yet ready on the first render loop. That's why in the example it is checked for the ready flag and if not ready a fallback will be rendered: https://github.com/i18next/i18next-http-backend/blob/master/example/next/pages/client-page.js#L11 btw: normally it is only a warning, not an error: "Warning: Expected server HTML to contain a matching text node for "loading translations..." in
fyi: I'm not a Next.js expert, but I think there is nothing we can do here if you want to use client side loading. Correct me if I'm wrong @isaachinman
Yes, what @adrai described is a correct explanation of what is happening. It's important to note that that is happening specifically because of this user's choice of backend
.
In NextJs apps, the correct pattern is to:
pageProps
pageProps
on first render, thus no hydration errorsI am aware of the pattern but I was trying to translate the navbar & footer of my app using a Layout which cannot use SSR or SSG since it's not a NextPage.
I also tried using the ready flag which doesn't help because it doesn't solve the problem that whats rendered on the server isn't the same as what's rendered on the client initialy.
But I found a solution instead of having the translation in the return statement directly I moved it in a useEffect which only gets executed on the client side and stored the translation in a useState value. On the server the useEffect doesn't run and the translated string is undefined and on the client it will be undefined for the first render because the translation file is not available yet since its async.
which cannot use SSR or SSG since it's not a NextPage
That is simply not true. You just need to pass the appropriate namespace via your page itself, or put it as defaultNS
.
That is simply not true. You just need to pass the appropriate namespace via your page itself, or put it as defaultNS.
In Next.js Layouts are located inside the custom app component which is not a next page.
From the docs:
Inside your layout, you can fetch data on the client-side using useEffect or a library like SWR. Because this file is not a Page, you cannot use getStaticProps or getServerSideProps currently.
So you can't just pass down the namespace because the layout lives above every single next page and wraps around them.
I can assure you it's possible.
A solution for this problem should be included in the readme file, as the example doesn't work (at least not without errors).
With React 18 you can enable useSuspense
option
/**
* @type {import('i18next').InitOptions & Pick<import('next').NextConfig, 'i18n'>}
*/
module.exports = {
backend: {
backendOptions: [
{ expirationTime: 60 * 60 * 1000 },
{
/* loadPath: 'https:// somewhere else' */
},
], // 1 hour
backends: typeof window !== 'undefined' ? [LocalStorageBackend, HttpBackend] : [],
},
i18n: {
defaultLocale: 'en-US',
locales: ['en-US'],
},
serializeConfig: false,
use: typeof window !== 'undefined' ? [ChainedBackend] : [],
react: {
useSuspense: true,
},
};
any update?
why this is closed with no solution? I think this is related to the rename of hydrate
functionality in react 18 to hydrateRoot
https://nextjs.org/docs/messages/react-hydration-error
https://reactjs.org/docs/react-dom.html#hydrate
it works fine in react 17 though
With React 18 you can enable
useSuspense
option/** * @type {import('i18next').InitOptions & Pick<import('next').NextConfig, 'i18n'>} */ module.exports = { backend: { backendOptions: [ { expirationTime: 60 * 60 * 1000 }, { /* loadPath: 'https:// somewhere else' */ }, ], // 1 hour backends: typeof window !== 'undefined' ? [LocalStorageBackend, HttpBackend] : [], }, i18n: { defaultLocale: 'en-US', locales: ['en-US'], }, serializeConfig: false, use: typeof window !== 'undefined' ? [ChainedBackend] : [], react: { useSuspense: true, }, };
with useSuspense
set to true the error disappear, but somehow it breaks some assets used in nextjs, like the use of next/image
,
@isBatak
@bryantobing12 how can that be related?
Maybe next/image
breaks when it's wrapped in <Suspense>
. Could you check that in isolation?
@isBatak I'm not sure. next/link SPA seems to break as well
wrapping my top component with <Suspense>
fix the issue
<React.Fragment>
<React.Suspense fallback={<div>Loading</div>}>
<NProgress stopDelayMs={0} />
<Component {...pageProps} />
</React.Suspense>
</React.Fragment>
I managed to solve this by preventing any client translations from being returned before the server-client cycle completes. Something like if (!hasRendered) return null
, but it's kinda exhaustive to put this line before the return statement of every client component. Instead, I've adjusted my useTranslations
hook a bit, making it return only the same values that were passed initially.
This's how useTranslations
return statement looked like (Hydration Error):
return {
translation: (key: string) => data?.[key] ?? key,
loading: isLoading,
locale: loc,
dict: data ?? {}, // NOTE: This is useful for passing translations as a prop, and for debugging.
refetchTranslations: refetch,
};
Added Lines (to the hook itself, not components):
const [hasRendered, setHasRendered] = React.useState(false);
React.useEffect(() => {
setHasRendered(true);
}, []);
if (!hasRendered) {
return {
translation: (key: string) => key,
loading: true,
locale: loc,
dict: {},
refetchTranslations: refetch,
};
}
You may also return null
if not yet rendered if you'd use the translations
function accordingly in your code.
๐ Bug Report
When using client side translation in development running
next dev
on page refresh react throws the following error:Error: Text content does not match server-rendered HTML.
The following errors appear in the console:
Text content did not match. Server: "Hello World" Client: "hello_world"
Error message running production server
next build
andnext start
.To Reproduce
The bug can be reproduced with the official next example and react & react-dom version 18.1.0.
Expected behavior
No hydration error.
Your Environment