Direct answer: Separate several language pages with a line containing --- and put a URL on the first line of each block. The tool parses each page's hreflang, canonical and html lang locally, then checks reciprocity, x-default, absolute URLs, self-reference and lang consistency. It does not crawl, and it does not judge translation quality.
hreflang is the easiest declaration to half-finish. In our own scan only 1 of 25 analyzable homepages used hreflang, and in acceptance work the recurring failures are structural: page A points to B while B never points back, or an English hreflang is claimed for what is really a machine placeholder. The tool handles the first; the second stays a manual judgement.
Reciprocity, x-default, absolute URLs, self-reference, lang match.
Whether the translation is real or the page resolves, the tool does not know.
It never crawls: it checks only the pages you paste.
The tool: paste pages → check
| Page | lang | canonical | hreflang | Self | x-default |
|---|
Pages you do not paste are reported as "cannot check reciprocity"; relative URLs, missing lang, missing self-reference and broken reciprocity are separate verdicts.
The default sample is a complete pair: both pages declare each other, both carry x-default, and lang matches the self-referencing hreflang, so there are no errors. "Load broken-reciprocity sample" removes the English page's link back to the Chinese page, and the tool immediately reports that the page does not point back.
Rules
| Check | Level | Meaning |
|---|---|---|
| Relative hreflang URL | Error | Declarations must be absolute URLs |
| A points to B, B does not point back | Error | Broken reciprocity; one-way declarations are usually ignored |
| html lang vs self hreflang primary language | Error | e.g. lang="en" with self hreflang="zh-CN" |
| No html lang / no hreflang at all | Error | Base declaration missing |
| Missing x-default | Warning | No fallback page named |
| No self-referencing hreflang | Warning | The page should be part of its own language set |
| Duplicate language declaration | Warning | Same hreflang twice |
| x-default differs across pages | Warning | Inconsistent fallback |
| Missing canonical | Warning | The input URL is used instead |
Regional variants (zh-CN vs zh-TW) are out of scope in this version; only the primary language (zh / en) is compared.
Five common failures
- Declared on one side only. The Chinese page points to English and the English page never points back; the tool reports broken reciprocity.
- Relative URLs.
hreflang="en" href="/en/"is ignored by some implementations; use the full URL. - No x-default. When no language matches there is no fallback, so the visitor can land on the wrong language.
- lang and hreflang disagree. A page declares
lang="zh-CN"but its self hreflang saysen; the signals contradict. - Declaring pages that do not exist. The tool only says "cannot check" when a page is not pasted; existence still needs a fetch.
Boundaries
- No crawling: only pasted HTML is parsed; unpasted language pages cannot be checked.
- No translation judgement: placeholders and non-equivalent content need manual or sampled review.
- No reachability: no requests, so 404s, redirects and login walls are invisible.
- No guarantee: a correct declaration structure does not mean a search engine will act on it.
Tests and recalculation
The logic lives in js/hreflang-check.js (no dependencies, no network); scripts/test_hreflang_check.js covers 28 assertions: URL normalisation, page parsing (attribute order), broken reciprocity, relative URLs, duplicate declarations, missing and inconsistent x-default, lang mismatch, single page, empty input and counts. Run:
node scripts/test_hreflang_check.js
Related: the 12 acceptance clauses (item 6 is hreflang), the JSON-LD checker and the 30-site scan (hreflang 1/25).
Sources and definitions
- Google Search Central, "Localized versions of your page": developers.google.com.
- HTML language declaration: HTML Standard.
- hreflang values: IETF language tags (the tool compares primary languages only).