How to detect user language preferences in Next.js (Pages Router) v16

Auto-redirect based on browser preferences

Problem

Every browser sends an Accept-Language header with each HTTP request, indicating the user's preferred languages in order of priority. Most applications ignore this valuable signal and serve a default language to all visitors, forcing users to manually search for a language switcher even when the application already knows their preferences. This creates unnecessary friction during the first visit and can lead to users abandoning the site before they find content in their language.

When users land on the root path of an application, there is an opportunity to examine their language preferences and immediately direct them to content in a language they understand. Without this detection, international users face an English-first experience regardless of their browser settings, missing the chance to provide a welcoming, localized first impression.

Solution

Create a root page that runs server-side logic on every request to the root path. Read the Accept-Language HTTP header from the incoming request and parse it to extract the user's most preferred language. Compare this language against the list of languages your application supports. If a match is found, redirect the user to that language's root path. If no supported language matches, redirect to a default language path.

This approach leverages Next.js's getServerSideProps function, which executes on the server for each request and can return a redirect response. By handling detection at the root path, the application provides an intelligent default while still allowing users to manually switch languages later if needed.

Steps

1. Install a library to parse the Accept-Language header

The Accept-Language header contains a comma-separated list of language codes with optional quality values that must be parsed. Install a parser library to handle this format.

npm install accept-language-parser

This library extracts language codes and quality scores from the header string and returns them in priority order.

2. Create a root page with server-side redirect logic

Create a pages/index.tsx file that will be automatically routed to the root directory. Use getServerSideProps to return a redirect object with a destination and permanent flag.

import { GetServerSideProps } from "next";
import parser from "accept-language-parser";

const SUPPORTED_LOCALES = ["en", "fr", "es", "de"];
const DEFAULT_LOCALE = "en";

export default function RootPage() {
  return null;
}

export const getServerSideProps: GetServerSideProps = async (context) => {
  const acceptLanguageHeader = context.req.headers["accept-language"];

  let targetLocale = DEFAULT_LOCALE;

  if (acceptLanguageHeader) {
    const languages = parser.parse(acceptLanguageHeader);

    const matchedLanguage = languages.find((lang) =>
      SUPPORTED_LOCALES.includes(lang.code),
    );

    if (matchedLanguage) {
      targetLocale = matchedLanguage.code;
    }
  }

  return {
    redirect: {
      destination: `/${targetLocale}`,
      permanent: false,
    },
  };
};

The getServerSideProps function runs on each request, reading the Accept-Language header and redirecting to the appropriate locale path before any page content is rendered.

3. Define your supported locales

Update the SUPPORTED_LOCALES array to match the languages your application supports. The parser will return languages in quality order, and the code selects the first match.

const SUPPORTED_LOCALES = ["en", "fr", "es", "de", "ja", "zh"];
const DEFAULT_LOCALE = "en";

The parser returns languages sorted by quality from highest to lowest, so the first supported language found represents the user's strongest preference that your application can fulfill.

4. Handle cases with no Accept-Language header

Some requests may not include an Accept-Language header. The code checks for the header's presence and falls back to the default locale when it's missing.

if (acceptLanguageHeader) {
  const languages = parser.parse(acceptLanguageHeader);

  const matchedLanguage = languages.find((lang) =>
    SUPPORTED_LOCALES.includes(lang.code),
  );

  if (matchedLanguage) {
    targetLocale = matchedLanguage.code;
  }
}

This ensures the application always redirects to a valid locale path, even when browser language information is unavailable.