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 issueBase languages must matchand the fixSet 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
enagainsten-gb. Report message:lang="en" and xml:lang="en-gb" differ slightly, notingBase languages match but subtags differ. - Warn when
xml:langis present andlangis not. Report message:xml:lang present without lang attribute, suggestingAdd lang attribute for HTML5 compatibility. - Pass with
lang and xml:lang attributes matchwhen 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. Whetherlangexists at all isa11y/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 ID | a11y/html-xml-lang-mismatch |
| Category | Accessibility |
| Scope | Per-page |
| Severity | error |
| Weight | 6/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.
Related rules
- a11y/html-lang-valid: whether
langexists and holds a usable value. - a11y/valid-lang:
langon elements below<html>. - i18n/lang-attribute: the same declaration checked for internationalization.
- core/doctype: the doctype that usually explains why
xml:langis there.
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
- The lang attribute, MDN
- Declaring language in HTML, W3C Internationalization
- Tags for identifying languages, RFC 5646
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.