I have used i18next-browser-languagedetector but it was always returning "en" while my browser is in a different language. Also, I would only like to fetch the locale based on the browser preference or the user store.
So I've wrote my own function. But it is never called during the initialization. How to fix this?
import i18n from "i18next";
import { initReactI18next } from "react-i18next";
import { userStore } from "src/stores/user";
import en from "./en.json";
import fr from "./fr.json";
const languages = {
EN: "en",
FR: "fr",
};
const resources = {
en: { translation: en },
fr: { translation: fr },
};
const detectLocale = () => {
const browserLocale = navigator.language.split("-")[0];
const { locale } = userStore();
if (locale) return locale;
if (browserLocale) return browserLocale;
return languages.EN;
};
export default i18n.use(initReactI18next).init({
resources,
lng: languages.EN,
detection: detectLocale,
fallbackLng: languages.EN,
keySeparator: ".",
whitelist: [languages.EN, languages.FR],
interpolation: { escapeValue: false },
});
Fetch the user locale or the browser locale.
Have a look at: https://www.i18next.com/misc/creating-own-plugins#languagedetector
and then pass it to the ’.use(myLngDet)’ function
Hi @andai, so I've read the doc and implemented as suggested, but it makes the app crash. I must do something wrong. Could you please provide me with a more detailed exemple?
const detectLocale = () => {
const browserLocale = navigator.language.split("-")[0];
const { locale } = userStore();
if (locale) return locale;
if (browserLocale) return browserLocale;
return languages.EN;
};
const myLngDet = {
type: "languageDetector",
async: true,
detect: detectLocale(),
};
const resources = {
en: { translation: en },
fr: { translation: fr },
};
export default i18n
.use(myLngDet)
.use(initReactI18next)
.init({
resources,
lng: languages.EN,
detection: langDetectorOptions,
fallbackLng: languages.EN,
keySeparator: ".",
whitelist: [languages.EN, languages.FR],
interpolation: { escapeValue: false },
});
If this is set to true, your detect function receives a callback function that you should call with your language

Here you have a simple example: https://codesandbox.io/s/react-i18next-with-custom-language-detector-tcf9f?file=/src/i18n.js
Does it work now?
Hi, sorry I was not connected this afternoon. So I've tried your example. It almost works. I now receive the browser default language, which is great, but I still can't access the user's locale, that is stored in a zustand store (it's another global state management library, like redux).
I wrote:
const detectLocale = () => {
const browserLocale = navigator.language.split("-")[0];
const { locale } = userStore();
if (locale) return locale;
if (browserLocale) return browserLocale;
return languages.EN;
};
const LanguageDetector = {
type: "languageDetector",
async: false,
init: function (services, detectorOptions, i18nextOptions) {
/* use services and options */
},
detect: detectLocale(),
cacheUserLanguage: function (lng) {
/* cache language */
},
};
const resources = {
en: { translation: en },
fr: { translation: fr },
};
export default i18n
.use(LanguageDetector)
.use(initReactI18next)
.init({
resources,
fallbackLng: languages.EN,
keySeparator: ".",
whitelist: [languages.EN, languages.FR],
interpolation: { escapeValue: false },
});
The advantage of Zustand is that I can interact with the store outside React components. Yet, it crashes here, claiming that I can't use any hook outside React component. Weird.
When the user connects, I fetch his infos on my server and populate a global store with the data received. I need this locale to make the i18n match his preference on any browser/device he might use. What would be your recommendation to make it work here (I'm not going off-topic, here it's really the core of the initial post).
Also, typescript is not happy with .use(LanguageDetector) . It claims that:
Argument of type '{ type: string; async: boolean; init: (services: any, detectorOptions: any, i18nextOptions: any) => void; detect: (callback: any) => string; cacheUserLanguage: (lng: any) => void; }' is not assignable to parameter of type 'Module | ThirdPartyModule[] | Newable
[] | Newable '.
I don't know zustand...
and without having a reproducable example it's not easy...
but maybe instead of "detect: detectLocale()," don't call the function, but just pass it, like "detect: detectLocale,"
Here is a reproductible example: https://codesandbox.io/s/holy-mountain-663k8?file=/src/i18n/index.ts
Please note the typescript error on i18n/index.ts at line 39. To reproduce the issue, just add detectLocale() on line 25 (same file: i18n/index.ts)
If there is a better way to retrieve the user's locale on any device, I'd be happy to hear your thoughts. Maybe my strategy is not that good with this library.
For example, I could:
myAppLocale token in localeStorage inside the cacheUserLanguage:function (I think it is its purpose right?)myAppLocale token to my user locale. myAppLocale.But if I do this, I'am afraid there would be performance issues (it might display the whole app in english for 1 second and immediately refresh into french for example). Also, it seems I can only update i18n locale from a React component, not inside an async function (if it is possible, how to do so?). Or maybe there is a possibility to make i18next subscribe to any change in a localStorage token, so I could only update the localStorage in a function and let i18next apdapt?
Seems zustand uses react hooks... and the language-detector knows nothing about react...
Based on a short google search, it seems replacing const { locale } = userStore(); with const { locale } = userStore.getState(); works
https://codesandbox.io/s/blissful-river-3h1hu?file=/src/i18n/index.ts:355-395
It works! Well played! Thank you so much for your time and your help @adrai!
EDIT: if anyone has a the same typescript issue, just write:
type: "languageDetector" as LanguageDetectorAsyncModule["type"],
Most helpful comment
Seems zustand uses react hooks... and the language-detector knows nothing about react...
Based on a short google search, it seems replacing
const { locale } = userStore();withconst { locale } = userStore.getState();workshttps://codesandbox.io/s/blissful-river-3h1hu?file=/src/i18n/index.ts:355-395