I'm having trouble with the useTranslation hook from the next-translate package in my Next.js application. While all my languages are being recognized, I encounter a hydration error whenever I change the language and refresh the page.

Here's the setup I have in my i18n.js file located at the root:

module.exports = {
  locales: ["ar", "en","es"],
  defaultLocale: "en",
  pages: {
    "*": ["common"],
  },
};

I'm using useTranslation in my components as follows:

import useTranslation from "next-translate/useTranslation";

const ComponentName = () => {
  const { t } = useTranslation("common");
  // rest of the component
}

I've placed all my translations inside the public folder. When I change the language, I store the new language in local storage.

The issue arises when I change the language and then refresh the page. I get a hydration error because the server side language is detected as English, while the client has the custom language I had selected and stored in local storage.

Is there a way to solve this problem such that the server side recognizes the selected language upon refreshing? I have tried all the ways. Any help or guidance is greatly appreciated. https://drive.google.com/file/d/1WSBKdXKTpOlQv6g2v1c2KpnZaZjecHhf/view?usp=sharing - here is my folder strucutre

I implemented multi-language support using the next-translate/useTranslation hook in my Next.js application. I stored the selected language in local storage when changing languages. I expected the application to maintain the selected language even after a page refresh, seamlessly translating the page to the chosen language.On refreshing the page after changing the language, I encountered a hydration error. The server side detected English (the default language) while the client had the custom language I selected, causing a mismatch.

2

There are 2 answers

0
Sheraff On

If you want the initial page load to be the correct language, you'll need to store the language preference in a way that is shared with the server. Like in the URL example.com/es or example.com?lang=es or in the cookies. Then you should initialise the language based on that value, even on the server.


If you set it in the cookies, you'll also have to respond with a Vary header that indicates that page content can vary depending on the cookie, which could be kinda bad if you have other stuff that changes a lot from one user to the next in the cookie...

On the server, you could also read the Accept-Language header to have a better chance of serving the page in the correct language the first time (and if you do, respond with a Vary header that indicates content might change based on Accept-Language).


If you decide to use the URL instead, the usual choice (for aesthetics) is example.com/es/page-name. So then you need a few redirections:

  • an internal rewrite that transforms /es/page-name into /page-name?lang=fr so that internally it hits the correct Next page code, just with a language param. This rewrite should be invisible to the user, I know this can be done with next middlewares
  • a client redirection, if you want, that could take someone who's accessing example.com but already has a preference set for another language to example.com/es

Those were ways to actually generate a different language on the server, but if all you want is to avoid the hydration error, just initialize the language on the client with the language that was set by the server, and then immediately change it (in a useEffect that will be executed after first render).

0
xperaz On

Make sure you import your useTranslation from the i18n Next.js library next-i18next instead of react-i18next. This is because the response of serverSideTranslations is a custom object with _nextI18Next property.

After that, wrap around your entry component App with appWithTranslation higher-order component like so: appWithTranslation(App). This is so that whenever the static props (including _nextI18Next) is returned from the server-side, the client-side will then be able to figure out how to hydrate those data back into the React Context.

I found the answer here: