html lang: valid values and what a missing one breaks
Without a lang attribute a screen reader guesses the pronunciation, often wrongly. squirrel checks the html element on every page.
What the lang attribute is
lang on <html> declares what language the document is written in, as a BCP 47 tag. A screen reader uses it to choose a voice and a set of pronunciation rules. Read English with a French synthesiser and most of it is unintelligible.
The tag has a primary subtag, en, optionally followed by script and region subtags, en-GB or zh-Hans-CN. The primary subtag is the part that matters for pronunciation; the region subtag usually selects an accent.
What squirrel checks
Two check names, at rule severity error. Only one runs per page.
html-has-langfails when<html>has nolangattribute, and also when it haslang="", since the rule tests the attribute for truthiness. Report message:HTML element missing lang attribute, with the expected valuelang="en" or appropriate language code. Nothing else is checked.html-lang-validfails when the value is whitespace only, or when its primary subtag is neither one of the 190 ISO 639-1 codes the rule knows nor a plain two or three letter sequence. Report message:Invalid lang attribute: empty valueorInvalid lang attribute: invalid primary language 'english', with the expected valueValid BCP 47 language tag (e.g., 'en', 'en-US', 'es').html-lang-validpasses withValid lang attribute: "en-GB".
Two limits follow from that shape check. Any two or three lowercase letters pass, so an invented code such as qq is accepted. And script and region subtags are not validated at all, so en-ZZ passes.
How to fix it
<html lang="en-GB">Set it on the <html> element of every page, including error pages and pages rendered by a framework’s fallback layout. A region subtag is worth adding when the accent matters, and harmless when it does not.
For a page whose main content is in another language, change this attribute rather than adding lang further down. Marking individual passages is what a11y/valid-lang covers.
| Rule ID | a11y/html-lang-valid |
| Category | Accessibility |
| Scope | Per-page |
| Severity | error |
| Weight | 7/10 |
Enable / disable
Disable this rule
[rules]
disable = ["a11y/html-lang-valid"]Disable all Accessibility rules
[rules]
disable = ["a11y/*"]Enable only this rule
[rules]
enable = ["a11y/html-lang-valid"]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/valid-lang:
langon every other element on the page. - a11y/html-xml-lang-mismatch:
langandxml:langdisagreeing. - i18n/lang-attribute: the same declaration seen from the internationalization side.
- i18n/hreflang: the language signals sent to search engines instead.
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
- Understanding SC 3.1.1 Language of Page, W3C
Check your site
Run squirrel audit https://example.com and open the Accessibility section of the report. A missing attribute and an invalid value are reported as different checks. Signed-out local audits need no account and cost nothing. Signed-in audits spend credits.