Как запомнить выбор языка между сессиями в Next.js (Pages Router) v16

Сохранение явного выбора языка пользователя

Проблема

Когда пользователь явно выбирает язык, этот выбор отражает его предпочтение и должен иметь приоритет над автоматическим определением языка через заголовки браузера или геолокацию. Без сохранения это предпочтение теряется при закрытии браузера или завершении сессии. При следующем посещении приложение начинает с нуля, заставляя пользователя снова выбирать язык. Такое повторение сигнализирует о том, что приложение не уважает предпочтения пользователя, создавая неудобства и снижая доверие.

Проблема состоит из двух частей: фиксирование явного выбора пользователя в момент выбора и восстановление этого выбора при последующих посещениях до запуска логики автоматического определения. Если сохраненное предпочтение не проверяется на раннем этапе обработки запроса, пользователь может быть перенаправлен на основе настроек браузера, а не своего явного выбора, что сводит на нет ценность сделанного выбора.

Решение

Сохраняйте выбор языка пользователя в постоянной cookie, когда он явно выбирает язык. При последующих посещениях корня приложения проверяйте эту cookie перед тем, как использовать автоматическое определение языка на основе настроек браузера. Если найдено действительное сохраненное значение локали, перенаправьте пользователя на корневой путь этой локали, гарантируя, что его предпочтение будет немедленно учтено.

Этот подход отделяет явный выбор пользователя от автоматического определения. Cookie выступает в роли устойчивого сигнала намерения, который сохраняется после перезапуска браузера и имеет приоритет над временными сигналами, такими как заголовок Accept-Language. Проверяя cookie на стороне сервера во время начального запроса, перенаправление происходит до рендеринга страницы, обеспечивая бесшовный опыт.

Шаги

Когда пользователь выбирает язык, сохраните его выбор в cookie, которая сохраняется между сессиями.

export function setLocalePreference(locale: string) {
  const maxAge = 60 * 60 * 24 * 365;
  document.cookie = `NEXT_LOCALE=${locale}; path=/; max-age=${maxAge}; SameSite=Lax`;
}

Эта функция записывает cookie с именем NEXT_LOCALE и выбранным языком, действительную в течение одного года. path=/ гарантирует доступность cookie во всем приложении, а SameSite=Lax обеспечивает разумную защиту от CSRF, позволяя отправлять cookie при навигации на верхнем уровне.

2. Вызовите вспомогательную функцию при выборе языка пользователем

Интегрируйте вспомогательную функцию в компонент переключателя языка, чтобы предпочтение сохранялось сразу после выбора.

import { useRouter } from "next/router";
import { setLocalePreference } from "@/lib/locale";

export default function LanguageSwitcher() {
  const router = useRouter();
  const { locales, locale: currentLocale } = router;

  const handleLocaleChange = (newLocale: string) => {
    setLocalePreference(newLocale);
    router.push(router.pathname, router.asPath, { locale: newLocale });
  };

  return (
    <select
      value={currentLocale}
      onChange={(e) => handleLocaleChange(e.target.value)}
    >
      {locales?.map((loc) => (
        <option key={loc} value={loc}>
          {loc.toUpperCase()}
        </option>
      ))}
    </select>
  );
}

Когда пользователь меняет выбор, cookie устанавливается, и роутер переходит на ту же страницу на новом языке. Cookie будет доступна для всех последующих запросов.

3. Проверьте сохранённое предпочтение на корневой странице

На корневой странице в getServerSideProps прочитайте cookie и перенаправьте на сохранённый язык, если он существует и является допустимым.

import { GetServerSideProps } from "next";

export const getServerSideProps: GetServerSideProps = async (context) => {
  const storedLocale = context.req.cookies.NEXT_LOCALE;
  const { locales, defaultLocale } = context;

  if (
    storedLocale &&
    locales?.includes(storedLocale) &&
    storedLocale !== defaultLocale
  ) {
    return {
      redirect: {
        destination: `/${storedLocale}`,
        permanent: false,
      },
    };
  }

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

export default function RootPage() {
  return null;
}

Этот код проверяет, существует ли cookie NEXT_LOCALE и содержит ли она допустимый язык из настроек приложения. Если сохранённый язык не является языком по умолчанию, пользователь перенаправляется на корневую страницу этого языка. В противном случае он перенаправляется на язык по умолчанию. Перенаправление выполняется на стороне сервера до рендеринга, что гарантирует, что пользователь сразу попадает на правильный язык.

4. Настройте маршрутизацию локалей в Next.js

Убедитесь, что ваш next.config.js определяет поддерживаемые локали, чтобы логика перенаправления могла проверять сохранённые предпочтения.

module.exports = {
  i18n: {
    locales: ["en", "fr", "de", "es"],
    defaultLocale: "en",
  },
};

Эта конфигурация включает встроенную маршрутизацию i18n в Next.js и делает locales и defaultLocale доступными в getServerSideProps. Логика корневой страницы использует эти значения для проверки сохранённого cookie и построения правильного адреса перенаправления.