Shouldn't i18next wait for language detection result before loading fallback language - i18next

I use react-i18next example as a base.
I replaced 'i18next-browser-languagedetector' by custom language detector:
class MyLanguageDetector {
constructor(services, options = {}) {
this.async = true;
this.init(services, options);
}
init(services, options = {}, i18nOptions = {}) { }
detect(callback) {
setTimeout(() => {
callback('de')
}, 5000);
}
cacheUserLanguage(lng, caches) { }
}
MyLanguageDetector.type = 'languageDetector';
Config:
i18n
.use(MyLanguageDetector)
.use(Backend)
.use(reactI18nextModule)
.init({
fallbackLng: 'en',
debug: true,
interpolation: { escapeValue: false },
react: { wait: true }
});
What I see is:
i18next::backendConnector: loaded namespace translation for language en
and after ~5 seconds:
i18next::backendConnector: loaded namespace translation for language de
I expected that 'en' won't be loaded since detected language is 'de'

fallbackLng is always loaded so that it can be used if current language translation file missing the particular key/resource
See related issue

Related

next-i18next, next export & 404 on non-generated pages (with fallback true/blocking)

Having followed the documentation on i18next/next-i18next to configure i18n and then the instructions on this locize blog post on how to export static sites with next export, I am able to export localised versions of each page.
The problem is that pages that have not been statically generated return a 404, despite setting fallback: true in the getStaticPaths return object. The page works on my localhost but not when deployed with Vercel.
Code:
const ArticlePage: NextPageWithLayout<Props> = ({ article }: Props) => {
const { i18n, t } = useTranslation('page/news/article')
const router = useRouter()
if (router.isFallback) return <div>Loading...</div>
return <div>Article</div>
}
export const getStaticPaths: GetStaticPaths = async () => {
let paths: { params: { aid: string; locale: TLocale } }[] = []
try {
const response = await api.get(`/articles?per_page=9999`)
const articles = response.data.data as IArticle[]
articles.forEach((a) => {
getI18nPaths().forEach((p) => {
paths.push({
params: {
aid: a.base_64_id,
locale: p.params.locale,
},
})
})
})
return {
paths,
fallback: true,
}
} catch (error) {
return {
paths,
fallback: true,
}
}
}
export const getStaticProps: GetStaticProps = async ({ locale, params }) => {
try {
const article = await api.get(`/articles/${params?.aid}`)
return {
props: {
...(await serverSideTranslations(locale || 'en', [
'footer',
'header',
'misc',
'page/news/index',
'page/news/article',
])),
article: article.data as IArticle,
},
}
} catch (error) {
return {
notFound: true,
}
}
}
ArticlePage.getLayout = function getLayout(page: ReactElement) {
return <Layout>{page}</Layout>
}
export default ArticlePage
"i18next": "22.4.9",
"next-i18next": "^13.1.5",
"react-i18next": "^12.1.5",
There is a warning in the console react-i18next:: You will need to pass in an i18next instance by using initReactI18next when entering an non-generated page (alongside the not-found error of course). An issue raised about this warning is interesting but I could not find an answer to my issue within: https://github.com/i18next/next-i18next/issues/1917.
Attempts to fix:
adding revalidate: 10 to the return object of getStaticProps
using fallback: 'blocking'
trying a few different variants of localePath in next-i18next.config including the recommendation featured here: https://github.com/i18next/next-i18next#vercel-and-netlify
adding react: { useSuspense: false } to next-i18next.config
combinations of the above

How can I mock Window in #testing-library or update my configurei18n.ts so it works with #testing-library

Summary
I have a codebase that I'm working on and the i18next/react-i18next. The internationalization is working, but having issues with #testing-library. Normally, the i18n resource files are in .json format, but these are different and can't be changed, so the changes have be make with i18n config or #test-library mocks.
The code currently works by looking at the window, checking the resources folder, and then looking for the language. So if the language is en, it's going to look for the file en in resources = window.Resources.en.
Error
TypeError: Cannot read properties of undefined (reading 'en')
> i18n.addResourceBundle(cultureName, 'translation', window['Resources'][cultureName]);
Project File Structure:
./Resources/en.js
./Code
./Scripts/configurei18n.ts
.package.json
configurei18n.ts
import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
export default function configurei18n(cultureName: string): typeof i18n {
i18n
.use(initReactI18next)
.init({
lng: cultureName,
fallbackLng: cultureName,
supportedLngs: [cultureName],
interpolation: {
escapeValue: false
},
initImmediate: false,
react: {
useSuspense: false
}
});
i18n.addResourceBundle(cultureName, 'translation', window['Resources'][cultureName]);
return i18n;
}
Resource File (English) (en.js)
(function () {
if (typeof window === 'undefined')
window = {};
window.Resources = window.Resources || {};
return window.Resources.en = window.Resources.en || {
"home_title": "REGISTRATION",
"help_header": {
"part1": "Need Help?",
"part2": "Please Contact Customer Service at",
"part3": "Call Customer Service at"
},
}
};
})();

