# Passages in another language read with the wrong voice

Source: https://easeweb.dev/learn/language-of-parts
Topics: Page structure > Passages in another language are marked with their own language (WCAG 3.1.2)
The fix: Put lang on the element that holds each passage in another language. Write it into the markup for fixed text, such as each name in the language switcher, and render it from the stored language for content that comes from data, such as customer reviews.
Test with: screen reader
References: https://www.w3.org/WAI/WCAG22/Understanding/language-of-parts.html https://www.w3.org/WAI/WCAG22/Techniques/html/H58 https://www.w3.org/International/questions/qa-lang-why https://www.w3.org/International/questions/qa-choosing-language-tags https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Global_attributes/lang https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/a#hreflang https://dequeuniversity.com/rules/axe/4.10/valid-lang

This is a recreated teaching example, not a finding about a named client. The code is a starting pattern: test it in your own product. No completed assistive-technology test is claimed here.

## What happens

An outdoor shop sells an enamel camping mug, and its English product page lists the reviews customers have left. Most are in English; one, from a customer in Lyon, is in French: "<span lang="fr">Parfaite pour le café du matin au camping.</span>" A screen reader user reads the reviews to decide whether to buy. The English ones are fine. The French one comes out as "par-FATE poor lee kaff-AY doo MAT-in aw CAMP-ing", French words with English sounds, and they can't make it out, even though they read French. At the bottom of the page, the language switcher offers "<span lang="fr">Français</span>", "<span lang="de">Deutsch</span>" and "<span lang="es">Español</span>", each read with the same English voice.

## Why it fails

The page is marked `lang="en"`, which is right for the page, and every element inside it inherits that language unless it says otherwise. The review is rendered from the shop's reviews feed, which knows the review was written in French, but the template never writes that out, so the review inherits English. The switcher's links carry `hreflang="fr"`, which says what language the page they lead to is in, not what language their own text is in. Screen readers, read-aloud tools and braille translators take the language of each piece of text from `lang`, so they read all of it as English. WCAG 3.1.2 asks that the human language of each passage or phrase can be programmatically determined; here the French review and the three language names say they are English.

## Who is affected

The failure follows the content, not the template: an English page looks and sounds right until a customer writes in another language, which a team testing with its own reviews may never see. Browsers can use `lang` too, to choose fonts, hyphenation and spell checking, and to decide what to offer to translate, so a passage marked with the wrong language can also be drawn and hyphenated wrongly.

## What automation and AI miss

axe's `valid-lang` rule checks the `lang` values a page has: `lang="francais"` is reported, `lang="fr"` passes. A passage with no `lang` at all is invisible to it, which is the usual form of this failure. EaseWeb's audit reports a language switcher whose language names have no `lang` of their own, and sends text in another writing system for review, such as a Japanese sentence on an English page or an English one on a Japanese page. A French review on an English page uses the same letters as the text around it, and no check here finds it. An AI tool can often guess what language a passage is in, but a person has to decide whether a word is a name, a technical term or a word borrowed into the page's language, none of which needs marking.

## Before: a French review and a language switcher with no lang

```html
<html lang="en">
…
<h1>Enamel camping mug</h1>
…
<ul class="reviews">  <!-- rendered from the reviews feed -->
  <li>
    <p class="review-text">Light, tough, and keeps tea hot.</p>
    <p class="reviewer">Sam, Leeds</p>
  </li>
  <li>
    <p class="review-text">Parfaite pour le café du matin au camping.</p>
    <p class="reviewer">Camille, Lyon</p>
  </li>
</ul>
…
<footer>
  <nav aria-label="Language">
    <a href="/en/" aria-current="true">English</a>
    <a href="/fr/" hreflang="fr">Français</a>
    <a href="/de/" hreflang="de">Deutsch</a>
    <a href="/es/" hreflang="es">Español</a>
  </nav>
</footer>
```

The French review and the three language names inherit `lang="en"` from the page, so they are read with English pronunciation. `hreflang` describes where each link goes, and changes nothing about how its text is read.

## After: lang on each passage

