i18next / next-i18next

The easiest way to translate your NextJs apps.
https://next.i18next.com
MIT License
5.53k stars 762 forks source link

react-i18next:: You will need to pass in an i18next instance by using initReactI18next when running build when there is translation in top level layout #1917

Closed bryanltobing closed 1 year ago

bryanltobing commented 2 years ago

🐛 Bug Report

image

This is only happening when I run build script and my layout call useTranslation hook

To Reproduce

A small repo to reproduce

This is the only place I call useTranslation hook

// My _app.tsx
import "../styles/globals.css";
import type { AppProps } from "next/app";
import { appWithTranslation, useTranslation } from "next-i18next";
import React from "react";

function MyApp({ Component, pageProps }: AppProps) {
  return (
    <Layout>
      <Component {...pageProps} />
    </Layout>
  );
}

const Layout: React.FC<{ children: React.ReactNode }> = ({ children }) => {
  const { t } = useTranslation();

  return (
    <div>
      <div>{t("hello-world")}</div>
      {children}
    </div>
  );
};

export default appWithTranslation(MyApp);

my only page is pages/index.tsx

and I already called the serverSideTranslation

export async function getStaticProps({ locale }: any) {
  return {
    props: {
      ...(await serverSideTranslations(locale, ["common"])),
    },
  };
}

the translation is working, and I believe as mentioned here https://github.com/i18next/next-i18next/issues/1840#issuecomment-1145614232 image

the only issue is that there are warnings happen when running build

Expected behavior

There's no warning that says react-i18next:: You will need to pass in an i18next instance by using initReactI18next when running build script

Your Environment

vitoropereira commented 1 year ago

I just fixed the error. I had to put these versions: "react-i18next": "^12.2.0", "i18next": "^22.4.10",

belgattitude commented 1 year ago

Nice. Can you give the output of 'npm why -r react-i18next i18next'

And then let me know if you're importing useTranslation from react-i18next or from next-i18next ?

vitoropereira commented 1 year ago

npm why -r react-i18next i18next

image

Remembering that these data are with the application working correctly. If you want, I can go back to the previous state, with a bug, and redo what you requested. And I do importging the useTranslation from "next-i18next".

belgattitude commented 1 year ago

data are with the application working correctly

Just to confirm, to make it work you've downgraded versions right ? Or I miss something cause it looks fine.

If you want, I can go back to the previous state

Yes that would be interesting for me to be sure about what I wrote in the https://github.com/i18next/next-i18next/blob/master/TROUBLESHOOT.md#how-to-debug-installation. I've tested yarn and pnpm but not yet npm. And this might be related to your error message as well

vitoropereira commented 1 year ago

Just to confirm, to make it work you've downgraded versions right ? Or I miss something cause it looks fine.

Yes. I made the downgraded.

Yes that would be interesting for me to be sure about what I wrote in the https://github.com/i18next/next-i18next/blob/master/TROUBLESHOOT.md#how-to-debug-installation. I've tested yarn and pnpm but not yet npm. And this might be related to your error message as well

I don't know why but when updating the version to "react-i18next": "^12.3.1", "i18next": "^22.5.0", "next-i18next": "^13.2.2", now it just appears the alert: Generating static pages (0/34)react-i18next:: You will need to pass in an i18next instance by using initReactI18next react-i18next:: You will need to pass in an i18next instance by using initReactI18next.

But it doesn't give an error when building. And then the system is not working anymore. If you look at https://www.smartmeet.digital you will see that.

Can you give the output of 'npm why -r react-i18next i18next'

After updade the version... image

Now I'm going to try to get it working again, just local. If I get it I'll get back to you here.

belgattitude commented 1 year ago

Could you try to run

npm dedupe npx -y rimraf --glob '**/node_modules' npm install

And see if the see if the problem persist. I would be very grateful 🙏

And if it doesn't I'll try to reproduce

Thanks a lot

vitoropereira commented 1 year ago

Here is the sequence of the test I did now: First my package.json.

image

After I run: npm why -r react-i18next i18next

image

Then... npm dedupe

image

npx -y rimraf --glob '**/node_modules'

image

npm install

image

Perhaps the error is in some configuration. Then here's some other data:

Part of _app.jsx

image

image

image

image

So far the application is with the bug. It's not working properly.

belgattitude commented 1 year ago

Thank you so much... Can I ask you 2 more things:

  1. Pass the next-i18next.config in appWithTranslation
