How to translate <title> with i18next? #3666
Replies: 3 comments 5 replies
-
|
For anyone with the same problem: I fixed it with a hook:
And then include it in your topmost component.
|
Beta Was this translation helpful? Give feedback.
-
|
@edwin-speer You sir are a gentleman and a scholar 🎩 This helped me resolve the issue with translating Turns out the configuration of {
defaultNS: false,
ns: [],
}to remove namespaces was causing /**
* A hook that manages language change effects in the application.
* It tracks the current i18n locale and updates the document's text direction
* when the language changes. The hook also forces a component rerender by
* updating state and triggers a router invalidation to ensure the application
* updates accordingly.
*
* The hook uses a state setter to trigger rerenders and sets up an i18n
* languageChanged event listener to handle language changes. It cleans up
* the event listener when the component unmounts.
*
*/
export function useLanguageChange() {
const [, setLocale] = useState(i18n.language);
const router = useRouter();
useEffect(() => {
const handler = (locale: string) => {
setLocale(locale);
document.documentElement.dir = i18n.dir(locale);
void router?.invalidate();
};
i18n.on("languageChanged", handler);
return () => i18n.off("languageChanged", handler);
}, []);
} |
Beta Was this translation helpful? Give feedback.
-
|
You can pass // __root.tsx
export const Route = createRootRouteWithContext<{
i18n: typeof i18next;
}>()({
head: ({ match: { context: { i18n }} }) => ({
meta: [
{
charSet: "utf-8",
},
{
name: "viewport",
content: "width=device-width, initial-scale=1",
},
{
title: i18n.t("title") || undefined,
},
],
}),
shellComponent: RootDocument,
});
// router.tsx
import { createRouter } from "@tanstack/react-router";
import i18next from "i18next";
import { I18nextProvider } from "react-i18next";
import { createI18nInstance } from "./i18n";
import { routeTree } from "./routeTree.gen";
interface RouterContext {
i18n: typeof i18next;
}
export const getRouter = async () => {
const i18n = await createI18nInstance({
...yourOptionsHere
});
const routerContext: RouterContext = {
i18n,
};
const router = createRouter({
routeTree,
defaultPreload: "intent",
context: routerContext,
scrollRestoration: true,
Wrap: ({ children }) => (
<I18nextProvider i18n={i18n}>
{children}
</I18nextProvider>
),
});
return router;
}
// i18n.ts
import { createIsomorphicFn } from "@tanstack/react-start";
import { getCookie, setCookie } from "@tanstack/react-start/server";
import Cookies from "js-cookie";
import type { LanguageDetectorModule } from "i18next";
import i18n, { createInstance, InitOptions } from "i18next";
import { initReactI18next } from "react-i18next";
type CookieOptions = {
domain?: string;
path?: string;
sameSite?: "lax" | "strict" | "none";
secure?: boolean;
expires?: Date;
};
type TanstackLanguageDetectorOptions = {
cookieName: string;
cookieOptions?: CookieOptions;
};
function TanstackLanguageDetector(): LanguageDetectorModule {
let options: TanstackLanguageDetectorOptions = {
cookieName: "locale",
};
return {
type: "languageDetector",
init(_services, detectorOptions: TanstackLanguageDetectorOptions) {
options = detectorOptions;
},
detect() {
const language = getLanguageCookie({
cookieName: options.cookieName,
});
return language;
},
cacheUserLanguage(lng: string) {
setLanguageCookie({
language: lng,
cookieName: options.cookieName,
cookieOptions: options.cookieOptions,
});
},
};
}
type CreateI18nInstanceOptions = InitOptions & {
detection?: TanstackLanguageDetectorOptions;
};
export async function createI18nInstance(options?: CreateI18nInstanceOptions) {
const instance = getInstance();
await instance
.use(TanstackLanguageDetector())
.use(initReactI18next)
.init({
resources,
fallbackLng: "en",
defaultNS: "translations",
detection: {
cookieName: "locale",
},
interpolation: {
escapeValue: false, // not needed for react
},
...options,
});
return instance;
}
// Isomorphic helpers to get i18n instance
type GetLanguageArgs = {
cookieName: string;
};
const getInstance = createIsomorphicFn()
.server(() => createInstance())
.client(() => i18n);
// Helper functions to get and set language cookie
const getLanguageCookie = createIsomorphicFn()
.server((args: GetLanguageArgs) => getCookie(args.cookieName))
.client((args: GetLanguageArgs) => Cookies.get(args.cookieName));
type SetLanguageArgs = {
language: string;
cookieName: string;
cookieOptions?: CookieOptions;
};
const setLanguageCookie = createIsomorphicFn()
.server(({ language, cookieName, cookieOptions }: SetLanguageArgs) => {
setCookie(cookieName, language, cookieOptions);
})
.client(({ language, cookieName, cookieOptions }: SetLanguageArgs) => {
Cookies.set(cookieName, language, cookieOptions);
}); |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
Fixed, see fix below.
I'm using i18next to translate texts. But I'm unable to translate the page title (title meta tag/document.title).
When I do this (when following the documentation):
The text is not translated, probably because the translation file is not yet loaded when
head()is called.The
<RouterProvider/>is wrapped inside<I18nextProvider/>.It seems the problem is that
head()is only called once and not again when the translation file is loaded.I now "solved" it in a very hacky (and possibly brittle) way, like this
Later I read out the
translationKeyand setdocument.title, but it's very non standard and not a desired solution.I couldn't find any documentation on how to solve this, although this problem seems to be not uncommon.
Is this a bug, feature request or am I missing something?
I'm using
@tanstack/router-plugin 1.111.3
Beta Was this translation helpful? Give feedback.
All reactions