Hello,
I have a question about the utilization of I18nextProvider.
Here is my code :
.use(initReactI18next)
.init({
resources: {
en: {
translation: {
"Validate": "Validate"
},
},
fr: {
translation: {
"Validate": "Valider"
}
},
lng: "fr",
fallbackLng: "fr",
interpolation: {
escapeValue: false
}
}
});
i18next.changeLanguage('fr')
class App extends Component {
render() {
return (
<I18nextProvider i18n={i18next}>
<Toto/>
</I18nextProvider>
);
}
}
export default App;
And my component Toto is :
class Toto extends Component {
render() {
const { t } = this.props;
return (
<p>{t('Validate')}</p>
)
}
}
const translatedToto = withTranslation()(Toto);
export { translatedToto as Toto };
If my Toto is in the same project that my component App it works well.
But if my Toto component is in an external package, the translation doesn't works and I have the following message in the console :
react-i18next:: You will need pass in an i18next instance by using i18nextReactModule
I am currently developing a components library so I use npm link to make the link between my components library and my test project.
So my question is the following : There is a particular way to use I18nextProvider in this specific case ?
Generally, what is the best practice to use react-i18next with a components library ?
Thanks.
Damien
My guess it should work as long your component library does not export some bundles that bundle their own react version -> which in any case will break hooks and context
But I never had the need for that - as most of the components we have in our component libraries are rather plain...where the text in most cases came via props...like eg. <Button>{t('save')}</Button>
If your components are very complex doing something like:
import i18next from 'i18next';
import Backend from 'i18next-xhr-backend';
import LanguageDetector from 'i18next-browser-languagedetector';
// import { initReactI18next } from 'react-i18next';
const i18n = i18next.createInstance();
i18n
// load translation using xhr -> see /public/locales
// learn more: https://github.com/i18next/i18next-xhr-backend
.use(Backend)
// detect user language
// learn more: https://github.com/i18next/i18next-browser-languageDetector
.use(LanguageDetector)
// pass the i18n instance to react-i18next.
// .use(initReactI18next) // not use as it's a singleton pattern -> use I18nextProvider
// init i18next
// for all options read: https://www.i18next.com/overview/configuration-options
.init({
fallbackLng: 'en',
debug: true,
interpolation: {
escapeValue: false, // not needed for react as it escapes by default
},
});
export default i18n;
And wrapping your components in that lib with the I18nextProvider might be an option.
But you're using
i18next
.use(initReactI18next)
that should set i18n properly on every usage of react-i18next --> so really seems you bundle two react-i18next
Thank you for your quick response it's clearly enjoyable !
So with your response I understand that my approach is not correct...
I will create my components library like you, with text in props.
All is ok for me --> Issue closed.
I'm hitting this issue as well. I could open a new issue, but it exactly fits this, so I figured I'd add on to this closed issue and maybe have it reopened.
My project topology looks like this; I have a "core" component library (We'll call it library-core) that provides some convenience components, and the translated text for the components. Then I have a bunch of "styled" libraries (We'll call one of them library-style) that use the core components, but wrap them in commonly used component libraries such as Bootstrap, Semantic UI etc. So you can use my component library, but still have it match your project's theme.
So the way it works is that library-core provides a component that wraps the library-style components in a <I18nextProvider />, while also providing some components that are translated. The library-style components then use withRouter to use the translations provided by library-core. However, only library-core's components are being translated and getting the correct props via context, while library-style's components complain with react-i18next:: You will need pass in an i18next instance by using i18nextReactModule. The library-core module has i18next and react-i18next as peer dependencies, and aren't included in its bundle, so I don't _think_ it's an issue with them being double included.
The only way I can think to get around this is to have both libraries do their own <I18nextProvider /> wrapper, and just have library-core export its i18n instance. That seems just a bit less clean than my expected solution though.
I don't want to make the text props as the OP did though, since half the point of using my component library is it provides helpful instruction copy to users on how to complete tasks so you don't have to explain to them how to do something. If you've got any thoughts on this, would love some help! I could also link to the libraries themselves if that would help.
@wbobeirne hard to tell what happens and why your code break - only thing i can say it's userland: I18nextProvider is a simple react context (zero magic) - all HOCs, render props and hook take i18n instance from the context if available - else the one instance passed using the i18n.use(...)
Also if you pass me the links i won't have the time to dig through all your code - just not have the time for this -> if you can make a small codesandbox reproducing.
Yep, that's totally fair. After doing some inspection of my rollup output, it looks like for some reason I was ending up with two outputs of react-i18next which each had their own context variable. This caused them not to share the same i18n, despite being nested. I couldn't quite figure out why this was happening, so I've simply decided to remove react-i18next and export the i18n object from the library-core module itself, and just use that directly.
Less than ideal, but I've had enough rollup configuration hell for one day. But as you said, it looks like this is a configuration issue, not anything inherent to react-i18next. Sorry for wasting your time!
If you like this module don鈥檛 forget to star this repo. Make a tweet, share the word or have a look at our https://locize.com to support the devs of this project -> there are many ways to help this project :pray:
I am facing a somewhat similar problem. I am working on a project that has three react frontends and only for one of them - the app, i18n-react is instantiated. Also, there is an external component-library that is used by all three frontends. The question is now, how do you optimally translate the texts in the component-library components?
1) Do you pass the t function from the app components down to the component-library components and leave all the translation work to the component-library components in dependence of the t function
2) Do you translate all texts in the app components beforehand and pass the translations down to the component-library components
@ndbeyer both works...it depends on what you prefer...
Encountered the exact same problem, but I'm not sure I grasped the behaviour of I18nextProvider correctly.
Say I have the following structure:
components
app
The components package gets transpiled with its own instance of React (same version though as in app). Does this mean that any context state is not shared between the two packages, hence the i18nobject is different?
own instance of react...very bad idea
I'm rather new to React Context. Is it better to just keep components un-transpiled, import them in app and let CRA handle the bundling?
By "better" - I mean whether the i18n would be available in components then.
you can transpile them...but exclude react from being included...there at anytime should be only one react instance...that's all
You can use <I18nextProvider /> to reference the same instance.
Here is my solution to refactoring the i18nInstance to a separate library (for a lerna monorepo for example)
// utils-lib.js lives outside of the front-end/app project
import { initReactI18next } from "react-i18next";
export const i18nInstance = i18n
.use(otherThings)
.use(initReactI18next);
i18nInstance.init(generateI18nInitOptions(), error => {
// handle your errors
});
// App.tsx or App root
import { i18nInstance } from 'utils-lib';
const App = () =>
<I18nextProvider i18n={i18nInstance}>
<MyComp />
</I18nextProvider>
import { useTranslation } from 'react-i18next';
const MyComp = () => {
const { t, i18n } = useTranslation();
return <div>{t('tag')}</div>
}
Most helpful comment
you can transpile them...but exclude react from being included...there at anytime should be only one react instance...that's all