-
I'm attempting to add i18n to a site. My goal would be that existing routes show the default language, in this case English, and those routes prefixed with For example
I believe this is a very common scenario which I've seen before in many places. I'm surprised it's been really hard to find anything about implementing this approach. The most useful discussion I found is this closed issue #5666 from 2017. I'm currently using react-router v5, but I'm happy to upgrade to v6. I've tried using nested routes after reading the docs. However, it's kind of tricky since there are places where the My approach so far has been moving all the current routes to a const SUPPORTED_LANGS = ['es', 'fr']
const App = () => (
<div className="app">
<Header /> {/* <- this has links inside */}
<Switch>
<Route exact path={`/:lang(${SUPPORTED_LANGS.join("|")})/`}>
<Routes /> {/* <- old routes */}
</Route >
<Routes /> {/* <- old routes again*/}
</Switch>
</div>
) The idea is that if a language from a supported language is matched, e.g. However, this doesn't work. For example What would be the best approach to achieve this? Thanks in advance! |
Beta Was this translation helpful? Give feedback.
Replies: 3 comments 2 replies
-
I ended up coding a custom solution. It's a bit more complex than I was hoping for, but hopefully straightforward enough. Please, comment on this discussion if you found a better approach. SolutionThere are a few changes involved. First, declare two constants to make references to the default and supported languages. // constants.js
export const DEFAULT_LANG = "en";
export const SUPPORTED_LANGS = ["es", "fr"]; Then create a custom hook to find if we were in a default-language url –i.e. // useUrlLang.jsx
import { useRouteMatch } from "react-router-dom";
import { DEFAULT_LANG, SUPPORTED_LANGS } from "../helpers/constants";
const useUrlLang = () => {
// INFO: if `langMatch = null`, there was no match, meaning we're on a default-language route.
const langMatch = useRouteMatch(`/:lang(${SUPPORTED_LANGS.join("|")})`);
const lang = langMatch?.params.lang ?? DEFAULT_LANG;
const langUrlPrefix = lang === DEFAULT_LANG ? "" : `/${lang}`;
const path = langMatch?.path ?? "/";
return { lang, langUrlPrefix, path };
};
export default useUrlLang; To be able to handle default and supported language routes, I wrap the routes like this: <Switch>
<Routes />
<Route path={`/:lang(${SUPPORTED_LANGS.join("|")})`}>
<Routes />
</Route>
</Switch> Which is basically: show either a default route if there's a match, or a route starting with a supported language if there's a match. But for that to work we need to make some changes in the import useUrlLang from "./hooks/useUrlLang";
const Routes = () => {
const { langUrlPrefix, path } = useUrlLang();
// INFO: pathPrefix is the path without trailing `/`
const pathPrefix = path.replace(/\/$/, "");
return (
<Switch>
<Route exact path={path}>
<RootView />
</Route>
<Route exact path={`${pathPrefix}/one`}>
<One />
</Route>
<Route path={`${pathPrefix}/a/nested/route`}>
<NestedView />
</Route>
<Route path={`${pathPrefix}/redirect-to-one`}>
<Redirect to={`${langUrlPrefix}/one`} />
</Route>
</Switch>
);
}; Note a few things here:
And finally, same as with the redirects, we need to take into account the current language in the url when using links. To fix that we need to find all the Links and update them. Yes, this is the most fun if you have a big app. + const { langUrlPrefix } = useUrlLang();
// ...
<Link href="https://external.link">Don't update external links</Link>
- <Link href='/one'>Go to page one</Link>
+ <Link href={`${langUrlPrefix}/one`}>Go to page one</Link> BonusTrigger language update when someone goes to a supported-language url: // index.jsx
const Root = () => {
const [locale, setLocale] = useState(DEFAULT_LANG);
return (
<I18nProvider locale={locale} ... >
<App setLocale={setLocale} />
</I18nProvider>
)
} // App.jsx
const App = ({ setLocale }) => {
const i18n = useI18n(); // your i18n object to get the current app language from
const { lang } = useUrlLang();
if (lang !== i18n.locale) {
setLocale(lang);
}
// ...
} |
Beta Was this translation helpful? Give feedback.
-
Optional Segments are generally the recommended way to do i18n URL prefixes: https://reactrouter.com/en/main/route/route#optional-segments |
Beta Was this translation helpful? Give feedback.
-
i have similar problem: i have a language selector in my sidebar, if i select a different language then the default lang and after that i will go to the child path i can traslate between the default lang and the lang i chosed before, the others language aren't loaded. please fix it, the Outlet component dosen't work as well |
Beta Was this translation helpful? Give feedback.
I ended up coding a custom solution. It's a bit more complex than I was hoping for, but hopefully straightforward enough.
Please, comment on this discussion if you found a better approach.
Solution
There are a few changes involved. First, declare two constants to make references to the default and supported languages.
Then create a custom hook to find if we were in a default-language url –i.e.
/one
–, or a supported-language url –i.e./es/one
–.