I have a page which contains 2 React apps, each having their own i18n.js config file like the one below. The last app to load seems to conflict with the "translation" namespace of the first app and triggers an error. The only way that I can have both of them load successfully is to import the i18n.js from the first app and add additional namespaces to it at runtime. However, doing it this way creates a dependency between two unrelated apps.
// React App #1
// project/src/ReactApp1/i18n.js
i18n
.use(LanguageDetector)
.use(initReactI18next)
.init({
fallbackLng: "en",
interpolation: {escapeValue: false},
resources: {
"en": {translation: require("./translations/en.json")},
},
});
export default i18n;
// Workaround for React App #2
// project/src/ReactApp2/i18n.js
// Ideally I would like to use a config file like the one above instead of this one which
// depends on the first config.
import i18n from "../ReactApp1/i18n";
i18n.addResourceBundle("en", "a_different_namespace", require("./translations/en.json"));
export default i18n;
Occurs in react-i18next version
i18next 14.1.1 / react-i18next 10.0.2
Expected behaviour
I expected that two separate i18next instances would not overwrite each other's translation namespaces.
So you're using the same i18next instance on both apps?
create separate instances like: https://www.i18next.com/overview/api#createinstance
I have created two separate instances as you suggested in your last comment, but the second instance still contains translations from the namespace of the first instance:
import * as LanguageDetector from "i18next-browser-languagedetector";
import i18n from "i18next";
import {initReactI18next} from "react-i18next";
const newInstance1 = i18n.createInstance();
newInstance1
.use(LanguageDetector)
.use(initReactI18next)
.init({
fallbackLng: "en",
interpolation: {
escapeValue: false,
},
resources: {
"en": {translation: require("./translations/en.json")},
"en-GB": {translation: require("./translations/en-GB.json")},
},
});
export default newInstance1;
import * as LanguageDetector from "i18next-browser-languagedetector";
import i18n from "i18next";
import {initReactI18next} from "react-i18next";
const newInstance2 = i18n.createInstance();
newInstance2
.use(LanguageDetector)
.use(initReactI18next)
.init({
fallbackLng: "en",
interpolation: {
escapeValue: false,
},
resources: {
"en": {translation: require("./translations/en.json")},
},
});
export default newInstance2;
When I log the i18n instance in the second app via const {t, i18n} = this.props; console.log(i18n); i18n.store.data contains translations from the first instance.
Ah and further your using same react-i18next for both apps? If so .use(initReactI18next) won't work as it's one global state. You will need to use the I18nextProvider: https://react.i18next.com/latest/i18nextprovider#what-it-does to pass i18n to your JSX tree
Thank you for your advice, @jamuhl! I will give this a try in the next day and report my results. Does the <I18nextProvider> tag need to be outside of my <App> component, or can I put it as the outermost tag inside of my
class App {
render() {
return (
<I18nextProvider i18n={i18n}>
<div>Hello, world!</div>
<OtherComponent/>
</I18nextProvider>
);
}
}
Just remove the .use(initReactI18next) in i18n.js and place the I18nextProvider where you like -> just must be a parent of all the other provided react-i18next components so they can pick up the i18next instance passed in from context
@msheakoski thanks, unfortunately, your CSB link leads to a 404
hey @jamuhl , i tried the solution suggested but it didn't worked.
I develop a component library that use 18next. the consumer of the npm, also use 18next.
we created instance for each and pass it down using the provider component.
when i removed .use(initReactI18Next) it corrupted the translation in the component and didn't help in the consumer with the initial issue. any other solution that might work ?
I18n.js file - in the components project:
import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
const resources = {
en: {
translation: require('./translations/messages_en.json'),
},
bg: {
translation: require('./translations/messages_bg.json'),
},
}
const widgetInstance = i18n.createInstance();
widgetInstance
// pass the i18n instance to react-i18next so the t function will be in the context.
.use(initReactI18next)
// init i18next
// for all options read: https://www.i18next.com/overview/configuration-options
.init({
resources,
react: {
useSuspense: false,
},
fallbackLng: 'en',
debug: true,
interpolation: {
escapeValue: false, // not needed for react as it escapes by default
},
})
.then(result => result)
.catch(error => console.error(error));
export default widgetInstance;
the component using it:
import i18next from '../../../locale/i18next';
....
<I18nextProvider i18n={i18next}>
<div className="myComponent"/>
</ I18nextProvider i18n={i18next}>
i18n.js in the consumer of the components npm library:
import i18next from ‘i18next’;
import {initReactI18next} from “react-i18next”;
const newInstance1 = i18next.createInstance();
export default function i18n(locale: string) {
void newInstance1.use(initReactI18next)
.use({
type: ‘backend’,
read: (
language: string,
namespace: string,
callback: (
err: any | null,
translations?: Record<string, string>,
) => void,
) => {
return import(`./locales/messages_${locale}.json`)
.then(translation => {
callback(null, translation);
})
.catch(error => {
callback(error);
});
},
})
.init({
lng: locale,
fallbackLng: “en”,
keySeparator: false,
});
return newInstance1;
}
the component using it in the provider in the consumer:
import i18n from ‘../../i18n’;
<I18nextProvider i18n={i18n(locale)}>
<LibrayComponent {...props} />
</I18nextProvider>
in your component library using the newly created instance don't use .use(initReactI18next) only work with wrapping into the i18nextProvider
@jamuhl thx for responding, i dropped the .use(initReactI18next) from the component library. and we have providers on the top component in the library and in the consumer.
(we tried also, to have provider only on the consumer) still, not working. any other solution ?
note* - the library component pass the i18n object exported from the i18n.ts, to the provider like this :
import i18next from '../../../locale/i18next';
<I18nextProvider i18n={i18next}>
while the consumer use it like this:
import i18n from ‘../../i18n’;
<I18nextProvider i18n={i18n(locale)}>
<Widget {...props} />
</I18nextProvider>
not sure what's going wrong in your setup...can only tell we're doing the same without any issue...even using 2 different backends -> loading two different locize projects...
ummmm... thx anyway, ill update when i find whats wrong
Most helpful comment
It worked! I left an example for any future readers:
I appreciate your assistance with my issue and also the great work you have put into i18next!