2

We're using Next.js and want to route all paths (not just root) to locale-based paths based on the browser Accept-Language header. However, if the user SETS their region, we will set a cookie that would need to be checked first to respect user preferences.

So we need to check for the cookie, and if it's not there, try redirect based on browser language header instead. We're using ISG so limited to next.config.js redirects serverside.

According to the docs, this should work, but since we're using ISG, we need to do this in next.config.js redirects function.

We've tried this solution and it does not work (we get infinite redirects as both cookie AND header match):

const { i18n } = require('./next-i18next.config');
const withTM = require('next-transpile-modules')(['fitty', 'react-svg']); // pass the modules you would like to see transpiled

const handleLocaleRedirects = (path) => {
  const result = [];
  i18n.locales.forEach((locale) => {
    i18n.locales.forEach((loc) => {
      if (loc !== locale) {
        result.push({
          source: `/${locale}${path}`,
          has: [
            {
              type: 'header',
              key: 'accept-language',
              value: `^${loc}(.*)`,
            },
          ],
          permanent: false,
          locale: false,
          destination: `/${loc}${path}`,
        });
        result.push({
          source: `/${locale}${path}`,
          has: [
            {
              type: 'cookie',
              key: 'NEXT_LOCALE',
              value: loc,
            },
          ],
          permanent: true,
          locale: false,
          destination: `/${loc}${path}`,
        });
      }
    });
  });
  return result;
};

module.exports = withTM({
  i18n,
  reactStrictMode: true,
  images: {
    domains: [
      'dxjnh2froe2ec.cloudfront.net',
      'starsona-stb-usea1.s3.amazonaws.com',
    ],
  },
  eslint: {
    // Warning: Dangerously allow production builds to successfully complete even if
    // your project has ESLint errors.
    ignoreDuringBuilds: true,
  },
  async redirects() {
    return [...handleLocaleRedirects('/:celebrityId')];
  },
});
fortunee
  • 3,852
  • 2
  • 17
  • 29
  • Question - How are you getting the user's set language? I imagine you'd be doing something like `navigator.language` to get the user's set language then comparing it before redirecting. – fortunee Jul 23 '21 at 18:23
  • User set their language in a region dropdown, so at that point we set the cookie NEXT_LOCALE = en-US. But once we do that, the infinite redirects start. – Augusto Samamé Barrientos Jul 23 '21 at 22:51
  • "since we're using ISG, we need to do this in next.config.js redirects function" - Can you clarify why? Next.js should automatically handle the locale if you're using the `NEXT_LOCALE` cookie. – juliomalves Jul 24 '21 at 09:10
  • Once the NEXT_LOCALE cookie is there, yes, Next.js handles it. But for FIRST TIME visitors to the page, there's no cookie, and Next.js ONLY handles locale based on browser Accept-Language header for the root path, not for subpaths, so we need to handle it ourselves. 90% of our traffic goes to our subpaths on first load. – Augusto Samamé Barrientos Jul 24 '21 at 14:31

1 Answers1

1

I've managed to achieve this using _app.js
Add getInitialProps inside _app.js
It checks cookie inside request, gets current locale using ctx.locale, My default locale is en-IN so if targetLocale matches default locale it sets an empty string to targetLocale, then redirects using header.
Other than that we don't have to use localeDetection because we are handling on our own.

MyApp.getInitialProps = async ({ ctx }) => {
  if (ctx.req) {
    const rawCookies = ctx.req.headers.cookie
    let locale = ctx.locale
    const path = ctx.asPath

    if (rawCookies != undefined) {
      const cookies = cookie.parse(rawCookies)
      let targetLocale = cookies['NEXT_LOCALE']

      if (targetLocale != locale) {
        if (targetLocale == 'en-IN') {
          targetLocale = ''
        } else {
          targetLocale = '/' + targetLocale
        }

        ctx.res.writeHead(302, {
          Location: `${targetLocale}${path}`
        })
        ctx.res.end()
      }
    }
  }

  return {}
}

Other than this, I'm showing modal when there is no cookie named NEXT_LOCALE to handle first time users.

Sølve T.
  • 4,159
  • 1
  • 20
  • 31
Sukhchain Singh
  • 834
  • 1
  • 8
  • 26
  • **Be aware that this disables Automatic Static Optimization** for all pages that does not have "getStaticProps" if I am not mistaking. In my case, I just wanted a redirect on one page. Adding a getInitialProps on this page only worked for me. This way, only this page is computed by the server, the rest behaves normally. – Sølve T. May 09 '22 at 11:58