( Understandable / WCAG 3.1.1 )
HTML lang attribute is not a valid BCP 47 tag
What is this issue?
The <html> element has a lang attribute set, and the value doesn’t match a valid BCP 47 language tag, the internet standard defining codes like en, fr-CA, or zh-Hant. A typo ("eng"), a spelled-out language name ("english"), or a malformed region format ("en_US" with an underscore) all fail to resolve to any real language a browser or screen reader recognizes.
This page describes exactly the same defect as html-lang-valid, which is the page with RedFlag’s live axe-core detector behind it. This page exists separately in RedFlag’s rule catalogue as documentation, not as a second working check for the same condition.
Why does this matter?
A screen reader that encounters an unresolvable lang value treats the page the same way it would treat a page with no lang attribute at all, falling back to a default pronunciation engine, usually the user’s own system language setting, regardless of whether that matches the page’s actual language. The presence of something in the attribute doesn’t help if that something doesn’t parse.
The practical effect can mangle an entire page’s speech output, not just isolated words, since the wrong pronunciation engine applied across full sentences frequently produces speech that doesn’t resolve into recognizable words at all, a serious barrier for anyone relying on synthesized speech to use the page.
Who is affected?
- Screen reader users: get speech synthesized in whatever language their assistive technology falls back to by default, unrelated to the page’s actual content, because the malformed tag gives the pronunciation engine nothing valid to match against.
What users experience
Elena uses VoiceOver on macOS to fill out a nonprofit’s donation form. A locale-picker widget wrote <html lang="EN-USA"> to the page, a three-letter region code the widget generated instead of a valid two-letter one. VoiceOver can’t match "EN-USA" against any recognized BCP 47 tag, so it falls back to its own system language, set to German on her machine. The donation amounts and form instructions come through with German pronunciation rules applied to English words, and she abandons the donation rather than guess at numbers she can’t make out.
How do I fix it?
Replace the invalid value with a real BCP 47 language tag: for most sites, a two-letter ISO 639-1 code such as en, fr, or de, optionally with a region subtag like en-US. This is the identical fix documented on lang attribute has an invalid value: the correction resolves both that page’s live automated finding and this one’s underlying condition at once, since they describe the same defect.
If the invalid value was hardcoded by hand, generate it instead from a validated source, such as a locale configuration object or a CMS field constrained to a dropdown of real language codes, so a future typo can’t silently reintroduce the same problem.
Code Examples
<html lang="english"><html lang="en">"english" spells out the language name rather than using its BCP 47 code, so no browser or screen reader resolves it to a pronunciation engine. The corrected two-letter code is the exact format every tool expects.
Common Mistakes
Mistake: “This rule ID showed up in RedFlag’s documentation, so an automated scan must be checking for it.” Not every page in RedFlag’s rule catalogue has a live detector behind it; this is one of several documentation-only pages with no automated check wired to this specific rule ID. Check the detection category stated at the top of “How RedFlag Detects This” on any rule page before assuming a scan covers it.
Mistake: “Since this page and html-lang-valid describe the same defect, RedFlag must dedupe them like it does for the missing-lang twins.” Deduplication only happens between two live detectors both firing on the same condition, which is the case for html-has-lang and its custom-check twin redflag-lang-missing. This page has no detector to dedupe with html-lang-valid in the first place; there’s only ever one live finding to see, under html-lang-valid’s rule ID.
Mistake: “If RedFlag doesn’t flag it automatically, it’s probably a low-priority issue.” The absence of an automated detector for this specific rule ID says nothing about the severity of an invalid lang value itself: that same underlying defect is actively caught by html-lang-valid’s axe-core check and remains a serious barrier to correct pronunciation regardless of which rule ID happens to have live detection behind it.
How RedFlag Detects This
Guidance only: RedFlag documents this issue but does not currently flag it; verify manually. This specific rule ID does not appear anywhere in RedFlag’s rule catalogue, criteria mapping, or coverage matrix; it is not wired to any automated detector, manual-check workflow, or even the coverage matrix’s own bookkeeping for known documentation-only checks. The identical underlying defect (an invalid BCP 47 value on the <html> element’s lang attribute) is genuinely caught automatically, but under a different rule ID: html-lang-valid, RedFlag’s stock axe-core check for this exact condition.
False negative: every occurrence is effectively a false negative under this specific rule ID, since nothing is ever automatically flagged as redflag-lang-valid, but the same page will still surface as an html-lang-valid finding if that detector is active in the scan. False positive: not applicable, since this rule ID never raises an automated violation to begin with. Manual step: if you need automated confirmation of an invalid page-level lang value, check the html-lang-valid finding on the same scan rather than relying on this rule ID to fire.
Manual Testing
- Open the page in Chrome or Firefox and check the
<html>element’slangattribute value in developer tools. - Compare the value against the IANA Language Subtag Registry or a known list of BCP 47 codes to confirm it’s a real, recognized tag.
- Load the page with NVDA or VoiceOver running and listen to the pronunciation and accent used for the first paragraph of visible text.
- If the value doesn’t match any real language code, or the announced speech uses an unexpected accent or mispronounces common words, the check fails, regardless of which RedFlag rule ID, if any, reported it.
Related WCAG Success Criteria
3.1.1 Language of Page: The default human language of each web page must be programmatically determinable. An invalid lang value fails this criterion exactly as thoroughly as a missing one, since a code that doesn’t resolve to a real language can’t be programmatically determined any more reliably than no code at all.
Related Issues
lang attribute has an invalid value documents the identical defect this page describes and is the page backed by RedFlag’s actual live detector; treat that page, not this one, as the source of truth for what RedFlag will automatically flag.
lang attribute on an element has an invalid value applies the same validity check to elements other than <html>, catching an invalid code on one passage rather than the page-wide default this page covers.
Page language is not set covers the related but distinct condition of a completely missing lang attribute, rather than one present with an invalid value.
References
- W3C Understanding 3.1.1: Language of Page
- W3C Technique H57: Using the language attribute on the html element
- MDN: lang global attribute
- IANA Language Subtag Registry
Frequently asked questions
Is this page a duplicate of html-lang-valid?
They document the same underlying defect, an invalid BCP 47 value in the html element's lang attribute, but only html-lang-valid is wired to an actual automated detector, RedFlag's axe-core rule. This page exists in RedFlag's catalogue as documentation only, with no detector behind it, so it does not get deduplicated with html-lang-valid the way true detector twins do.
Why does RedFlag have a documentation page with no detector attached?
It reflects how the rule catalogue grew over time: some documentation pages were written before, or independently of, the specific automated check that ended up covering the same ground. RedFlag has not removed or merged this page into html-lang-valid, so it remains as guidance-only content today.
Should I rely on this page, or html-lang-valid, if I want to know whether RedFlag will actually flag an invalid lang value on my site?
Rely on html-lang-valid for that answer; it is the page backed by RedFlag's live axe-core detector. Read this page for the same underlying explanation and fix, but do not expect an invalid lang value to appear as an automated finding under this specific rule ID.
Does an invalid lang value ever get caught by two different RedFlag findings the way a missing lang value does?
No, not for invalid values. A missing lang attribute is genuinely caught by two live detectors (html-has-lang and its custom-check twin redflag-lang-missing) and deduplicated on the dashboard. An invalid lang value has only one live detector, html-lang-valid: this page is not a second detector, just documentation.