I'm working on a Next.js app with App Router and I'm implementing localization using i18next with dynamic JSON imports for translations. I'm running into the following error:
Error: Only plain objects, and a few built-ins, can be passed to Client Components from Server Components. Classes or null prototypes are not supported.
{common: Module}
^^^^^^
This error happens when I try to load my translations in layout.tsx like this:
const locale = "en";
const namespaces = ["common"];
const resources = await import(`@/locales/${locale}/${"common"}.json`);
<TranslationsProvider
locale={locale}
namespaces={namespaces}
resources={{ [locale]: { ["common"]: resources } }}
>
{children}
</TranslationsProvider>
i18nConfig.ts:
const i18nConfig = {
locales: ["en", "ar"],
defaultLocale: "en",
};
export default i18nConfig;
middleware.ts:
import { i18nRouter } from "next-i18n-router";
import i18nConfig from "../i18nConfig";
import { NextRequest, NextResponse } from "next/server";
export function middleware(request: NextRequest): NextResponse {
return i18nRouter(request, i18nConfig);
}
export const config = {
matcher: "/((?!api|static|.*\\..*|_next).*)",
};
i18n.ts:
import { createInstance } from "i18next";
import { initReactI18next } from "react-i18next/initReactI18next";
import resourcesToBackend from "i18next-resources-to-backend";
import i18nConfig from "../../i18nConfig";
export default async function initTranslations(locale, namespaces, i18nInstance?, resources?) {
i18nInstance = i18nInstance || createInstance();
i18nInstance.use(initReactI18next);
if (!resources) {
i18nInstance.use(
resourcesToBackend(
(language, namespace) =>
import(`@/locales/${language}/${namespace}.json`)
)
);
}
await i18nInstance.init({
lng: locale,
fallbackLng: i18nConfig.defaultLocale,
supportedLngs: i18nConfig.locales,
defaultNS: namespaces[0],
fallbackNS: namespaces[0],
ns: namespaces,
resources,
preload: resources ? [] : i18nConfig.locales,
});
return {
i18n: i18nInstance,
resources: {
[locale]: i18nInstance.services.resourceStore.data[locale],
},
t: i18nInstance.t,
};
}
My Question: How can I correctly pass the translation resources to a client component like TranslationsProvider in Next.js App Router without triggering this error? What is the best practice for passing translations from server to client components using i18next in the App Router?
Any help or best practices would be appreciated!
The problem is that await import(...)
returns Module
not a plain object.
This simples trick to solve your problem would be destructure resources
like this:
const locale = "en";
const namespaces = ["common"];
const resources = await import(`@/locales/${locale}/${"common"}.json`);
<TranslationsProvider
locale={locale}
namespaces={namespaces}
resources={{ [locale]: { ["common"]: {...resources} } }}
>
{children}
</TranslationsProvider>
JSON.parse(JSON.stringify(resources))
will do the job as well, but {...resources}
is faster and looks nicer :)