如何在 Next.js(Pages Router)v16 中标记混合语言内容

为不同语言的文本添加可访问性标记

问题

当页面包含多种语言的文本时,浏览器和辅助技术会将所有内容视为页面的主要语言。例如,配置为英语的屏幕阅读器会用英语发音规则来朗读法语短语、西班牙语书名或德语公司名,这会导致依赖音频的用户听到难以理解的内容。浏览器还会根据错误的语言应用拼写检查和排版规则,将拼写正确的外语单词标记为错误,并错误处理特定语言的标点和格式规范。

这会为依赖准确发音的视障用户带来障碍,同时由于错误的拼写警告和不正确的文本渲染,也会影响所有用户的体验,增加视觉干扰。

解决方案

为包含外语文本的元素添加 HTML lang 属性,明确声明该内容的语言。该属性会告知浏览器和辅助技术,标记的文本应按照声明的语言规则处理,而不是页面的主要语言。屏幕阅读器会切换到相应的发音引擎,浏览器会应用正确的拼写检查词典,排版引擎也会采用特定语言的格式规则。

通过将外语文本包裹在带有正确 lang 属性的元素中,可以在内容中创建清晰的语言边界,从而保证多语言文本的完整性。

步骤

1. 创建带语言标记的文本组件

构建一个 React 组件,将外语内容包裹在 span 元素中,并添加合适的 lang 属性。

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

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

该组件接受一个语言代码,并将其子元素包裹在 span 中,同时设置 lang 属性,从而创建一个语言边界,便于辅助技术和浏览器识别。

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

lang 直接应用于块级元素(如 blockquotep)可以标记整个片段,使屏幕阅读器在朗读时保持一致的发音,浏览器也能对完整上下文应用语言规则。

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

将支持的语言定义为常量,可以在编辑器中获得自动补全,并清晰记录应用中使用了哪些语言代码,同时在需要时仍可使用任意语言代码。