Next.js(Pages Router) v16에서 다국어 콘텐츠를 표시하는 방법

접근성을 위해 다른 언어의 텍스트 표시하기

문제

페이지에 여러 언어의 텍스트가 포함되어 있을 때, 브라우저와 보조 기술은 모든 콘텐츠가 페이지의 기본 언어에 속하는 것처럼 처리합니다. 영어로 구성된 스크린 리더는 프랑스어 구문, 스페인어 책 제목 또는 독일어 회사 이름을 영어 발음 규칙으로 읽으려고 시도하여 오디오에 의존하는 사용자에게 이해할 수 없는 출력을 생성합니다. 브라우저는 잘못된 언어를 기반으로 맞춤법 검사 및 타이포그래피 규칙을 적용하여 올바르게 철자된 외국어 단어를 오류로 표시하고 언어별 구두점 및 서식 규칙을 잘못 처리합니다.

이는 정확한 발음에 의존하는 시각 장애가 있는 사용자에게 장벽을 만들고, 잘못된 맞춤법 검사 경고 및 부적절한 텍스트 렌더링으로 인한 시각적 노이즈를 도입하여 모든 사용자의 경험을 저하시킵니다.

해결 방법

외국어 텍스트가 포함된 요소에 HTML lang 속성을 적용하여 해당 콘텐츠의 언어를 명시적으로 선언하세요. 이 속성은 브라우저와 보조 기술에 표시된 텍스트가 페이지의 기본 언어가 아닌 선언된 언어의 규칙을 사용하여 처리되어야 함을 알립니다. 스크린 리더는 적절한 발음 엔진으로 전환하고, 브라우저는 올바른 맞춤법 검사 사전을 적용하며, 타이포그래피 엔진은 언어별 서식 규칙을 사용합니다.

올바른 lang 속성을 가진 요소로 외국어 텍스트를 감싸면 다국어 텍스트의 무결성을 보존하는 명확한 언어 경계를 콘텐츠 내에 생성할 수 있습니다.

단계

1. 언어 표시 텍스트 컴포넌트 생성

외국어 콘텐츠를 적절한 lang 속성이 있는 span 요소로 감싸는 React 컴포넌트를 구축하세요.

interface ForeignTextProps {
  lang: string;
  children: React.ReactNode;
}

export function ForeignText({ lang, children }: ForeignTextProps) {
  return <span lang={lang}>{children}</span>;
}

이 컴포넌트는 언어 코드를 받아 자식 요소를 lang 속성이 설정된 span로 감싸서 보조 기술과 브라우저가 인식할 수 있는 언어 경계를 생성합니다.

2. 외국어 구문을 표시하기 위해 컴포넌트 사용하기

적절한 ISO 639-1 언어 코드를 지정하여 컴포넌트를 사용해 콘텐츠 내의 외국어 텍스트를 감쌉니다.

export default function ArticlePage() {
  return (
    <article>
      <h1>Understanding French Cuisine</h1>
      <p>
        The concept of <ForeignText lang="fr">mise en place</ForeignText> is
        fundamental to professional cooking. It means having all ingredients
        prepared and organized before you begin.
      </p>
      <p>
        The restaurant <ForeignText lang="fr">Le Bernardin</ForeignText> in New
        York has maintained three Michelin stars for decades.
      </p>
    </article>
  );
}

각 외국어 구문은 해당 언어 코드와 함께 컴포넌트로 감싸져 있어, 스크린 리더가 올바르게 발음하고 브라우저가 적절한 언어 규칙을 적용할 수 있도록 합니다.

3. 긴 외국어 단락 처리하기

여러 문장으로 구성된 외국어 콘텐츠의 경우, 언어 컨텍스트가 분절되지 않도록 전체 단락을 하나의 언어 표시 요소로 감쌉니다.

export default function QuotePage() {
  return (
    <article>
      <h1>Universal Declaration of Human Rights</h1>
      <h2>Article 1</h2>
      <blockquote lang="es">
        <p>
          Todos los seres humanos nacen libres e iguales en dignidad y derechos
          y, dotados como están de razón y conciencia, deben comportarse
          fraternalmente los unos con los otros.
        </p>
      </blockquote>
    </article>
  );
}

blockquote 또는 p와 같은 블록 수준 요소에 lang를 직접 적용하면 전체 단락이 표시되어 스크린 리더가 전체적으로 일관된 발음을 유지하고 브라우저가 전체 컨텍스트에 언어 규칙을 적용할 수 있습니다.

4. 서식이 지정된 메시지에서 외국어 텍스트 표시하기

번역된 메시지 내에 외국어 콘텐츠가 나타나는 경우, 메시지의 리치 텍스트 서식 내에서 컴포넌트를 사용합니다.

import { FormattedMessage } from "react-intl";

export default function RecipePage() {
  return (
    <div>
      <FormattedMessage
        id="recipe.description"
        defaultMessage="This dish is called {dishName} in French cuisine."
        values={{
          dishName: <ForeignText lang="fr">coq au vin</ForeignText>,
        }}
      />
    </div>
  );
}

이 컴포넌트는 react-intl의 리치 텍스트 서식과 통합되어, 번역된 콘텐츠 내에서 외국어 용어를 표시하면서 보조 기술을 위한 언어 경계를 유지할 수 있습니다.

5. 의미론적 강조를 위한 변형 만들기

외국어 텍스트에 강조나 관용적 스타일링이 필요한 경우, 의미론적 HTML 요소를 사용하도록 패턴을 확장합니다.

interface ForeignEmphasisProps {
  lang: string;
  children: React.ReactNode;
}

export function ForeignEmphasis({ lang, children }: ForeignEmphasisProps) {
  return <i lang={lang}>{children}</i>;
}

i 요소는 의미론적으로 대체 음성이나 분위기의 텍스트를 나타내므로 주변 텍스트와 구분되는 외국어 용어에 적합합니다. lang 속성은 올바른 발음을 보장하고 요소는 의미론적 의미를 제공합니다.

6. 지원되는 언어 코드 문서화하기

애플리케이션이 지원하는 언어 코드를 정의하는 타입 또는 상수를 생성하여 일관성을 보장하고 개발 단계에서 오류를 포착하세요.

export const SUPPORTED_LANGUAGES = {
  FRENCH: "fr",
  SPANISH: "es",
  GERMAN: "de",
  ITALIAN: "it",
  JAPANESE: "ja",
  CHINESE: "zh",
} as const;

type LanguageCode =
  (typeof SUPPORTED_LANGUAGES)[keyof typeof SUPPORTED_LANGUAGES];

interface ForeignTextProps {
  lang: LanguageCode | string;
  children: React.ReactNode;
}

export function ForeignText({ lang, children }: ForeignTextProps) {
  return <span lang={lang}>{children}</span>;
}

지원되는 언어를 상수로 정의하면 에디터에서 자동 완성 기능을 제공하고 애플리케이션 전체에서 사용되는 언어 코드를 문서화하는 동시에, 필요한 경우 임의의 언어 코드도 허용할 수 있습니다.