DEV Community

Cover image for react-i18next useTranslation Does not Get Same Provider
Eyal Lapid
Eyal Lapid

Posted on • Updated on

react-i18next useTranslation Does not Get Same Provider

Photo by Amador Loureiro on Unsplash

Scenario

Using react-i18next with I18nextProvider instead of one global provider.

In our case, in a hybrid app with Angular and React, the i18next instance was generated in the Angular app. The React sub app needed that instance instead of its own.

  • js monorepo using yarn workspaces and yarn pnp
  • angular app with webpack bundler - bundle all including libraries and external third parties
  • react sub apps with rollup bundler - leaves out external to be bundle by the end app

Case Problem

At first, it seems that the provider was receiving the instance correctly.

But the useTranslation hook, when asking for the react i18next conext, always received and empty one. Not the instance that was set on the provider

  const { i18n: i18nFromContext, defaultNS: defaultNSFromContext } = useContext(I18nContext) || {};
Enter fullscreen mode Exit fullscreen mode

Solution - TLDR

Make the js bundler provider the same react-i18next library instance for both apps/libraries.

For webpack and yarn pnp, we use the reslove alias configuration:

   resolve: {
      alias: {
        'react-i18next': require.resolve('react-i18next'),
      },
      extensions: ['.ts', '.js'],
    },
Enter fullscreen mode Exit fullscreen mode

Solution Walkthrough

To debug the situation, the context can be marked by adding a property guid to the object.

import * as rx from '@proftit/rxjs';
import { I18nextProvider, I18nContext } from 'react-i18next';
import { TranslationsContext } from './TranslationsContext';
import { useObservable, useObservableState } from 'observable-hooks';

export function TranslationsProvider({ value, children }) {
  const i18n = useMemo(() => {
    value.__guid = 'translation-provider-top';
    return value;
  }, [value]);

  return (
    <TranslationsContext.Provider value={value}>
      {i18n && <I18nextProvider i18n={i18n}>{children}</I18nextProvider>}
    </TranslationsContext.Provider>
  );
}
Enter fullscreen mode Exit fullscreen mode

Then, in the useTranslation that does not work at the further down component, we can step into the useTranslation code and see if the useContext gives the same instance with the __guid property on it.

Since it does not, this indicate that the I18nContext given as a token for the useContext to search the correct context in its storage, is not the same.

To prove it, again, we can import the I18nContext in the TranslationProvider and tag it also. Then in the useTranslation we see it is not the same instance

import { getI18n, getDefaults, ReportNamespaces, I18nContext } from './context';

// ....
  const { i18n: i18nFromContext, defaultNS: defaultNSFromContext } = useContext(I18nContext) || {};
Enter fullscreen mode Exit fullscreen mode

This phenomenon is explained in more details here

In some js bundling scenarios, one library gets a different instance of the js module of third party library like react-i18next then the other library.

Since React useContext works on reference equality for the tokens for its dependency injection like mechanism, it does not find the correct context.

This can happen more generally for other libraries that stumble on the same use case situation. The solution for webpack is the same

    resolve: {
      alias: {
        'react-i18next': require.resolve('react-i18next'),
        '@emotion/react/jsx-runtime': emotionJsxPath,
        '@emotion/react': require.resolve('@emotion/react'),
      },
   }
Enter fullscreen mode Exit fullscreen mode

Storybook Solution

This also happens in a monorepo with storybooks packages as self packages.

The solution is the same. You need to configure storybook main.js webpack settings to resolve the the import to the same package instance.

main.js

module.exports = {
  resolve: {
      alias: {
        'react-i18next': require.resolve('react-i18next'),
      },
      extensions: ['.ts', '.js'],
    }
}
Enter fullscreen mode Exit fullscreen mode

References

Discussion (0)