Next.js(Pages Router) v16에서 언어 전환 컴포넌트를 구축하는 방법

동일한 페이지에 머물면서 언어 전환하기

문제

사용자는 언어 전환기가 현재 위치를 유지하기를 기대합니다. 누군가가 영어로 제품 페이지를 보다가 스페인어로 전환하면, 홈페이지로 리디렉션되는 것이 아니라 스페인어로 된 동일한 제품 페이지를 보기를 원합니다. 이러한 기대를 저버리면 마찰이 발생하고 사용자가 원래 있던 곳으로 다시 이동해야 하므로 경험이 저하되고 잠재적으로 작업을 완전히 포기하게 만들 수 있습니다.

많은 언어 전환기 구현은 언어 선택을 현재 뷰의 변환이 아닌 단순한 탐색으로 취급하기 때문에 실패합니다. 현재 페이지의 라우팅 정보에 액세스할 수 없으면 전환기는 고정된 대상으로만 링크할 수 있어 사용자의 컨텍스트를 잃게 됩니다.

솔루션

라우터에서 현재 경로의 pathname과 쿼리 파라미터를 읽은 다음, 로케일만 변경하면서 이 라우팅 정보를 보존하는 지원되는 각 언어에 대한 링크를 생성하는 언어 전환기를 구축합니다. pathname과 쿼리를 대상 로케일과 함께 탐색 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 prop을 허용합니다. pathnamequery를 객체로 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 훅은 UI 레이블을 번역하기 위한 포맷팅 함수에 대한 액세스를 제공합니다. aria-labelaria-current 속성은 스크린 리더 사용자를 위한 접근성을 향상시킵니다.