// _app.tsx
import type { AppProps } from 'next/app'
import { appWithTranslation } from 'next-i18next'
import nextI18NextConfig from '../next-i18next.config'
const MyApp = ({ Component, pageProps }: AppProps) => (
  <Component {...pageProps} />
)
export default appWithTranslation(MyApp, nextI18NextConfig)
  1. In AboutUs, same idea
import nextI18nextConfig from '../../next-i18next.config'
//...
   ...(await serverSideTranslations(locale, namespacesRequired, config)
//...

PS: see https://github.com/i18next/next-i18next/blob/master/TROUBLESHOOT.md#how-to-explicitly-pass-the-config

If it fixes the issue, I think I know where to look for and might take to time to rework it in there https://github.com/i18next/next-i18next/pull/1973 (esmodules and dual packaging)

vitoropereira commented 1 year ago

I don't know what happened, I just rebuild the application and it works again... 🤷🏻‍♂️ before I do nothing

image

After I do rebuild.

image

avb7 commented 1 year ago

I am using next-i18next which has a react-i18next dependency. I updated my project to use ESM by adding "type": "module" to my package.json

I had the same problem after this change to my package.json. "react-i18next:: You will need to pass in an i18next instance by using initReactI18next "

The issue for me was resolved by removing "type": "module" from the package.json and renaming the next.config.cjs back to next.config.js.

Root Cause for my setup: The "type": "module" setting tells Node.js to treat .js files as ESM. By default, Node.js treats .js files as CommonJS modules. However, as for next-i18next, it seems there was still ongoing discussion and no official support yet for ESM syntax

The issue with useTranslation appears to have been a side effect of a conflict in the module system. After removing "type": "module" from package.json (thereby treating .js files as CommonJS modules by default), I could rename next.config.cjs back to next.config.js and useTranslation started functioning correctly again.

Sharing this experience here for anyone who might encounter a similar situation. Further investigation might be required to see if there's a more effective way to handle this transition or to clarify the documentation on how Next.js and react-i18next handle ESM.

belgattitude commented 1 year ago

Cannot promise, but I'll try to come with a pr for full esm. Thanks for digging into it.

FYI I have a repo https://github.com/belgattitude/nextjs-monorepo-example where esm seem to work without warning but my setup explicitly set the config https://github.com/i18next/next-i18next/blob/master/TROUBLESHOOT.md#how-to-explicitly-pass-the-config... see https://github.com/belgattitude/nextjs-monorepo-example/blob/main/apps/nextjs-app/src/backend/i18n/getServerTranslations.ts

rahad06 commented 9 months ago

having the same error in a remix app using react-i18next react-i18next:: You will need to pass in an i18next instance by using initReactI18next which i have correctly passed already: /**

import {PassThrough} from "node:stream"; import {createReadableStreamFromReadable} from "@remix-run/node"; import {RemixServer} from "@remix-run/react"; import isbot from "isbot"; import {renderToPipeableStream} from "react-dom/server"; import {CacheProvider} from '@emotion/react'; import ServerStyleContext from '../app/styles/server.context'; import createEmotionCache from '../app/styles/createEmotionCache'; import createEmotionServer from '@emotion/server/create-instance'; import {renderToString} from 'react-dom/server'; import {createInstance} from "i18next"; import Backend from "i18next-fs-backend"; import {resolve} from "node:path"; import {I18nextProvider, initReactI18next} from "react-i18next"; import i18next from "./i18next.server"; // The backend file we created import i18n from "./i18n"; // The configuration file we created const ABORT_DELAY = 5_000;

export default async function handleRequest( request, responseStatusCode, responseHeaders, remixContext, ) { const cache = createEmotionCache(); const {extractCriticalToChunks} = createEmotionServer(cache);

const html = renderToString(
    <ServerStyleContext.Provider value={null}>
        <CacheProvider value={cache}>
            <RemixServer
                context={remixContext}
                url={request.url}
            />
        </CacheProvider>
    </ServerStyleContext.Provider>,
);

const chunks = extractCriticalToChunks(html);

const markup = renderToString(
    <ServerStyleContext.Provider value={chunks.styles}>
        <CacheProvider value={cache}>
            <RemixServer
                context={remixContext}
                url={request.url}
            />
        </CacheProvider>
    </ServerStyleContext.Provider>,
);
let instance = createInstance();

// We can detect the specific locale from each request
let lng = await i18next.getLocale(request);
// The namespaces the routes about to render wants to use
let ns = i18next.getRouteNamespaces(remixContext);
let callbackName = isbot(request.headers.get("user-agent"))
    ? "onAllReady"
    : "onShellReady";
await instance
    .use(initReactI18next)
    .use(Backend)
    .init({
        ...i18n,
        lng,
        ns,
        backend: {
            loadPath: resolve("./public/locales/{{lng}}/{{ns}}.json"),
        },
    });
return new Promise((resolve, reject) => {
    let didError = false;

    let {pipe, abort} = renderToPipeableStream(
        <I18nextProvider i18n={instance}>
            <RemixServer context={remixContext} url={request.url}/>
        </I18nextProvider>,
        {
            [callbackName]: () => {
                let body = new PassThrough();

                responseHeaders.set("Content-Type", "text/html");

                resolve(
                    new Response("<!DOCTYPE html>" + body, {
                        headers: responseHeaders,
                        status: didError ? 500 : responseStatusCode,
                    })
                );

                pipe(body);
            },
            onShellError(error) {
                reject(error);
            },
            onError(error) {
                didError = true;

                console.error(error);
            },
        }
    );

    setTimeout(abort, ABORT_DELAY);
})

}

function handleBotRequest( request, responseStatusCode, responseHeaders, remixContext ) { return new Promise((resolve, reject) => { let shellRendered = false; const {pipe, abort} = renderToPipeableStream( <RemixServer context={remixContext} url={request.url} abortDelay={ABORT_DELAY} />, { onAllReady() { shellRendered = true; const body = new PassThrough(); const stream = createReadableStreamFromReadable(body);

                responseHeaders.set("Content-Type", "text/html");

                resolve(
                    new Response(stream, {
                        headers: responseHeaders,
                        status: responseStatusCode,
                    })
                );

                pipe(body);
            },
            onShellError(error) {
                reject(error);
            },
            onError(error) {
                responseStatusCode = 500;
                // Log streaming rendering errors from inside the shell.  Don't log
                // errors encountered during initial shell rendering since they'll
                // reject and get logged in handleDocumentRequest.
                if (shellRendered) {
                    console.error(error);
                }
            },
        }
    );

    setTimeout(abort, ABORT_DELAY);
});

}

function handleBrowserRequest( request, responseStatusCode, responseHeaders, remixContext ) { return new Promise((resolve, reject) => { let shellRendered = false; const {pipe, abort} = renderToPipeableStream( <RemixServer context={remixContext} url={request.url} abortDelay={ABORT_DELAY} />, { onShellReady() { shellRendered = true; const body = new PassThrough(); const stream = createReadableStreamFromReadable(body);

                responseHeaders.set("Content-Type", "text/html");

                resolve(
                    new Response(stream, {
                        headers: responseHeaders,
                        status: responseStatusCode,
                    })
                );

                pipe(body);
            },
            onShellError(error) {
                reject(error);
            },
            onError(error) {
                responseStatusCode = 500;
                // Log streaming rendering errors from inside the shell.  Don't log
                // errors encountered during initial shell rendering since they'll
                // reject and get logged in handleDocumentRequest.
                if (shellRendered) {
                    console.error(error);
                }
            },
        }
    );

    setTimeout(abort, ABORT_DELAY);
});

} /**

import {RemixBrowser} from "@remix-run/react"; import {startTransition, StrictMode} from "react"; import {hydrateRoot} from "react-dom/client"; import {useCallback, useState} from 'react'; import {CacheProvider} from '@emotion/react'; import ClientStyleContext from '../app/styles/client.context'; import createEmotionCache from '../app/styles/createEmotionCache'; import i18next from "i18next"; import LanguageDetector from "i18next-browser-languagedetector"; import Backend from "i18next-http-backend"; import {I18nextProvider, initReactI18next} from "react-i18next"; import {getInitialNamespaces} from "remix-i18next"; import i18n from "./i18n"; // The configuration file we created

function ClientCacheProvider({children}) { const [cache, setCache] = useState(createEmotionCache());

const reset = useCallback(() => {
    setCache(createEmotionCache());
}, []);

return (
    <ClientStyleContext.Provider value={{reset}}>
        <CacheProvider value={cache}>{children}</CacheProvider>
    </ClientStyleContext.Provider>
);

}

i18next .use(initReactI18next) .use(LanguageDetector) .use(Backend) .init({ ...i18n, ns: getInitialNamespaces(), backend: { loadPath: "/locales/{{lng}}/{{ns}}.json", }, detection: { order: ["htmlTag"], caches: [], }, }) .then(() => { startTransition(() => { hydrateRoot( document,

            );
        });
    }
)
iwt-jandwinger commented 7 months ago

i solve the issue by replace

import { useTranslation } from "react-i18next";

with

import { useTranslation } from "next-i18next";