i18next-http-backend icon indicating copy to clipboard operation
i18next-http-backend copied to clipboard

Use with nextjs not watching changes in json files on development

Open bryanltobing opened this issue 1 year ago • 16 comments

🐛 Bug Report

I want to watch changes in the translation JSON files like reloadOnPrerender in next-18next does https://github.com/i18next/next-i18next#reloading-resources-in-development

But every time I make changes to JSON files it doesn't read the changes automatically. In order for the changes to be applied, I need to:

  • shut down my development server
  • clear localStorage (if I didn't clear localStorage I will get a hydration error) Error: Text content does not match server-rendered HTML. See more info here: https://nextjs.org/docs/messages/react-hydration-error
  • run my dev server again, and it is really a hassle.

To Reproduce

I am following example from i18next-http-backend with nextjs https://github.com/i18next/i18next-http-backend/tree/master/example/next

this is my configuration

const path = require("path");
const HttpBackend = require("i18next-http-backend/cjs");
const ChainedBackend = require("i18next-chained-backend").default;
const LocalStorageBackend = require("i18next-localstorage-backend").default;

module.exports = {
  backend: {
    backendOptions: [{ expirationTime: 60 * 60 * 1000 }, {}], // 1 hour
    backends:
      typeof window !== "undefined" ? [LocalStorageBackend, HttpBackend] : [],
  },
  i18n: {
    defaultLocale: "en",
    locales: ["en", "id"],
    reloadOnPrerender: process.env.NODE_ENV === "development",
    localePath: path.resolve("./public/locales"),
  },
  serializeConfig: false,
  use: typeof window !== "undefined" ? [ChainedBackend] : [],
  react: {
    useSuspense: true,
  },
};

Expected behavior

Every time I make changes to JSON files and refresh the page the changes will apply, (check at runtime) similar to reloadOnPrerender in next-i18next when using serverSideTranslations

Your Environment

  • runtime version: i.e. node v16.13.2
  • i18next version
"i18next-chained-backend": "^3.0.2",
"i18next-http-backend": "^1.4.1",
"i18next-localstorage-backend": "^3.1.3",
"next-i18next": "^11.0.0",
  • os: Windows
  • react, next version:
"next-i18next": "^11.0.0",
"next": "^12.2.2",
"react": "^18.2.0",
"react-dom": "^18.2.0"

I also created a discussion in next-i18next https://github.com/i18next/next-i18next/discussions/1914

bryanltobing avatar Jul 19 '22 10:07 bryanltobing

A couple of inputs:


Regarding localstorage during dev... you could change your config with something like:

const HttpBackend = require('i18next-http-backend/cjs')
const ChainedBackend= require('i18next-chained-backend').default
const LocalStorageBackend = require('i18next-localstorage-backend').default

const isBrowser = typeof window !== 'undefined'
const isDev = process.env.NODE_ENV === 'development'

module.exports = {
  debug: isDev,
  backend: isDev ? {} : {
    backendOptions: [{ expirationTime: 60 * 60 * 1000 }, {}], // 1 hour
    backends: isBrowser ? [LocalStorageBackend, HttpBackend]: [],
  },
  // react: { // used only for the lazy reload
  //   bindI18n: 'languageChanged loaded',
  //   useSuspense: false
  // },
  i18n: {
    defaultLocale: 'en',
    locales: ['en', 'de'],
  },
  serializeConfig: false,
  use: isBrowser ? [isDev ? HttpBackend : ChainedBackend] : [],
}

Regarding hot reload, you may check out i18next-hmr, I never used it in combination with next.js, maybe @felixmosh can help?


Regarding the hydration warning:

If client render and server renders outputs different html this may occur: https://github.com/i18next/i18next-http-backend/issues/91

There's an alternative usage, you can try: https://locize.com/blog/next-i18next/#alternative-usage https://github.com/i18next/i18next-http-backend/tree/master/example/next#alternative-usage

adrai avatar Jul 19 '22 10:07 adrai

Thank you for the response @adrai

  • So basically in the development, we are not saving the translation to the localStorage to prevent such errors?
  • I haven't checked i18next-hmr yet. but is this really something that is not supported by default?
  • I have checked the alternative usage, but it doesn't apply to my case at all. because it still requires the use of getStaticProps. The reason I use the client-side translation for all my next.js pages in the first place is to prevent the use of serverSideTranslation
  • I'm not using serverSideTranslation because of this issue https://github.com/i18next/next-i18next/issues/1400#issuecomment-1178610620 with dynamic routes and I'm following your comment here https://github.com/i18next/next-i18next/issues/1400#issuecomment-1178610620

bryanltobing avatar Jul 19 '22 10:07 bryanltobing

So basically in the development, we are not saving the translation to the localStorage to prevent such errors?

yes

I haven't checked i18next-hmr yet. but is this really something that is not supported by default?

no

I have checked the alternative usage, but it doesn't apply to my case at all. because it still requires the use of getStaticProps. The reason I use the client-side translation for all my next.js pages in the first place is to prevent the use of serverSideTranslation

then you have to live with that warning, sorry (that's the nature of it)

adrai avatar Jul 19 '22 11:07 adrai

Not sure if this is the right place to ask or related to this. But I'm curious how did you guys handle dynamic routes using next-i18next then, if in the case that we can't prefetch all our dynamic paths in getStaticPaths. I think this is common cases.

for example, if we have a page to list all users /users/[id].tsx, if we want to use serverSideTranslations we need to use getStaticProps, and because it is dynamic page we need to use getStaticPaths as well to list our ids, and locales.

if we fetch all user's ids from the backend I think it would be a very expensive request to make at build time, for example, if we have millions ids of users.

And also become a hassle to add fetch for every features users, transactions, orders, carts, etc

This has been asked many times. but there's no proper explanation for this I think for such common cases. Instead of answering with a solution, people just provide a hack

  • https://stackoverflow.com/questions/70548101/failing-to-get-quite-simple-i18n-and-dynamic-routing-to-work-with-next-js
  • https://stackoverflow.com/questions/66963434/how-to-add-next-i18next-translation-on-a-dynamically-routed-page-in-a-nextjs-app
  • https://github.com/i18next/next-i18next/issues/1400
  • https://github.com/i18next/next-i18next/issues/1237 and many others

This is why I switch to full client-side translation using next-i18next and i18next-http-backend

bryanltobing avatar Jul 19 '22 11:07 bryanltobing

Sorry, but I've not such a use case like you describe. Mostly static stuff... Also I'm relatively new to next.js https://github.com/i18next/next-i18next/issues/1897 So, if you know of a way for an alternative approach, feel free to contribute to next-i18next with a PR.

adrai avatar Jul 19 '22 11:07 adrai

Regarding hot reload, you may check out i18next-hmr, example folder which contains an example setup for popular frameworks.

It works perfectly with next.js on a daily basis :]

felixmosh avatar Jul 19 '22 12:07 felixmosh

Actually what I need here is not a hot reload. I just want the changes made to be applied at runtime (browser refresh) without the need to restart the server reloadOnPrerender does. not when saving files which I believe is what hmr does.

The browser is actually able to read the changes and apply them in page refresh, but I got this error when refreshing the page, and there's flickering between the old translation to the new translation

old translation

// common.json
{
  "button": {
     "register": "Register"
  }
}

new translation

// common.json
{
  "button": {
     "register": "Register Here"
  }
}

https://user-images.githubusercontent.com/46083126/179746541-687d59e5-2d6b-4c3b-adb5-d385ad4c3e95.mp4

next-dev.js?3515:24 Warning: Text content did not match. Server: "Register" Client: "Register Here"
    at button
    at eval 

when I restart the dev server it works fine.

I presume this is again because of the hydration things since I only use client-side translation.

bryanltobing avatar Jul 19 '22 12:07 bryanltobing

I suspect you need to move the reloadOnPrerender option out of the i18n option in your config

i18n: {
    defaultLocale: "en",
    locales: ["en", "id"],
-    reloadOnPrerender: process.env.NODE_ENV === "development",
    localePath: path.resolve("./public/locales"),
  },
+reloadOnPrerender: process.env.NODE_ENV === "development",

adrai avatar Jul 19 '22 12:07 adrai

This is exactly the problem that i18next-hmr solves, when the translations are change, your browser (& server) will reflect the new translation (without reloading dev server or the browser state).

https://user-images.githubusercontent.com/9304194/71188474-b1f97100-2289-11ea-9363-257f8a2124b1.gif

felixmosh avatar Jul 19 '22 12:07 felixmosh

I suspect you need to move the reloadOnPrerender option out of the i18n option in your config

i18n: {
    defaultLocale: "en",
    locales: ["en", "id"],
-    reloadOnPrerender: process.env.NODE_ENV === "development",
    localePath: path.resolve("./public/locales"),
  },
+reloadOnPrerender: process.env.NODE_ENV === "development",

I have always used it inside i18n obj and it still works in my static site, just tried to move it out, and still works. both work in my static site.

But in my case here for client-side translation, neither work

My current updated i18n configuration next-i18next.config.js

const path = require("path");
const HttpBackend = require("i18next-http-backend/cjs");
const ChainedBackend = require("i18next-chained-backend").default;
const LocalStorageBackend = require("i18next-localstorage-backend").default;

const isDev = process.env.NODE_ENV === "development";
const isBrowser = typeof window !== "undefined";

module.exports = {
  backend: isDev
    ? {}
    : {
        backendOptions: [{ expirationTime: 60 * 60 * 1000 }, {}], // 1 hour
        backends: isBrowser ? [LocalStorageBackend, HttpBackend] : [],
      },
  i18n: {
    defaultLocale: "en",
    locales: ["en", "id"],
    localePath: path.resolve("./public/locales"),
  },
  serializeConfig: false,
  use: isBrowser ? [isDev ? HttpBackend : ChainedBackend] : [],
  react: {
    useSuspense: true,
  },
  reloadOnPrerender: process.env.NODE_ENV === "development",
};

bryanltobing avatar Jul 19 '22 12:07 bryanltobing

This is exactly the problem that i18next-hmr solves, when the translations are change, your browser (& server) will reflect the new translation (without reloading dev server or the browser state).

Question. Why is the example here https://github.com/felixmosh/i18next-hmr/blob/master/examples/next-with-next-i18next/pages/second-page.js using getInitialProps, is it just to prove that It works in client and server? or it's required? and not using useTranslation hooks and passing t in page props instead?

bryanltobing avatar Jul 19 '22 12:07 bryanltobing

Why is the example here

getInitialProps is not mandatory, for client side HMR you will need i18next-http-backend (this lib), it works with Webpack's HMR mechanisem.

felixmosh avatar Jul 19 '22 12:07 felixmosh

getInitialProps is not mandatory, for client side HMR you will need i18next-http-backend (this lib), it works with Webpack's HMR mechanism.

I see that the example uses the very old version of next-i18next. is there any example using the current version ? i try to upgrade to the latest version and there are some breaking changes like next-i18next API that is not exported anymore

bryanltobing avatar Jul 19 '22 12:07 bryanltobing

Check the new example of the latest next-i18next

felixmosh avatar Jul 19 '22 19:07 felixmosh

If you have any issue, I will be more than happy to help you, just open an issue on the i18next-hmr repo :]

felixmosh avatar Jul 20 '22 07:07 felixmosh

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 Aug 13 '22 03:08 stale[bot]