squirrelscan
GitHub

lang and xml:lang disagreeing on the html element

When both attributes are present and name different languages, a screen reader can pick the wrong voice. squirrel compares them.

What xml:lang is

xml:lang is the XML spelling of the language declaration. It matters in XHTML served as application/xhtml+xml, where it takes precedence over plain lang. In an ordinary HTML document it is redundant, and most pages that carry it inherited it from an old doctype.

Carrying both is allowed, but the specification requires them to agree. When they disagree, which one wins depends on how the document was parsed, so the announced language depends on the content type rather than on anything visible.

What squirrel checks

One check, html-xml-lang-mismatch, at severity error. It reads lang and xml:lang from <html>, trimmed and lowercased, and compares the primary subtag of each.

  • Fail when both are present and the primary subtags differ. Report message: lang="en" and xml:lang="fr" don't match, with the issue Base languages must match and the fix Set both to the same value or remove xml:lang.
  • Warn when both are present and the primary subtags match but the full values differ, such as en against en-gb. Report message: lang="en" and xml:lang="en-gb" differ slightly, noting Base languages match but subtags differ.
  • Warn when xml:lang is present and lang is not. Report message: xml:lang present without lang attribute, suggesting Add lang attribute for HTML5 compatibility.
  • Pass with lang and xml:lang attributes match when both values are non-empty and identical.
  • Info with Only lang attribute present (recommended for HTML5) in every other case. Every branch above tests the trimmed, lowercased value for truthiness, so an empty or whitespace-only attribute counts as absent and lands here. So does a page with neither attribute, where the message reads oddly. Whether lang exists at all is a11y/html-lang-valid.

Values are lowercased before comparison and before being quoted in the message, so a report shows en-gb where the markup said en-GB.

How to fix it

<html lang="en-GB">

For an HTML document, delete xml:lang. It does nothing, and deleting it removes the possibility of the two drifting apart.

For a document genuinely served as XML, set both to exactly the same string.

Rule IDa11y/html-xml-lang-mismatch
CategoryAccessibility
ScopePer-page
Severityerror
Weight6/10

Enable / disable

Disable this rule

[rules]
disable = ["a11y/html-xml-lang-mismatch"]

Disable all Accessibility rules

[rules]
disable = ["a11y/*"]

Enable only this rule

[rules]
enable = ["a11y/html-xml-lang-mismatch"]

enable replaces the default ["*"], so this is the only rule selected. Leave disable out: a rule that matches any disable pattern is skipped even when enable names it. Only an enabled setting under a rule’s own [rule_options] table overrides both lists.

Accessibility findings ship in every audit next to the SEO, performance and agent experience rules. See Website security scan with AI for how an agent works through a report.

References

Check your site

Run squirrel audit https://example.com and open the Accessibility section of the report. A different language fails, a different region subtag only warns. Signed-out local audits need no account and cost nothing. Signed-in audits spend credits.

Type to search…

↑↓ navigate↵ openesc close