valid-lang

Element lang attribute must be syntactically valid

automaticWCAG 3.1.2 (AA)confidence: highseverity: moderateunderstandable

Checks that any element (other than the root <html>) with a non-empty lang attribute uses a syntactically valid language tag.

Applies to. Applies to any element other than the root <html> with a non-empty lang attribute AND at least some non-whitespace "governed text" that actually inherits its language from that element, per ACT de46e4:

  • Descendant text/alt is governed by the nearest lang-carrying ancestor only, a nested descendant with its own non-empty lang re-scopes everything inside it, so that subtree no longer counts toward the outer element's applicability (it counts toward the nested element's own, if that one is also being evaluated).
  • A non-empty alt attribute on img/area/input[type=image] counts as governed text, the same as a text node.
  • Text (or alt) that CSS keeps out of the render tree (display:none, the hidden attribute, ...) does not count. An <area> is drawn by the image that uses its map, so its own display and its map's don't keep its alt out. aria-hidden and offscreen positioning do NOT exempt text, per ACT's own failed examples for both, only actual non-rendering does.

Expectation. The primary language subtag of the lang value (the part before the first hyphen) is a registered language subtag, as ACT de46e4 requires. WCAG 3.1.2 (Language of Parts) requires that when a passage's language differs from the page's default, it is identified programmatically. A tag whose primary subtag is unknown fails to identify a real language at all; a malformed later subtag (lang="en-US_x") still identifies English and passes here, since it is a markup validity error rather than a missing language.

What a finding reports.

  • reasonCode: DEFAULT_LANG_INVALID when no ancestor (through shadow hosts, <html> included) has a non-empty lang, so this one sets the language its text starts from, as <body lang> does on a page whose <html> has none; ELEMENT_LANG_INVALID when it changes an inherited language. Both fail 3.1.2, as ACT de46e4 does.
  • value: the lang attribute's value, with surrounding whitespace removed.

These data.details fields help to read and reproduce a finding. Apart from reasonCode and the few fields Output schema names as stable, they are not a stable contract: a field may be renamed or dropped in a minor release, so don’t build on them.

Examples

pass
<p lang="fr">Bonjour</p>

"fr" is a syntactically valid BCP 47 language tag.

fail
<p lang="xyz123!!">???</p>

The value isn't valid BCP 47 syntax.

View source