js-lingui icon indicating copy to clipboard operation
js-lingui copied to clipboard

Consistent warning that "I18nProvider did not render ..."

Open tommhuth opened this issue 2 years ago • 10 comments

When using I18nProvider from @lingui/react in a Next app, I got a consistent console warning every-time warning that

I18nProvider did not render. A call to i18n.activate still needs to happen or forceRenderOnLocaleChange must be set to false.

We load the locale async in an useEffect like

	const { locale = 'en' } = useRouter();

	useEffect(() => {
		async function load(locale: string) {
			const { messages } = await import(
				`../translations/locales/${locale}/messages.po`
			);

			i18n.load(locale, messages);
			i18n.activate(locale);
		}

		load(locale);
	}, [locale]);

meaning activate will always get called, though not in the initial server side render output. Is this warning an indication of an issue or problem? If so how can I fix it? Setting forceRenderOnLocaleChange to false is not an option as I cannot see a reason why I would not want children to rerender if the user changes the language.

Or is this async locale load simply not the way to do it?

tommhuth avatar Jan 14 '22 13:01 tommhuth

I18nProvider did not render. A call to i18n.activate still needs to happen or forceRenderOnLocaleChange must be set to false. Me too.

usercao avatar Jan 20 '22 10:01 usercao

@tommhuth You need to set forceRenderOnLocaleChange={false}

And create local instance like this

  const [i18n] = useState(() =>
    setupI18n({
      messages: {
        [locale]: { ...messages },
      },
      localeData: {
        en: { plurals: en },
        ru: { plurals: ru },
      },
      locale,
    }),
  );

On ssr I created thunk with webpack dynamic import First SSr render completed with correct locales On static (no ssr) mode app waiting for tryToLoad locale (its quickly operation)

dmitryshelomanov avatar Jan 25 '22 13:01 dmitryshelomanov

SSR steps -> fetchData -> create store -> render app (locales already)

(Between page navigation we can local locales same) -> go to page -> call fetch -> render page with next data

Static steps

-> render app -> call fetch inside effects -> try import and set flag to store -> in root return null if false (its more usable then fonts change from lang to another lang)

dmitryshelomanov avatar Jan 25 '22 13:01 dmitryshelomanov

Same question as @tommhuth and same setup. Language files are loaded in async/await and only then activate()/load() is called. It seems to be working anyway but quite annoying if it doesn't really indicate a real issue. It should be possible though according to https://lingui.js.org/guides/dynamic-loading-catalogs.html#final-i18n-loader-helper

boy-bizzmine avatar Feb 18 '22 14:02 boy-bizzmine

I ran into the same issue after applying the dynamic loading method. It seems that it is triggered by the <I18nProvider> component when it is first rendered, before the useEffect hook runs.

Maintainers, please correct me if I'm wrong. As far as I can see things still work as expected. The warning doesn't seem to do any harm, but it is annoying and it is costing developers time because it looks like something that needs to be fixed.

As a workaround, to get rid of the warning, I added two lines to the I18n loader helper, immediately after the call to i18n.loadLocaleData():

i18n.load(defaultLocale, {});
i18n.activate(defaultLocale);

This activates the default locale with an empty set of messages, so at least a "dummy locale" is active when <I18nProvider> is first rendered.

marcvangend avatar Mar 15 '22 21:03 marcvangend

dmitryshelomanov do you have a complete example of your solution ?

gpessa avatar May 06 '22 19:05 gpessa

Running in to the same issue with a ssg next.js app. is there an official solution to this problem? if so I could make a pull request on the official next.js example.

feledori avatar Jul 06 '22 17:07 feledori

haven't found one so far

gpessa avatar Jul 06 '22 18:07 gpessa

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

stale[bot] avatar Sep 08 '22 22:09 stale[bot]

still happening

gpessa avatar Sep 09 '22 08:09 gpessa

Seeing this exact problem as well.

oyed avatar Oct 05 '22 07:10 oyed

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

stale[bot] avatar Dec 04 '22 11:12 stale[bot]

Please refer to ice-test of this project for a complete solution.

usercao avatar Dec 04 '22 11:12 usercao

@gpessa create codesandbox for me with default states (lingui, redux (?)) and etc I will try to share complex solution

dmitryshelomanov avatar Dec 10 '22 14:12 dmitryshelomanov

i'm cloning the example in this repo. if you load the index, and check the source code of the generate page, you'll see it empty

gpessa avatar Dec 14 '22 20:12 gpessa

// Provider

import { ReactNode, useEffect, useState } from 'react';
import { en, ru } from 'make-plural';
import { setupI18n } from '@lingui/core';
import { I18nProvider } from '@lingui/react';
import { useStore } from 'effector-react/scope';
import { $locale, $messages } from './model';

type Props = {
  children: ReactNode;
};

export function LocaleProvider({ children }: Props) {
  const locale = useStore($locale);
  const messages = useStore($messages);

  const [i18n] = useState(() =>
    setupI18n({
      messages: {
        [locale]: { ...messages },
      },
      localeData: {
        en: { plurals: en },
        ru: { plurals: ru },
      },
      locale,
    }),
  );

  useEffect(() => {
    i18n.load(locale, { ...messages });
    i18n.activate(locale);
  }, [i18n, locale, messages]);

  return (
    <I18nProvider i18n={i18n} forceRenderOnLocaleChange={false}>
      {children}
    </I18nProvider>
  );
}

model

import { Messages } from '@lingui/core';
import { createEffect, createEvent, createStore, sample } from 'effector';
import { appStarted } from 'shared/start';

export const $locale = createStore('ru');
export const $messages = createStore<Messages>({});

export const changeLocale = createEvent<string>();

export const loadLocalesFx = createEffect(async ({ locale }: { locale: string }) => {
  const rootMessages = await import(/* webpackChunkName: "root-i18-[request]" */ `i18n/locales/${locale}.js`).then(
    ({ messages }) => messages as Messages,
  );

  return rootMessages;
});

$locale.on(loadLocalesFx.done, (_, { params }) => params.locale);
$messages.on(loadLocalesFx.doneData, (_, messages) => messages);

sample({
  clock: appStarted,
  source: $locale,
  fn: locale => ({ locale }),
  target: loadLocalesFx,
});

sample({
  clock: changeLocale,
  fn: locale => ({ locale }),
  target: loadLocalesFx,
});

With redux

effector replace with redux and use createAsyncThunk for handle async import, selectors for data access

@gpessa

dmitryshelomanov avatar Dec 18 '22 08:12 dmitryshelomanov

and you need to trans with

const {i18n} = useLingui()

Even if you use lingui macro

macro transform you code and set i18 global when local not found

dmitryshelomanov avatar Dec 18 '22 08:12 dmitryshelomanov

@andrii-bodnar I believe this can be closed as the implementation has changed in v4 🙂

vonovak avatar May 26 '23 13:05 vonovak