React Router v7で多言語コンテンツをマークする方法

アクセシビリティのために異なる言語のテキストをマークする

問題

ページに複数の言語のテキストが含まれている場合、ブラウザと支援技術はすべてのコンテンツがページで宣言された言語を使用していると想定します。英語用に設定されたスクリーンリーダーは、フランス語のフレーズ、スペイン語の書籍タイトル、ドイツ語の名前を英語の音声学を使用して発音しようとするため、音声に依存するユーザーにとって理解不能な出力が生成されます。ブラウザはページ言語に基づいてスペルチェックとタイポグラフィのルールを適用し、外国語の単語を誤ってエラーとしてフラグを立て、言語固有の句読点を誤って処理します。検索エンジンと翻訳ツールは、明示的な言語マーカーなしでは多言語コンテンツを正確に処理できません。

これは、スクリーンリーダーユーザーだけでなく、自動翻訳、テキスト読み上げ、リーディングモードなど、言語検出に依存するブラウザ機能を使用するすべてのユーザーに影響します。

解決策

外国語のテキストを、正しい言語コードに設定されたlang属性を持つHTML要素でラップします。lang属性は、ブラウザと支援技術にその特定のコンテンツの言語ルールを切り替えるよう指示し、スクリーンリーダーによる正確な発音と、ブラウザによる適切なスペルチェックとタイポグラフィを保証します。

<span><i>などのセマンティックHTML要素を使用して、レイアウトを崩すことなくインラインの外国語テキストをラップします。lang属性は、ISO 639-1言語コード(フランス語の場合はfr、スペイン語の場合はesなど)と、方言固有の処理のためのオプションの地域コードを受け入れます。

手順

1. 外国語テキストをマークするためのコンポーネントを作成する

適切なlang属性を持つspan要素でテキストをラップする再利用可能なコンポーネントを構築します。

type ForeignTextProps = {
  lang: string;
  children: React.ReactNode;
};

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

このコンポーネントは言語コードを受け取り、子要素をlang属性を持つspanでラップすることで、スクリーンリーダーが囲まれたテキストの発音規則を切り替えられるようにします。

2. コンポーネントを使用してインラインの外国語テキストをマークする

コンテンツ内の外国語の単語やフレーズをForeignTextコンポーネントでラップします。

export default function ArticlePage() {
  return (
    <article>
      <h1>International Cuisine</h1>
      <p>
        The restaurant's signature dish is{" "}
        <ForeignText lang="fr">coq au vin</ForeignText>, a classic French
        preparation that pairs perfectly with their house wine.
      </p>
      <p>
        Their dessert menu features{" "}
        <ForeignText lang="es">tres leches</ForeignText> and{" "}
        <ForeignText lang="it">tiramisu</ForeignText>.
      </p>
    </article>
  );
}

スクリーンリーダーは「coq au vin」をフランス語の音声規則で、「tres leches」をスペイン語の音声規則で、「tiramisu」をイタリア語の音声規則で発音し、周囲の英語テキストは英語の発音を使用します。

3. より長い外国語の文章をマークする

複数の文からなる外国語コンテンツの場合は、lang属性をブロックレベル要素に直接適用します。

export default function QuotePage() {
  return (
    <article>
      <h1>Universal Declaration of Human Rights</h1>
      <h2>Article 1</h2>
      <blockquote lang="fr">
        <p>
          Tous les êtres humains naissent libres et égaux en dignité et en
          droits. Ils sont doués de raison et de conscience et doivent agir les
          uns envers les autres dans un esprit de fraternité.
        </p>
      </blockquote>
    </article>
  );
}

blockquote要素にlangを適用することで、引用全体をフランス語としてマークし、文章全体の正しい発音とタイポグラフィを保証します。

4. 翻訳されたUIテキストと組み合わせる

外国語コンテンツを翻訳されたインターフェーステキストと一緒に表示する場合は、UI要素にはreact-intlを使用し、ユーザー生成コンテンツや引用コンテンツにはlang属性を使用します。

import { FormattedMessage } from "react-intl";
import { ForeignText } from "~/components/ForeignText";

export default function BookReview() {
  return (
    <article>
      <h1>
        <FormattedMessage id="review.title" defaultMessage="Book Review" />
      </h1>
      <p>
        <FormattedMessage
          id="review.intro"
          defaultMessage="The novel {title} explores themes of identity and belonging."
          values={{
            title: <ForeignText lang="de">Der Steppenwolf</ForeignText>,
          }}
        />
      </p>
    </article>
  );
}

インターフェーステキストはreact-intlを通じてユーザーのロケールに適応し、書籍タイトルはアクセシビリティのための適切な言語マーキングとともに元のドイツ語を保持します。