Implementing basic i18n in React Router v7 (framework mode) #14681
Replies: 2 comments 1 reply
-
You can reuse the same route model if you give it a unique id https://sergiodxa.com/tutorials/reuse-route-modules-in-react-router |
Beta Was this translation helpful? Give feedback.
-
|
@sergiodxa Thanks, That's great to hear and sounds promising. Do you know if there is a way to pass data or state (like a locale) to the route modules via the route config? This way we could extract the locale in loaders without parsing the request URL (since locale would still not be a dynamic or static optional parameter)? |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
I’m currently wrapping my head around how to implement basic i18n in React Router v7 (framework mode) without relying on an external package like
i18next.My goal is fairly modest, something I’d expect a router to handle reasonably well:
Desired routing behavior:
And from a DX perspective:
So far, I’ve struggled to find a clean solution. Most issues and discussions I found seem to target earlier versions of React Router, so I’m curious whether I’m missing something, or if someone in the community has already solved this in v7.
❌ Approach 1: Optional dynamic route segment
This breaks down because there’s no regex or validation for dynamic route segments. The router can’t distinguish between:
/de → German homepage
/about → English “about” page
Both resolve to the same route shape, making locale detection ambiguous.
❌ Approach 2: Separate route definitions with dynamic segments
This doesn’t work because route modules must be unique (?!) and can only be used once. You can’t reuse the same module for both localized and non-localized variants.
🥴 Approach 3: Static optional route segments
Okay, let's hard-code all supported languages:
This kind of works, but DX is sub-optimal:
request.url<Link />(e.g./→/de) does not trigger loader revalidation, because the router sees this as the same routeThat makes a language switcher essentially useless unless you use a native
<a />, or addreloadDocumentto the<Link />Neither feels great.
❌ Approach 4: Single wildcard route
In theory, you could:
But delegated modules can’t have their own loaders, so all data loading for all routes ends up in a single loader. This kind of defeats the purpose of using a router framework in the first place.
Final thoughts
Don’t get me wrong, I’ve really enjoyed React Router’s DX so far. But I’m honestly flabbergasted that such a common use case feels this hard to model cleanly.
Am I missing an intended pattern in v7?
Or has anyone successfully implemented optional locale prefixes in framework mode without external i18n libraries?
Would love to hear how others are handling this.
Beta Was this translation helpful? Give feedback.
All reactions