Get i18n language from Async Storage

I am using i18n to translate a React Native app. In i18n.js file:
const getLang = async () => {
const language = await AsyncStorage.getItem("locale");
// console.log(`language |==> `, language);
return "pt";
};
// passes i18n down to react-i18next
i18n.use(initReactI18next).init({
resources: {
en,
pt,
},
lng: getLang(), // getting language from local storage
interpolation: {
escapeValue: false,
},
react: { useSuspense: false },
});
export default i18n;
using getLang() function I try to access Async Storage and get the user's selected language, but I am getting the below error:
I couldn't copy-paste the error, so this is the error I get in the simulator. How do I resolve above stated issue?
Thank You
Because i was looking for help but couldn't find any, i did the following.
In use({}) added a custom languageDetector:
i18n
.use(initReactI18next)
.use({
type: 'languageDetector',
name: 'customDetector',
async: true, // If this is set to true, your detect function receives a callback function that you should call with your language, useful to retrieve your language stored in AsyncStorage for example
init: function () {
/* use services and options */
},
detect: function (callback: (val: string) => void) {
console.log('[LANG] detecting language');
AsyncStorage.getItem('LANG').then((val: string | null) => {
const detected = val || fallbackLanguage;
console.log('[LANG] detected:', detected);
callback(detected);
});
},
cacheUserLanguage: function (lng: string) {
return lng;
},
})
.init({
resources,
fallbackLng: fallbackLanguage,
interpolation: {
escapeValue: false, // react already safes from xss
},
returnObjects: true,
debug: true,
// react-i18next options
react: {
useSuspense: true,
},
detection: {
order: ['customDetector'],
},
})
.then(() => console.log('[INIT] i18n initialized'));
Note it's important the detection.order part because it won't be called otherwise.

How to use react-i18next inside BASIC function (not component)?

I know that react-i18next work in every component: functional (with useTranslation) and class component (with withTranslation()) BUT I can't use translation inside a basic function like this:
const not_a_component = () => {
const { t } = useTranslation();
return t('translation')
};
const translate = not_a_component();
ERROR HOOKS !
Thanks !
You could just use i18next library for translation using javascript.
react-i18next is just a wrapper library on top of i18next.
Below is an example if you are already using react-i18next and it is configured.
import i18next from "i18next";
const not_a_component = () => {
const result = i18next.t("key");
console.log(result);
return result;
};
export default not_a_component;
If you opt to use only i18nextthen you could simply get t function.
It all depends upon your requirement.
import i18next from 'i18next';
i18next.init({
lng: 'en',
debug: true,
resources: {
en: {
translation: {
"key": "hello world"
}
}
}
}, function(err, t) {
// You get the `t` function here.
document.getElementById('output').innerHTML = i18next.t('key');
});
Hope that helps!!!
Alternatively, you can pass t as an additional parameter:
const not_a_component = (t) => {
return t('translation')
};
// Within a component
const { t } = useTranslation()
not_a_component(t)

Handling locale change with react-native-i18next/locize/locize backend

I'm trying to get RN i18next and locize to provide translations based on the current locale from react-native-i18n (Already have translations setup locally)
I was having issues with the languageDetector library found here:
https://github.com/DylanVann/i18next-react-native-language-detector
It would throw an error about not finding the parameter "replace" of an object.
So I figured I would setup my own locize/languageDetector using the following code.
However.. It doesn't seem to update when the locale changes..
What am I doing wrong?
in i18next.js:
import i18next from "i18next";
import LocizeBackend from "i18next-locize-backend";
import I18n from "../i18n/i18n";
const languageDetector = {
init: Function.prototype,
type: "languageDetector",
async: true, // flags below detection to be async
detect: lng => I18n.locale,
cacheUserLanguage: () => {}
};
i18next
.use(languageDetector)
.use(LocizeBackend)
.init({
fallbackLng: I18n.locale,
lng: I18n.locale,
debug: true,
whitelist: ["en", "fr", "sv", "dev"],
keySeparator: false,
ns: ["common"],
defaultNS: "common",
saveMissing: false,
interpolation: {
escapeValue: false
},
backend: {
referenceLng: "en",
}
});
Looks like the language detection you use I18n.locale is not async -> so remove async: true...further if you set language on i18next.init it won't use the detector...
you might also use this detector as sample: https://github.com/dormakaba-digital/digital-reactnative-client/blob/master/src/modules/i18n/i18n.js#L4 -> used deviceInfo module