#Remix with i18-next persist user language selection
1 messages · Page 1 of 1 (latest)
Save it in a cookie or session
Thank you for a quick reply and great job with helping the community. I tried your suggested approach namely a cookie based solution. But unfortunately the app still is flickering. I am setting a cookie inside an action of language change for my language picker component and it is read properly inside a loader inside my Root component. When I log the locale variable its value is what I would expect and never changes until an action is set. It seems like fallback values are being presented for a moment, because when I removed a fallback translation a key is being shown
did you setup i18next-browser-languagedetector?
and are you setting the lang attribute in the <html> tag?
Yes, I have both set - lang is getting my locale value
What do you mean by flickering, exactly?
Does it render correctly, went kaput, then re-renders everything correctly?
It renders for a moment with a fallback language english in this case, even though a cookie with another language is set. Then after a short time it's all set. It is a similar problem to setting dark mode from user preference, where the app renders in light mode on init and then re-renders in dark, which is what the user wanted
is your loader returning the correct locale? do you see it in the <html lang>? could you share some repo replicating it?
what's your i18next config?
export default {
// This is the list of languages your application supports
supportedLngs: ["en", "fr"],
// This is the language you want to use in case
// if the user language is not in the supportedLngs
fallbackLng: "en",
// The default namespace of i18next is "translation", but you can customize it here
defaultNS: "translation",
// Disabling suspense is recommended
react: { useSuspense: false },
};
replace supportedLngs: ["en", "fr"] with supportedLngs: ["fr", "en"]
no luck
Set handles properly?
this is most likely a misconfiguration, but it's hard without seeing the code, is it possible to share the code?
I would have to prepare a separate code base, but won't be able to do it today
async function hydrate() {
await i18next
.use(initReactI18next) // Tell i18next to use the react-i18next plugin
.use(LanguageDetector) // Setup a client-side language detector
.use(Backend) // Setup your backend
.init({
...i18n, // spread the configuration
// This function detects the namespaces your routes rendered while SSR use
ns: getInitialNamespaces(),
backend: { loadPath: "/locales/{{lng}}/{{ns}}.json" },
detection: {
// Here only enable htmlTag detection, we'll detect the language only
// server-side with remix-i18next, by using the <html lang> attribute
// we can communicate to the client the language detected server-side
order: ["cookie", "htmlTag"],
// Because we only use htmlTag, there's no reason to cache the language
// on the browser, so we disable it
caches: [],
},
});
startTransition(() => {
hydrateRoot(
document,
<I18nextProvider i18n={i18next}>
<StrictMode>
<RemixBrowser />
</StrictMode>
</I18nextProvider>
);
});
}
most of my configuration is a copy/paste of your example in https://github.com/sergiodxa/remix-i18next
order: ["cookie", "htmlTag"], remove cookie here
keep only htmlTag
you use the cookie server-side in your loader, the i18next-language-detector plugin must read always from htmlTag
still, it flickers on init
if you're using i18next.getLocale(request) to detect the locale server-side, your can pass your Remix cookie object to RemixI18Next on the detection key
const i18next = new RemixI18Next({
detection: {
cookie: userPrefs,
supportedLanguages: i18n.supportedLngs,
fallbackLanguage: i18n.fallbackLng,
}
import { createCookie } from "@remix-run/node";
export const userPrefs = createCookie("i18next", {
maxAge: 604_800, // one week
});
the latter code snippet is my cookie.server.ts
Thank you guys, I will come back to this tomorrow as it's very late in my timezone
Not sure what you mean by flickering but when using the TFunction from useTranslation hook of react-i18next without suspense you need to wait for the translations to be ready like so:
if (!ready) return null
return <div>{t('greeting')}</div>```
> Not using Suspense you will need to handle the not ready state yourself by eg. render a loading component as long !ready . Not doing so will result in rendering your translations before they loaded which will cause save missing be called although translations exists (just yet not loaded).
https://react.i18next.com/latest/usetranslation-hook
Thanks, I’ll try with “ready” flag, hopefully adding it somewhere near application root will help
@bright cargo Waiting for translations helped. I've added a spinner, but it's still a better UX than translations changing from default to selected one, thank you. I thought that, if a locale is set inside a loader and assigned to the document in the root, translation would be available before initial render - maybe it's a misunderstanding on my part
@steep vine I've had similar issue, the transltations would be handle just client side.
I've fixed it replacing backend: { loadPath: "/locales/{{lng}}/{{ns}}.json" } everywhere with resources (which is a json imported)
import commonEN from './en/common.json'
import commonIT from './it/common.json'
export const resources = {
en: { common: commonEN },
it: { common: commonIT },
}
```
```
i18next: {
...i18n,
resources,
},
```
Thank you, something still seems off, so perhaps my configuration is messed up somewhere. Because reading directly from a file does not seem to fix the original issue. Although I removed the spinner, assuming that was your hint @bronze mica ?
In the source code you see the translation or the original text?
I use translations with const {t} = useTranslation() and then use it inside components as ${t("name", "Name")}. When I open the app I get a moment where I see Name and other default texts and then after a split second it gets translated to a locale from a cookie
it means the translation appears just on the client, if you inspect the html you don't have the string translated. it was my same problem but I've fixed as mentioned above