Как создать компонент переключателя языка в Next.js (Pages Router) v16

Переключайте языки, оставаясь на той же странице

Проблема

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

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

Решение

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

Шаги

1. Создайте компонент переключателя языка, который считывает текущий маршрут

Объект маршрутизатора предоставляет свойства pathname, asPath, query, locale и locales, которые содержат всю необходимую информацию для создания ссылок, учитывающих локаль.

import { useRouter } from "next/router";
import Link from "next/link";

export default function LanguageSwitcher() {
  const router = useRouter();
  const { locale, locales, pathname, asPath, query } = router;

  return (
    <nav>
      {locales?.map((loc) => (
        <Link key={loc} href={{ pathname, query }} as={asPath} locale={loc}>
          {loc.toUpperCase()}
        </Link>
      ))}
    </nav>
  );
}

Компонент Link принимает свойство locale для перехода на другую локаль из текущей активной. Передача pathname и query в виде объекта в href сохраняет всю информацию о маршрутизации, включая значения динамических параметров маршрута.

2. Стилизуйте активную локаль для визуальной обратной связи

Выделите текущий язык, чтобы пользователи знали, какую локаль они просматривают.

import { useRouter } from "next/router";
import Link from "next/link";

export default function LanguageSwitcher() {
  const router = useRouter();
  const { locale, locales, pathname, asPath, query } = router;

  return (
    <nav>
      {locales?.map((loc) => {
        const isActive = loc === locale;
        return (
          <Link
            key={loc}
            href={{ pathname, query }}
            as={asPath}
            locale={loc}
            style={{
              fontWeight: isActive ? "bold" : "normal",
              textDecoration: isActive ? "none" : "underline",
              marginRight: "1rem",
            }}
          >
            {loc.toUpperCase()}
          </Link>
        );
      })}
    </nav>
  );
}

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

3. Добавьте доступные метки с использованием react-intl

Замените коды локалей на читаемые названия языков для улучшения удобства использования.

import { useRouter } from "next/router";
import { useIntl } from "react-intl";
import Link from "next/link";

const localeNames: Record<string, string> = {
  en: "English",
  es: "Español",
  fr: "Français",
  de: "Deutsch",
};

export default function LanguageSwitcher() {
  const router = useRouter();
  const intl = useIntl();
  const { locale, locales, pathname, asPath, query } = router;

  return (
    <nav
      aria-label={intl.formatMessage({
        id: "languageSwitcher.label",
        defaultMessage: "Select language",
      })}
    >
      {locales?.map((loc) => {
        const isActive = loc === locale;
        return (
          <Link
            key={loc}
            href={{ pathname, query }}
            as={asPath}
            locale={loc}
            aria-current={isActive ? "true" : undefined}
            style={{
              fontWeight: isActive ? "bold" : "normal",
              textDecoration: isActive ? "none" : "underline",
              marginRight: "1rem",
            }}
          >
            {localeNames[loc] || loc}
          </Link>
        );
      })}
    </nav>
  );
}

Хук useIntl предоставляет доступ к функциям форматирования для перевода меток пользовательского интерфейса. Атрибуты aria-label и aria-current улучшают доступность для пользователей экранных читалок.