T
TanStack8mo ago
eastern-cyan

Advice on path-based i18n without lazy-loading or importing every language on the client.

I'm looking to build a pretty straight-forward path-based i18n setup like:
/en/projects
/en/projects/1
/en/...
/de/projects
/de/projects/1
/de/...
/ja/projects
/ja/projects/1
/ja/...
/en/projects
/en/projects/1
/en/...
/de/projects
/de/projects/1
/de/...
/ja/projects
/ja/projects/1
/ja/...
However, it's important that we don't want to lazy load the individual localizations (or load all of them), but instead import them directly in separate components like: /de -> de.tsx:
import * as m from "@/paraglide/messages/de.js"
import '@valibot/i18n/de'
import * as v from 'valibot';

v.setGlobalConfig({ lang: 'de' });

function DeLocale() {
return (
<LocaleContext.Provider messages={m} locale="de"> // or maybe a store, whatever...
<Outlet />
</LocaleContext.Provider>
)
}
import * as m from "@/paraglide/messages/de.js"
import '@valibot/i18n/de'
import * as v from 'valibot';

v.setGlobalConfig({ lang: 'de' });

function DeLocale() {
return (
<LocaleContext.Provider messages={m} locale="de"> // or maybe a store, whatever...
<Outlet />
</LocaleContext.Provider>
)
}
This might look something like with file-based routes:
/
├── de/
| └── index.tsx <DeLocale>
│ └── [REST OF THE APP'S ROUTES]
└── en/
| └── index.tsx <EnLocale>
│ └── [REST OF THE APP'S ROUTES]
└── etc...
/
├── de/
| └── index.tsx <DeLocale>
│ └── [REST OF THE APP'S ROUTES]
└── en/
| └── index.tsx <EnLocale>
│ └── [REST OF THE APP'S ROUTES]
└── etc...
But obviously, I don't want to have to repeat the entire route-tree for each language. I'm thinking, from looking at documenation, the best way to achieve this might be Virtual File Routes at the top level, then drop back to physical routes for the rest of the app structure? I'm not sure if this is the best way to go about it, so I'm looking for some guidance. Thanks! Is there a better way with splats, path segments, or a second root router mounted after the locale path (is that even possible?) This is a front-end only vite-built project with Router, that I plan to eventually adapt to Start, where this will be more important.
Don't Lazy-Load Translations
Lazy-loading translations can seriously hurt your web-vitals. Here is what to do instead.
9 Replies
conscious-sapphire
conscious-sapphire8mo ago
Hi, I actually just wrote a small guide how one can use paraglide with tanstack start for i18n. Its currently cookie based but I will soon extend it with examples on how to do route based. https://eugeneistrach.com/blog/paraglide-tanstack-start/ essentially you just need to change what is done in root route. You create a new route $language and in the loader you grab the language param, then you use that to set the language instead of the cookie. The setlanguage function also needs to change to use redirects etc instead of cookies and for router only there are some other considerations maybe
Modern i18n DX in TanStack Start with Paraglide JS
A comprehensive guide to implementing type-safe internationalization in TanStack Start using Paraglide JS, covering everything from basic setup to advanced patterns.
sensitive-blue
sensitive-blue8mo ago
@Eugen that's some great content! Do you have any plans about extending the article with localized URLs as well? I haven't found any resources about this topic. (having both /about-us for english and /uber-uns for german without creating two routes)
conscious-sapphire
conscious-sapphire8mo ago
I need to think about it but might be tricky with all the type safety. In such case maybe Astro is more suitable as they have a guide on this, but idk
sensitive-blue
sensitive-blue8mo ago
Yup, if you figure something out, let me know please🙏
eastern-cyan
eastern-cyanOP7mo ago
Thank you!
harsh-harlequin
harsh-harlequin7mo ago
@Eugen were you able to get paraglide v2 working? All I get is an infinite page flashing because setLocale in client.ts causes the page to reload 🙁
conscious-sapphire
conscious-sapphire7mo ago
i just tried and dont have this issue even with 2 beta if you can give me some reproduction I can help you solve it actually for v2 you dont need to use setLocale in your client entry anymore i was wrong, instead of setLocale you need to use defineGetLocale callback. but there are some other considerations with v2. I have made a working example here, but there is some more improvements that can be made for v2, e.g better support for server fns, and removing some of the utils code as paraglide has some build in way to detect languages in v2 https://github.com/EugenEistrach/start-paraglide-v2
wee-brown
wee-brown7mo ago
I've had v2 working by following the guide. I was in the same spot as you (flashing) but did the following on top of updating types to locale: * Remember to uninstall the vite plugin. Instead use the one from the v2 import { paraglideVitePlugin } from '@inlang/paraglide-js' * Removed the code from client.tsx (yes everything for paraglide) After that everything seems to work just fine. I still think there are improvements to be made (e.g. using the url)
equal-aqua
equal-aqua5mo ago
but I will soon extend it with examples on how to do route based.
Hi @Eugen is there any updated docs on handling language selection via route paths? website.com/en-us/myroute ?

Did you find this page helpful?