```html
<html lang="en">
…
<h1>Enamel camping mug</h1>
…
<ul class="reviews">  <!-- each li takes the language stored with its review -->
  <li lang="en">
    <p class="review-text">Light, tough, and keeps tea hot.</p>
    <p class="reviewer">Sam, Leeds</p>
  </li>
  <li lang="fr">
    <p class="review-text">Parfaite pour le café du matin au camping.</p>
    <p class="reviewer">Camille, Lyon</p>
  </li>
</ul>
…
<footer>
  <nav aria-label="Language">
    <a href="/en/" aria-current="true">English</a>
    <a href="/fr/" hreflang="fr" lang="fr">Français</a>
    <a href="/de/" hreflang="de" lang="de">Deutsch</a>
    <a href="/es/" hreflang="es" lang="es">Español</a>
  </nav>
</footer>
```

Each review is wrapped in an element that carries the language stored with it, so the French one is read with French rules and the English text after it goes back to English. Every review gets its `lang`, the English ones too, so a review in English stays English on the French version of the page. Each language name carries the `lang` of the language it names, on the link itself, beside the `hreflang` that describes its target.

## Implementation decisions

Put `lang` on the element that holds the whole passage: the review's list item, a `blockquote`, a `p`. For a few words inside a sentence, wrap them in a `span lang="…"`. On a link or a button, put it on the element itself where you can: a control's name is worked out as plain text, and a `lang` on part of it can be lost on the way to assistive technology. A span with its own `lang` inside the link, as in W3C's own example, still meets 3.1.2. For the same reason, don't give a switcher link an `aria-label` such as "Switch to French": the name is then English, and a `lang="fr"` on the link would have it read with French rules. Let the visible language name be the name.

Content you don't write needs its language from somewhere. The ways to get it differ in how sure they are:

- The language the person used: a review posted from the French version of the site is most likely in French. Store that locale with the review when it is posted.
- Detection when the content arrives: a language detection library, or the browser's own detection where it has one, guesses well for a sentence or more and badly for two or three words. Store the result, and let a moderator correct it.
- A field on the form: ask the reviewer which language they wrote in, defaulting to the site's current language.

When the language can't be known, leave the passage without `lang` rather than guess, and treat that as a gap to close; a wrong `lang` fails just as a missing one does.

Some words in another language need no marking. WCAG 3.1.2 excludes proper names, technical terms, words of indeterminate language, and words that have become part of the surrounding language: "Camille" and "Lyon", "café" in an English sentence, a brand name. A test is whether the word would be said the same way by a speaker of the page's language. On a Japanese page, katakana loanwords are Japanese, but an English sentence such as a slogan or a review is a passage and needs `lang="en"`. Marking Japanese text correctly also matters for how it looks: browsers can choose the shapes of Chinese characters from `lang`, so Japanese text inside a passage marked as Chinese can be drawn with Chinese forms.

Showing a review translated into the page's language is a separate feature. The translation is in the page's language and needs no `lang`; the original, wherever it is shown, still does.

The language of the page itself is 3.1.1, covered in [page language set wrong, so screen readers mispronounce it](/learn/page-language). Use valid tags here as there: `fr`, `de`, `es`, `zh-Hant`, never `french` or `jp`.

## Verify the fix

1. Find every passage on the page that is not in the page's language, such as reviews, quotes and product names that aren't names, and check that the element holding each one has a `lang` for its language.
2. In the language switcher, check that each link has a `lang` matching the language its text is in, on the link or on the text inside it, and no `aria-label` in another language.
3. Post a review in another language through the real form, or import one, and check that the page renders it with the right `lang`; then check a review in the page's own language on another language version of the page.
4. With a screen reader, read the review and the switcher, and check that each is read in a voice for its language and that the text after them returns to the page's voice.
5. Run axe's `valid-lang` rule, or check each `lang` value against a list of valid tags.

Record the OS, browser and assistive-technology versions, the build, the date, and the actual result of each step. Repeat when content from a new source, or a new language, is added.

## Limits

This is a starting pattern, not a guarantee for every application. Screen readers switch voices only when a voice for that language is installed, and some let the user turn automatic language switching off; that is outside your control, but the markup must still be right. Some screen readers also try to guess the language from the text itself; a guess is least reliable for a short passage in the same letters as the page, and not every reader makes one, so it doesn't replace `lang`. Whether a word needs marking can be a judgement call: check the passages that matter with someone who reads that language if you can.

---

Get this guide in your coding agent: MCP https://easeweb.dev/mcp (setup at https://easeweb.dev/services/developers)
