T
TanStack2mo ago
ugly-tan

i18n with optional locale param?

I see the example in here https://tanstack.com/router/latest/docs/framework/react/guide/path-params#type-safety-for-i18n And it uses optional locale param. Also, there is this example https://tanstack.com/router/latest/docs/framework/react/examples/basic-ssr-file-based that has the head in the __root route. I want to alter the head and also the <html lang={...}> of the RootComponent, based on the locale. But as I understand, locale is not defined yet in the root route? It gets defined only in it's childern, `root/__main/{-$locale}/...` ?
React TanStack Router Basic Ssr File Based Example | TanStack Route...
An example showing how to implement Basic Ssr File Based in React using TanStack Router.
Path Params | TanStack Router React Docs
Path params are used to match a single segment (the text until the next /) and provide its value back to you as a named variable. They are defined by using the $ character prefix in the path, followed...
4 Replies
inland-turquoise
inland-turquoise2mo ago
You can use useMatches hook for <html lang={...}>. For head, there should be matches object in its argument
async head({ matches }) {
// ...
}
async head({ matches }) {
// ...
}
You can reduce the matches to find the locale
const nullableLocale = matches.reduce((reduced, match) => reduced === null && 'locale' in match.params ? match.params.locale : null, null);
const nullableLocale = matches.reduce((reduced, match) => reduced === null && 'locale' in match.params ? match.params.locale : null, null);
sensitive-blue
sensitive-blue2mo ago
in this case i would useParams({strict: false })
ugly-tan
ugly-tanOP2mo ago
Weirdly enough, if I use some arguments passed to the head, for example, match: head: ({match}) => { return { meta: [...]} }, Typescript will underline beforeLoad of the same Route with error:
...
Type '(ctx: BeforeLoadContextOptions<Register, any, undefined, {}, Context, AnyContext, unknown, undefined>) => Promise<{ lang: "ja" | "en"; messages: { [x: string]: any; }; user: { id: string; email: string; emailVerified: boolean; name: string; createdAt: Date; updatedAt: Date; image?: string | null | undefined; banned: boolean | null | undefined; role?: string | null | undefined; banReason?: string | null | undefined; banExpires?: Date | null | undefined; } | null; }>' is not assignable to type '(ctx: BeforeLoadContextOptions<Register, any, undefined, {}, Context, AnyContext, unknown, undefined>) => never'.
Type 'Promise<{ lang: "ja" | "en"; messages: { [x: string]: any; }; user: { id: string; email: string; emailVerified: boolean; name: string; createdAt: Date; updatedAt: Date; image?: string | null | undefined; banned: boolean | null | undefined; role?: string | null | undefined; banReason?: string | null | undefined; banExpires?: Date | null | undefined; } | null; }>' is not assignable to type 'never'. (ts 2322)
...
Type '(ctx: BeforeLoadContextOptions<Register, any, undefined, {}, Context, AnyContext, unknown, undefined>) => Promise<{ lang: "ja" | "en"; messages: { [x: string]: any; }; user: { id: string; email: string; emailVerified: boolean; name: string; createdAt: Date; updatedAt: Date; image?: string | null | undefined; banned: boolean | null | undefined; role?: string | null | undefined; banReason?: string | null | undefined; banExpires?: Date | null | undefined; } | null; }>' is not assignable to type '(ctx: BeforeLoadContextOptions<Register, any, undefined, {}, Context, AnyContext, unknown, undefined>) => never'.
Type 'Promise<{ lang: "ja" | "en"; messages: { [x: string]: any; }; user: { id: string; email: string; emailVerified: boolean; name: string; createdAt: Date; updatedAt: Date; image?: string | null | undefined; banned: boolean | null | undefined; role?: string | null | undefined; banReason?: string | null | undefined; banExpires?: Date | null | undefined; } | null; }>' is not assignable to type 'never'. (ts 2322)
the error is shown even if I'm not using the match anywhere in the head... My route is:
export const Route = createRootRouteWithContext<Context>()({
head: (ctx) => {
return {
meta: [
{
charSet: "utf-8",
},
{
name: "viewport",
content: "width=device-width, initial-scale=1",
},

{ rel: "icon", href: "/favicon.ico" },

],

links: [{ rel: "stylesheet", href: css }],
};
},
component: RootComponent,

beforeLoad: async (ctx) => {
const locale = await getLocaleFn();

const user = await getUser();

await saveLocaleFn({ data: { lang: locale } });

const messages = await getMessagesFn({ data: locale });

return {
lang: locale,
messages,
user,
};
},
});
export const Route = createRootRouteWithContext<Context>()({
head: (ctx) => {
return {
meta: [
{
charSet: "utf-8",
},
{
name: "viewport",
content: "width=device-width, initial-scale=1",
},

{ rel: "icon", href: "/favicon.ico" },

],

links: [{ rel: "stylesheet", href: css }],
};
},
component: RootComponent,

beforeLoad: async (ctx) => {
const locale = await getLocaleFn();

const user = await getUser();

await saveLocaleFn({ data: { lang: locale } });

const messages = await getMessagesFn({ data: locale });

return {
lang: locale,
messages,
user,
};
},
});
sensitive-blue
sensitive-blue2mo ago
can you please provide a complete example repo that reproduces this?

Did you find this page helpful?