Open zecka opened 9 months ago
My team encountered the same issue just yesterday. We even did a test to check this out and we've been able to replicate it. It seems to be a problem with the last version only 'cause we downgraded and it works client side.
sounds a lot like https://github.com/aralroca/next-translate/issues/1158 to me. this issue makes the package unusable for many projects
Yeah, it's the same issue as #1158. I created a repro: https://codesandbox.io/p/devbox/relaxed-kapitsa-59xwtj
Any updates on this? Because I tend to use one layout tsx for almost all pages where I need consistent layout and this breaks things for me
+1 running into this issue.
I might have a solution here, but it might not be suitable for everyone. For me it does only half the work, because I want to use dynamic language changes via queryParams and this solution does not address this issue (but the content is translated and header on reload is translated as well). I hope someone can provide a more suited solution
Diving into the next.js docs, I found out that
layouts preserve state, remain interactive, and do not re-render.
but there is another option - templates which
are similar to layouts in that they wrap each child layout or page. Unlike layouts that persist across routes and maintain state, templates create a new instance for each of their children on navigation.
That being said, if you have this structure
// src/components/translated-component.tsx
'use client';
import useTranslation from 'next-translate/useTranslation';
export const TranslatedComponent = () => {
const { t } = useTranslation('common');
return <div>This is component that needs translation, translation value: {t('test')}</div>;
};
// src/app/layout.tsx
import type {Metadata} from 'next'
import {ReactNode} from "react";
import TranslatedComponent from "@/components/translated-component";
import './globals.css'
export const metadata: Metadata = {
title: 'Website title',
description: 'Website description',
}
export default function LocaleLayout({ children } : { children: ReactNode }) {
return (
<html lang='en' suppressHydrationWarning>
<body>
<TranslatedComponent />
{children}
</body>
</html>
)
}
and you run into issue mentioned before, you can take out TranslatedComponent
from layout.tsx
(or js, or jsx) and put it into new file template.tsx
. But only moving the component won't solve the issue, you need to use useTranslation
inside template.tsx and pass translated strings as props to problematic element:
// src/components/translated-component.tsx
export const TranslatedComponent = ({value} : {value:string}) => {
return <div>This is component that needs translation, translation value: {value}</div>;
};
// src/app/layout.tsx
import type {Metadata} from 'next'
import {ReactNode} from "react";
import './globals.css'
export const metadata: Metadata = {
title: 'Website title',
description: 'Website description',
}
export default function LocaleLayout({ children } : { children: ReactNode }) {
return (
<html lang='en' suppressHydrationWarning>
<body>
// <TranslatedComponent /> // remove this line
{children}
</body>
</html>
)
}
// src/app/template.tsx
import TranslatedComponent from "@/components/translated-component";
import useTranslation from 'next-translate/useTranslation';
export default function Template({children}: { children: React.ReactNode }) {
const { t } = useTranslation('common');
return <div>
<TranslatedComponent value{t('test')}/>
{children}
<Footer/>
</div>
}
which would result in following nested structure:
<Layout>
<Template>{children}</Template>
</Layout>
Basically template starts doing the work of layout, but with useEffect and other client functions
More info in NextJS documentation I also including link to corrected sandbox that I forked from https://github.com/aralroca/next-translate/issues/1173#issuecomment-1871995857
P.S. I'm not very experienced with next.js and typescript, so if there are any hidden problems with using template file over layout file, I really would like to know. But this setup is working for me so far. P.S.S Before fix I just put my dynamic components in page. But that's weird solution =(
That works for my use case. I think this is not an error with next-translate but with how nextJS works. I think this issue should be closed with this fix
Also, it would be interesting to have this knowledge somewhere in the docs, I think people will be running into this quite frequently
I'm not sure where to add it, though 😅
@acidfernando This still doesn't solve the issue for people who would like to use the dynamic translation by lang={locale}
method. For some reason, the template.tsx
is updated only on page transitions, changing translation on the page translates only content, but not strings in the component in the template.tsx
. You can see that behaviour in my sandbox - clicking on english / french buttons changes translations for body component, but not the component inside template
One thing that might be helpful here is the fact that I experience this when I render the children
behind a client-only
gurad (we use this pattern https://github.com/gfmio/react-client-only)
When component is not wrapped with client only guard the translation is loaded (but only if the client component is rendered after the {children}
.
Do we have some updates regarding this issue? It's pretty huge. I personally had to use another lib to manage translations because of this :-( It's sad because I love this lib 👍
+1, this is a huge breaking issue for us...
This might be a possible solution:
// src/app/layout.tsx
import { TestClient } from "@/components/TestClient";
import { TestServer } from "@/components/TestServer";
import I18nProvider from 'next-translate/AppDirI18nProvider';
export default function RootLayout({ children}: {children: React.ReactNode;}) {
const { config, lang, namespaces } = globalThis.__NEXT_TRANSLATE__;
return (
<html lang="en">
<body>
<I18nProvider
lang={lang}
namespaces={namespaces}
config={JSON.parse(JSON.stringify(config))}
>
<h2>Inside LAYOUT</h2>
<TestClient />
<TestServer />
{children}
</I18nProvider>
</body>
</html>
);
}
Make sure to import Provider like this import I18nProvider from 'next-translate/AppDirI18nProvider';
because next-translate-plugin
also imports this module here https://github.com/aralroca/next-translate-plugin/blob/99c1faefcd7167a7caa309f2affc7d0d807a76e4/src/templateAppDir.ts#L95
globalThis.__NEXT_TRANSLATE__
is available on the server side because next-translate-plugin
actually wrap your component source code with some stuff that is doing side-effects
:
https://github.com/aralroca/next-translate-plugin/blob/99c1faefcd7167a7caa309f2affc7d0d807a76e4/src/templateAppDir.ts#L127
Additionally, AppDirI18nProvider
name is kinda wrong because this component is not a provider, it just doing a side-effect
in the render, which seems pretty wrong.
export default function AppDirI18nProvider({
lang,
namespaces = {},
config,
children,
}: AppDirI18nProviderProps) {
globalThis.__NEXT_TRANSLATE__ = { lang, namespaces, config } // this is a side-effect
// It return children and avoid re-renders and also allow children to be RSC (React Server Components)
return children
}
What might also work is to use Script
component like this:
export default function RootLayout({ children}: {children: React.ReactNode;}) {
const { config, lang, namespaces } = globalThis.__NEXT_TRANSLATE__;
return (
<html lang="en">
<head>
<Script id={lang}>
{`globalThis.__NEXT_TRANSLATE__ = ${{ lang, namespaces, config }}`}
</Script>
</head>
<body>
<h2>Inside LAYOUT</h2>
<TestClient />
<TestServer />
{children}
</body>
</html>
);
}
@aralroca What do you think of this Script
solution?
Script
also have strategy settings to assure it's executed on time https://nextjs.org/docs/app/building-your-application/optimizing/scripts#strategy
@aralroca What do you think of this
Script
solution?Script
also have strategy settings to assure it's executed on time https://nextjs.org/docs/app/building-your-application/optimizing/scripts#strategy
Good to know it @isBatak , probably we can add it internally and solve this AppDirI18nProvider
wrong implementation. Lately I am quite busy, I will try to dedicate some time to it when I can, otherwise any PR will be welcome.
What version of this package are you using? next-translate: ^2.6.2 next-translate-plugin: ^2.6.2 next: 14.0.3
What operating system, Node.js, and npm version? node: 18.17.1 npm: 9.6.7 yarn: 1.22.19
What happened? I'm using app router on nextjs In a client component used on a layout, i use
useTranslation
. On server rende, all translations is correct. But on client side the translation is not loaded.The same component work well when used on page.tsx
Screenshot of render from server (with javascript browser disabled)
Screenshot of render from client(with javascript browser enabled)
How reproduce?
Github repository for this issue: https://github.com/zeckaissue/next-translate-issue-client-on-layout Codesandbox for this issue: https://codesandbox.io/p/github/zeckaissue/next-translate-issue-client-on-layout/main?file=%2Fsrc%2Fapp%2Flayout.tsx%3A21%2C23&workspaceId=08c0217d-10c7-486d-a55b-8417490cf07b
EDIT: