felixmosh/i18next-hmr

Client-HMR not working for getStaticPages in NextJS

KingMatrix1989 opened this issue · 16 comments

Describe the bug
Client-HMR does not update resource that required in SSG page but not loaded by default in the I18N configuration.

To Reproduce

  1. Install these two dependencies:
yarn add i18next-hmr i18next-http-backend --dev
  1. Update the next.config.js like so:
const { resolve } = require("path")
const { I18NextHMRPlugin } = require("i18next-hmr/plugin")

const localesDir = resolve("public/locales")

module.exports = {
  // ...other next config options
  webpack(config, context) {
    if (!context.isServer && context.dev) {
      config.plugins.push(new I18NextHMRPlugin({ localesDir }))
    }

    return config
  },
}
  1. Update the next-i18next.config.js like so:
const HttpBackend = require('i18next-http-backend/cjs');

const config = {
  i18n: {
    defaultLocale: 'fa',
    locales: ['en', 'fa'],
    localeDetection: false,
  },
  localePath: './public/locales',
  use: typeof window !== 'undefined' ? [HttpBackend] : [],
};

module.exports = config;
  1. Create a hook for applying the HMR code during development. I created mine in hooks/use-hmr.ts and it looks like this:
import { useTranslation } from "next-i18next"
import { useEffect } from "react"

export const useHMR = (): void => {
  const { i18n } = useTranslation()

  if (process.env.NODE_ENV === "development" && !process.browser) {
    import("i18next-hmr/server").then(({ applyServerHMR }) => {
      applyServerHMR(i18n)
    })
  }

  useEffect(() => {
    if (process.env.NODE_ENV === "development") {
      import("i18next-hmr/client").then(({ applyClientHMR }) => {
        applyClientHMR(i18n)
      })
    }
  }, [i18n])
}
  1. Import and pass next-i18next.config.js to appWithTranslation (typically in pages/_app.tsx) and use the HMR hook created above within the body of the App component:
import { AppProps } from "next/app"
import { FunctionComponent } from "react"
import { appWithTranslation } from "next-i18next"
import { useHMR } from "../hooks/use-hmr"
import i18nextConfig from "../../next-i18next.config"

export const App: FunctionComponent<AppProps> = ({ Component, pageProps }) => {
  useHMR()

  return <Component {...pageProps} />
}

export default appWithTranslation(App, i18nextConfig) // Required to use the HttpBackend that enables HMR on the client
  1. Create a SSG page (here faq):
import { GetStaticProps } from 'next';
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';

const Faq = () => {
  return (
    // Content of page
    <></>
  );
};

export const getStaticProps: GetStaticProps = async ({ locale }) => {
  return {
    props: { ...(await serverSideTranslations(locale, ['common', 'faq', 'footer'])) },
  };
};

export default Faq;
  1. Update content of faq.json (any language)
  2. Update content of common.json (any language)

Expected behavior

  • faq.json updates don't apply to client-side.
  • But Update of common.json, affecting client-side as well.

I am using:

  • OS: Win
  • i18next-hmr version: 1.7.7
  • next-i18next version: 11.0.0
  • i18next-http-backend version: 1.4.0

It seems Client-HMR only works in case that all namespaces declared in next-i18next-config, because the plugin check for i18n.options.ns by default.

If we change the source code of client-hmr.js as below, problem will be solved:

    // other code

    module.hot.accept('./trigger.js', () => {
      const { changedFiles } = require('./trigger.js');
      const currentNSList = Object.keys(i18n.store.data[i18n.language]); // get all loaded namespaces

      const list = changedFiles
        .map((changedFile) => extractLangAndNS(changedFile, currentNSList))
        .filter(({ lang, ns }) => Boolean(lang) && Boolean(ns));

      if (!list.length) {
        return;
      }

      log(`Got an update with ${printList(list)}`);

      return reloadTranslations(list);
    });

#108

Thank you, I'll review it asap

As I've suspected, it is an issue in next-i18next which is not passing the namespaces to the client, so the data contains some namespace, but not the list.

On the server side it works since the options.ns is correct.

server side

image

client side

image
image

Let's open a bug in next-i8next

I've opened a new issue on next-i18next

i18next configuration may contain only default namespace common, so options.ns will be equal to ['common']; on both the server and client sides.

It is not true, in the images I've added, the values are from server & client at runtime

@felixmosh You are right.
And what do you think about i18n.store.data? It includes all namespaces on both sides.

It's possible, but I don't want to solve a bug of other lib.

@skmohammadi if you have sometime, maybe you can prepare a PR for next-i18next, it should be straight forward.

It's possible, but I don't want to solve a bug of other lib.

It appears to be a problem with your library and implementation of i18n.store.data will correct it.

How i18n.options related to this lib, this lib is only reading from it.
The problem is that the field is not properly passed to the client (as a proof, server side works properly).

Hi @skmohammadi , can you try v11.2.1 of next-i18next it should work now.
Check i18next/next-i18next#1839

Hi @felixmosh. I appreciate you following up on this matter. Sure, I'll check it out.

Thank you @felixmosh. Now, it works as expected.

Can we close this issue?