When you want to include i18n
in your react component library, but the two use cases of i18next
conflict with one another — long story short, you need I18nextProvider
.
Step 1: create i18next instance
In component library, we will need to create an i18next
instance, and not use initReactI18next
to initialize it, because initReactI18next
generates a global config, which will pollute the outside use cases.
import i18next from 'i18next'
// Note that we are using createInstance here
const i18n = i18next.createInstance(
{
lng: 'en',
fallbackLng: 'en',
ns: ['translation'],
defaultNS: 'translation',
react: { useSuspense: false },
interpolation: { escapeValue: false },
resources: {
en: { translation: require('./locales/en/translation.json') },
fr: { translation: require('./locales/fr/translation.json') },
},
},
// We must provide a function as second parameter, otherwise i18next errors
(err, t) => {
if (err) return console.log(err)
},
)
export default i18n
Step 2: I18nextProvider
import i18next from 'i18n'
import { useTranslation, I18nextProvider } from 'react-i18next'
export default function FantasticComponent(props) {
// here we make sure that the scope of translations is confined within this component. Translation method t() is not available yet
return (
<I18nextProvider i18n={i18next}>
<GridPage {...props} />
</I18nextProvider>
)
}
function FantasticComponentWithContext({ language }) {
// Within i18n context, we now have access to t()
const { t, i18n } = useTranslation()
// here we pass in `language` as one of the props so we can sync its change:
useEffect(() => {
i18n.changeLanguage(language)
}, [i18n, language])
return <h1>{t('blast')}</h1>
}
Could provide an example how to use consumer i18n instance in component library?
I enjoyed reading your blog post. In the project I’m working on, we use several internal shared module libraries within a single host app. I’m wondering if it’s okay for each library to create and manage its own i18n instance.
For example, the main application project has an i18n instance created and maintained within it. Then, a modal component used to render a specific page in the project creates its own i18n instance, and likewise, a card component rendering a card list on the same page creates yet another i18n instance. I’m concerned that this might lead to excessive load at the application level.
Application A
Modal component library B used by Application A
Card component library C used by Application A
function FantasticComponentWithContext({ language }) {
// Within i18n context, we now have access to t()
const { t, i18n } = useTranslation()
// here we pass in `language` as one of the props so we can sync its change:
useEffect(() => {
i18n.changeLanguage(language)
}, [i18n, language])
return <h1>{t('blast')}</h1>
}
In this example, does the FantasticComponentWithContext component have access to an i18n context provided by the library instance that exports this component?
If not, useTranslation will likely reference the context from the application that is using the component library. In that case, what would happen if the i18n instance at the application level does not contain the resource with the blast key used in FantasticComponentWithContext?