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>フランス料理を理解する</h1>
      <p>
        <ForeignText lang="fr">mise en place</ForeignText>の概念は
        プロの料理において基本的なものです。これは調理を始める前にすべての材料を
        準備し整理しておくことを意味します。
      </p>
      <p>
        ニューヨークのレストラン<ForeignText lang="fr">Le Bernardin</ForeignText>は
        数十年にわたり三つ星ミシュランを維持しています。
      </p>
    </article>
  );
}

各外国語フレーズはコンポーネントで言語コードとともに囲まれており、スクリーンリーダーが正しく発音し、ブラウザが適切な言語ルールを適用することを保証します。

3. より長い外国語の文章を処理する

複数の文からなる外国語コンテンツの場合、言語コンテキストを断片化しないように、全体を単一の言語マーク付き要素で囲みます。

export default function QuotePage() {
  return (
    <article>
      <h1>世界人権宣言</h1>
      <h2>第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>
  );
}

langblockquotepなどのブロックレベル要素に直接適用することで、文章全体をマークし、スクリーンリーダーが一貫した発音を維持し、ブラウザが言語ルールを全体のコンテキストに適用できるようにします。

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>;
}

サポートされている言語を定数として定義することで、エディタでのオートコンプリートが提供され、アプリケーション全体で使用される言語コードが文書化されます。また、必要に応じて任意の言語コードも許可します。