ReactJS

How to have multiple instances of i18next for component library

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>
}
Standard

3 thoughts on “How to have multiple instances of i18next for component library

  1. yuna says:

    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

  2. yuna says:

    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?

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.