I am creating web app that can be accessed only when user login.
I use nextjs 13 with App Route (not Pages Route!).
And I am trying to use internalization without having to create sub-route like /en
or domain like mywebsite.lang
or lang.website
. I would like to fetch user data from database of his preferred language and apply it on the whole app. Is it possible?
I read on nextjs doc about i18n but it tells only about using sub-routing or domain. And other blogs or videos available I can find online only demonstrating either sub-route/domain or nextjs using Pages Route.
I have tried this 2 different blogs instruction. But unfortunately it uses Pages Route not App Route, so I can’t implement it to my app that uses App Route.
https://hackernoon.com/implementing-i18n-in-nextjs-a-guide-to-non-route-based-localization
https://iamsannyrai.medium.com/i18n-in-next-js-without-sub-path-or-domain-routing-2443c1a349c6
When I implement instruction from the blogs above, my codes belows in src/app/layout.tsx
, it gets error belows.
// src/app/layout.tsx
"use client";
import { appWithI18Next, useSyncLanguage } from "ni18n";
import { ni18nConfig } from "../../ni18n.config.js";
function MyApp({ Component, pageProps }) {
const locale =
typeof window !== "undefined" && window.localStorage.getItem("MY_LANGUAGE");
useSyncLanguage(locale || "en");
return <Component {...pageProps} />;
}
export default appWithI18Next(MyApp, ni18nConfig);
// ni18n.config.js
export const ni18nConfig = {
supportedLngs: ["en", "id"],
ns: ["sign-up"],
};
error
Unhandled Runtime Error
TypeError: Cannot read properties of undefined (reading 'locale')
Call Stack
WithI18Next
node_modules/ni18n/dist/esm/app-with-i18next/app-with-i18next.js (71:0)
renderWithHooks
node_modules/next/dist/compiled/react-dom/cjs/react-dom.development.js (10697:0)
updateFunctionComponent
node_modules/next/dist/compiled/react-dom/cjs/react-dom.development.js (15180:0)
mountLazyComponent
node_modules/next/dist/compiled/react-dom/cjs/react-dom.development.js (15620:0)
...
update: still not working
/// src/pages/_app.tsx
import { useEffect, useState } from "react";
import { I18nextProvider } from "react-i18next";
import { serverSideTranslations } from "next-i18next/serverSideTranslations";
import i18n from "../../i18n";
export default function MyApp({ Component, pageProps }) {
const [locale, setLocale] = useState(pageProps.locale);
useEffect(() => {
// update locale on client
setLocale(window.localStorage.getItem("locale"));
}, []);
return (
<I18nextProvider i18n={i18n}>
<Component {...pageProps} />
</I18nextProvider>
);
}
export async function getServerSideProps() {
// fetch user locale from DB
const locale = "en";
return {
props: {
...(await serverSideTranslations(locale)),
locale,
},
};
}
// i18n.js
import { initReactI18next } from "react-i18next";
import i18n from "i18next";
i18n.use(initReactI18next).init({
lng: "en",
fallbackLng: "en",
});
export default i18n;
// src/app/coba/page.tsx
"use client";
import { useTranslation } from "react-i18next";
function Coba() {
const { t, i18n } = useTranslation();
return <div>content Coba: {t("lang")}</div>;
}
export default Coba;
// public/locales/en/sign-up.json
{
"lang": "language"
}
When I go to url /coba
on browser, the result is
content Coba: lang
that should be "content Coba: language"
2
Answers
The issue is that
appWithI18Next
is a higher order component designed to wrap Pages, not App components in Next.js.Since you are using App Routing, you’ll need to handle i18n a bit differently:
pages/_app.tsx
, get the user’s locale preference from the database on the server. Pass it touseState
on the client:useTranslation
hook in your components.This allows you to handle i18n without subpaths while using App Routing.
You should take a different path Because there is a server components too
First Download the following packages with this command:
I will separate the config Because it will be easier to change it later
i18n.config.ts
Then we will make the configuration for the server:
dictionary.ts
We need to create a function to detect locale
Than we need middleware:
middleware.ts
Then we have to define types for server component IntelliSense:
global.d.ts
Now we need a provider for client components:
providers.tsx
before we add our layout file we have to move every route in the app directory to the [lang] folder like this:
layout.tsx
client component usage:
server component usage: