How to refresh next-i18next content update automatically?
Tags: nextjs, i18next, react
Published: 2021-06-13 08:51:04

## tl;dr
If you want to refresh your Next.js app using [`next-i18next`](https://github.com/isaachinman/next-i18next) automatically, you can do it like below:

```tsx
import { GetStaticProps } from "next";
import { i18n } from "next-i18next";
import { serverSideTranslations } from "next-i18next/serverSideTranslations";
import { useRouter } from "next/router";
import { useEffect } from "react";
import Home from "../components/home";

export const getStaticProps: GetStaticProps = async ({ locale }) => {
  if (process.env.NODE_ENV === "development") {
    await i18n?.reloadResources();
  }
  return {
    props: {
      ...(await serverSideTranslations(locale!, ["common"])),
    },
  };
};

const Index = () => {
  if (process.env.NODE_ENV === "development") {
    const router = useRouter();
    useEffect(() => {
      const timer = setInterval(() => {
        router.replace(router.asPath, undefined, {
          scroll: false,
        });
      }, 5000);
      return () => clearTimeout(timer);
    });
  }
  return <Home />;
};

export default Index;
```

## What was the issue?
When I started using [`next-i18next`](https://github.com/isaachinman/next-i18next), I realized `next dev` server only loaded the translation files once when initialized and never updated even if I reloaded the page on my browser since server side doesn't change. Whenever I updated the translation, I needed to restart `next dev` server, which was bad developer experience.

## Alternative solutions
That is a known limitation and there are multiple GitHub issues like [this](https://github.com/isaachinman/next-i18next/issues/881). I was able to find two approaches:

1. Restart `next dev` automatically whenever the content is updated.
1. Poll API endpoint to monitor the content update and refresh the content.

The option 1 is simple and [`nodemon`](https://www.npmjs.com/package/nodemon) can easily achieve the goal. However, this is not "Fast Refresh" and takes a while.

The option 2 seems better because `next dev` server keeps running, but too complicated to implement internal API. It can be done without API like [`next-remote-watch`](https://github.com/hashicorp/next-remote-watch) which monitors files and calls Next.js's internal method to reload the page. I tried it but it still requires implementation of content refresh by calling `i18n.reloadResources()` anyway. Also, page refresh is not "Fast Refresh" neither.

## Solution
Then, I realized this can be done much simpler. First of all, it anyway requires polling from client side because there is no public method to execute ["Fast Refresh"](https://nextjs.org/docs/basic-features/fast-refresh) from Next.js server side. Using internal method like `next-remote-watch` does is not sustainable. Therefore, client side polling is the best way.

However, setting up an API (i.e. `/api/something`) for such a simple polling seems overkill. I thought it's probably enough by just re-rendering the page. With this approach, unless the virtual DOM of React has been updated, nothing happens on client side (I think).

Now, how I can tell the translation files' change to the client? Next.js has a good mechanism to provide props to page i.e. `GetStaticProps` which is already used by `next-i18next` installation. I found [a great solution](https://www.joshwcomeau.com/nextjs/refreshing-server-side-props/) to trigger this from client side.

In addition, I found that it can call `i18n.reloadResources()` there because `i18n` instance is stored in a global value. I lazily implemented it with reloading `i18n` at every request because my project doesn't have large translation files. This can eliminate file watcher logic at all.

## Conclustion
Now, by adding a simple `SetInterval()` to refresh the page every 5 seconds on client side and reloading `i18n` on every `GetStaticProps` call, my Next.js pages are always in sync within 5 seconds. This is Next.js/React refresh, not browser refresh nor server restart, thus it's fast enough.

Let me know if you have a better solution or if you find a drawback of this solution :)

## Notes
When your URL has hash (#foo), `router.replace()` always scrolls up to the anchor and doesn't reload preps from the server. This is a known issue and there is a discussion on GitHub repository: https://github.com/vercel/next.js/discussions/13804