lingui / js-lingui

šŸŒ šŸ“– A readable, automated, and optimized (3 kb) internationalization for JavaScript
https://lingui.dev
MIT License
4.31k stars 368 forks source link

Ids are displayed sometimes on production build #1956

Open semoal opened 3 weeks ago

semoal commented 3 weeks ago

Describe the bug We're seeing an strange behaviour on Next.js, after a redirect or reload sometimes the translations are lost and we only can see the ids on the screen.

To Reproduce I'm not sure how even is this reproducible, I'll try to reproduce it but couldn't find a way to do it.

Expected behavior To not lose translations.

Additional context Add any other context about the problem here. image (10)

Code i18n.ts:

export function useLinguiInit(messages?: Messages) {
  const router = useRouter();
  const locale = router.locale || router.defaultLocale!;
  const isClient = typeof window !== "undefined";

  if (!isClient && locale !== i18n.locale) {
    // there is single instance of i18n on the server
    // note: on the server, we could have an instance of i18n per supported locale
    // to avoid calling loadAndActivate for (worst case) each request, but right now that's what we do
    i18n.loadAndActivate({ locale, messages });
  }
  if (isClient && (i18n.locale === "" || i18n.locale === undefined)) {
    // first client render
    i18n.loadAndActivate({ locale, messages });
    activateDayjsLocale(locale);
  }

  useEffect(() => {
    const localeDidChange = locale !== i18n.locale;
    if (localeDidChange) {
      i18n.loadAndActivate({ locale, messages });
      activateDayjsLocale(locale);
    }
  }, [locale, messages]);

  return { i18n };
}

_app.ts:

  const { i18n } = useLinguiInit(pageProps.translation);
        <I18nProvider i18n={i18n}>
....
thekip commented 3 weeks ago

if you are not using a global t in your code, i will recommend to create a i18n instance for each render and pass it down the tree. This makes configuration less complicated and avoid all kind of these issues.

semoal commented 3 weeks ago

We're using the useLingui hook on every component and pass the i18n instance to each t(i18n)

thekip commented 3 weeks ago

so you can simplify useLinguiInit to something like that

export function useLinguiInit(messages?: Messages) {
  const router = useRouter()
  const locale = router.locale || router.defaultLocale!

  const i18n = useMemo(
    () =>
      setupI18n({
        locale,
        messages: {
          [locale]: messages,
        },
      }),
    [messages, locale]
  )

  return { i18n }
}
root-io commented 1 week ago

Just to note I had a similar issue: https://github.com/lingui/js-lingui/discussions/1816

@semoal is your Next.js project setup with output: "standalone"?

semoal commented 1 week ago

Just to note I had a similar issue: https://github.com/lingui/js-lingui/discussions/1816

@semoal is your Next.js project setup with output: "standalone"?

Yea it is, and Iā€™m still seeing this error sometimes in the production build, usually is shown after the user has expired session and we redirect to login page via getServerSideProps.

Even passing i18n to all t() and initializing as @thekip said

thekip commented 5 days ago

So you need to debug it's on your own. Most likely nextjs skipping calling your root component during some client-side navigation.

PS the concept of hybrid router presented in nextjs is very complex, despite it hiding complexity from end user the complexity is still there. There are many things which can go wrong because some believes it should work like that but in fact it works differently. That creates very hard to catch bugs with weird STRs. That's the price you have to pay working with nextjs (or not pay by choosing many other good frameworks out here)