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

为不同语言的文本标记以提高可访问性

问题

当页面包含多种语言的文本时,浏览器和辅助技术会将所有内容视为页面主要语言的一部分。配置为英语的屏幕阅读器会尝试用英语发音规则来朗读法语短语、西班牙语书名或德语公司名称,这会为依赖音频的用户生成难以理解的输出。浏览器会基于错误的语言应用拼写检查和排版规则,将正确拼写的外语单词标记为错误,并错误处理语言特定的标点符号和格式规则。

这为依赖准确发音的视障用户制造了障碍,同时由于错误的拼写检查警告和不正确的文本渲染引入了视觉噪音,降低了所有用户的体验。

解决方案

将 HTML 的 lang 属性应用于包含外语文本的元素,明确声明该内容的语言。此属性向浏览器和辅助技术发出信号,表明标记的文本应使用其声明语言的规则进行处理,而不是页面主要语言的规则。屏幕阅读器会切换到适当的发音引擎,浏览器会应用正确的拼写检查字典,排版引擎会使用语言特定的格式规则。

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

步骤

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

构建一个 React 组件,将外语内容包裹在带有适当 lang 属性的 span 元素中。

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>第一条</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="这道菜在法式料理中被称为 {dishName}。"
        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>;
}

将支持的语言定义为常量可以在编辑器中提供自动补全功能,并记录应用程序中使用的语言代码,同时在需要时仍允许使用任意语言代码。