How to translate <title> with i18next? #3666
Replies: 3 comments 4 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.
-
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