How to mark mixed-language content in Next.js (Pages Router) v16
Mark text in different languages for accessibility
Problem
When a page contains text in multiple languages, browsers and assistive technologies treat all content as if it belongs to the page's primary language. A screen reader configured for English will attempt to pronounce French phrases, Spanish book titles, or German company names using English phonetics, producing unintelligible output for users who rely on audio. Browsers apply spell-checking and typography rules based on the wrong language, flagging correctly spelled foreign words as errors and mishandling language-specific punctuation and formatting conventions.
This creates barriers for users with visual impairments who depend on accurate pronunciation, and degrades the experience for all users by introducing visual noise from incorrect spell-check warnings and improper text rendering.
Solution
Apply the HTML lang attribute to elements containing foreign-language text, explicitly declaring the language of that content. This attribute signals to browsers and assistive technologies that the marked text should be processed using the rules of its declared language rather than the page's primary language. Screen readers switch to the appropriate pronunciation engine, browsers apply correct spell-checking dictionaries, and typography engines use language-specific formatting rules.
By wrapping foreign text in elements with the correct lang attribute, you create clear language boundaries within your content that preserve the integrity of multilingual text.
Steps
1. Create a language-marked text component
Build a React component that wraps foreign-language content in a span element with the appropriate lang attribute.
interface ForeignTextProps {
lang: string;
children: React.ReactNode;
}
export function ForeignText({ lang, children }: ForeignTextProps) {
return <span lang={lang}>{children}</span>;
}
This component accepts a language code and wraps its children in a span with the lang attribute set, creating a language boundary that assistive technologies and browsers can recognize.
2. Use the component to mark foreign phrases
Wrap foreign-language text within your content using the component, specifying the appropriate ISO 639-1 language code.
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>
);
}
Each foreign phrase is wrapped in the component with its language code, ensuring screen readers pronounce it correctly and browsers apply appropriate language rules.
3. Handle longer foreign-language passages
For multi-sentence foreign content, wrap the entire passage in a single language-marked element to avoid fragmenting the language context.
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>
);
}
Applying lang directly to block-level elements like blockquote or p marks the entire passage, allowing screen readers to maintain consistent pronunciation throughout and browsers to apply language rules to the full context.
4. Mark foreign text in formatted messages
When foreign-language content appears within translated messages, use the component inside the message's rich text formatting.
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>
);
}
The component integrates with react-intl's rich text formatting, allowing you to mark foreign terms within translated content while preserving the language boundary for assistive technologies.
5. Create variants for semantic emphasis
Extend the pattern to use semantic HTML elements when foreign text also requires emphasis or idiomatic styling.
interface ForeignEmphasisProps {
lang: string;
children: React.ReactNode;
}
export function ForeignEmphasis({ lang, children }: ForeignEmphasisProps) {
return <i lang={lang}>{children}</i>;
}
The i element semantically represents text in an alternate voice or mood, making it appropriate for foreign terms that are also set off from surrounding text. The lang attribute ensures correct pronunciation while the element provides semantic meaning.
6. Document supported language codes
Create a type or constant defining the language codes your application supports to ensure consistency and catch errors at development time.
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>;
}
Defining supported languages as constants provides autocomplete in your editor and documents which language codes are used throughout your application, while still allowing arbitrary language codes when needed.