How to enforce i18n locale slugs and achieve i18n consistency upon reload in Next.js?



I’m using next-translate. By default, my routes are recognized as follows:

/about         <---
/de/about
/es/about

but I’d like to enforce a locale for all paths:

/en/about      <---
/de/about
/es/about

Here’s my config:

next.config.js

const nextTranslate = require('next-translate');

module.exports = nextTranslate({
    ...
    i18n: {
        localeDetection: false,
        locales: ['en', 'de', 'es'],
        defaultLocale: 'en',
    }
});

i18n.js

module.exports = {
    locales: ['en', 'de', 'es'],
    defaultLocale: 'en',
    pages: {
        '*': ['common']
    },
    interpolation: {
        prefix: '${',
        suffix: '}',
    },
    loadLocaleFrom: (locale, namespace) =>
        import(`./translations/${locale}/${namespace}`).then((m) => m.default),
}

Note that I also have a lang change component that persists the NEXT_LOCALE cookie. As such, I would expect that when I access /about and my NEXT_LOCALE cookie had previously been set to de, the router would redirect me to /de/about. But it does not. It stays at /about and rewrites the cookie to en

Here’s current pages folder structure:

...
pages/
  _app.tsx
  _document.tsx
  about.tsx
  ...

Do I need to restructure it to this?

pages/
  _app.tsx
  _document.tsx
  [lang]/         <---
    about.tsx
    ...

If so, what would be the next step?

  • Parse the preferred locale through useRouter()
  • Parse the NEXT_LOCALE cookie
  • Parse the lang slug

and then decide which has higher precedence? Where should I do that? In _app.tsx / some HOC?

Do I need any rewrites or redirects in my next.config.js or should I handle these dynamically through Router.push?

Answer

The fact that the persisted NEXT_LOCALE cookie doesn’t automatically redirect based on its value is because you have explicitly disabled it by setting localeDetection: false. This affects the header-based redirection as well as the cookie-based one.

Simply removing it from your next.config.js should solve that issue.

const nextTranslate = require('next-translate');

module.exports = nextTranslate({
    ...
    i18n: {
        locales: ['en', 'de', 'es'],
        defaultLocale: 'en',
    }
});

There’s no built-in way to force the default locale on all paths. However, you could check for the default locale and set it explicitly in the URL on first mount through router.push.

Let’s assume the following custom useDefaultLocale hook that abstracts the logic to be re-used.

import { useState, useEffect } from 'react';
import { useRouter } from 'next/router';

export const useDefaultLocale = () => {
    const router = useRouter();

    useEffect(() => {
        if (router.locale === router.defaultLocale) {
            router.push(`/${router.locale}${router.asPath}`, undefined, {
                locale: false,
                shallow: true // Optionally add this if you don't want to rerun data fetching methods
            });
        }
    }, [router.asPath]);
};

Which could then be used in your pages or _app.js.

import { useDefaultLocale } from '<path-to>/use-default-locale';

const AboutPage = () => {
    useDefaultLocale()

    return <>About Page</>;
};


Source: stackoverflow