Skip to main content
i18n / SEO Auditor — fetches the localized versions a page publishes (<link rel="alternate" hreflang>, or language-switcher links when there are none) and reports the ones that still show the audited page’s language, the ones that do not resolve, and the ones whose <html lang> contradicts the language promised for them.

Translation is not localization

The Liferay 2026 Website Localization Report found that 56% of multilingual consumers walked away from a purchase because a site felt poorly adapted, and 22% had met buttons, menus or messages left in English.

Fallback hides in plain sight

A CMS that cannot find a translation shows the default language instead, with HTTP 200 and the right lang attribute. Nothing breaks, so nobody notices until a customer does.
Localization is standards-based, so the auditor is generic: it reads hreflang alternates, <html lang> and switcher links, and compares text. It needs no platform in the software stack and runs from Quick Audit.

How it works

  1. Fetch the audited page with 37AuditsBot’s user agent, Web Bot Auth signature and the URL’s Basic Auth. HTTP 403, HTTP 429 or a Cloudflare challenge is ERROR (590); any other failure is ERROR (500).
  2. Discover the localized versions — <link rel="alternate" hreflang="…" href="…"> first; when the page publishes none, <a hreflang href> then <a lang href> switcher links. The page itself, x-default and versions in the page’s own language (en-GB for an en-US page) are dropped, and at most maxAlternates (default 10) are kept in document order. No version left is INFORMATION (100): the run is not charged.
  3. Fetch every version in parallel (five at a time, 40 s budget). A version that answers 403, 429, a Cloudflare challenge or a bot wall is WARNING (302); any other non-2xx answer or network error is FAIL (401).
  4. Compare the text — the body text nodes of each page outside script, style, noscript, template and svg, whitespace-collapsed, kept when they have at least 3 words containing a letter (or 6 letters in a script without word spaces, such as Japanese). A version’s block counts as untranslated when it appears verbatim on the audited page, or when it has at least 8 words and a two-language detector (Lingua, loaded for the audited page’s language and the version’s only) attributes it to the audited page’s language by a clear margin. Detection is skipped, and the reason recorded in data.languageDetection, when either language has no model, the audited page declares no language, or the two pages share a language. Text the markup hides at load time is left out of the count first, unless countHiddenText is on: an ancestor with the hidden or inert attribute or aria-hidden="true", an inline style with display:none, visibility:hidden or opacity:0, an <option>, a closed <dialog> or <details>, or a class such as hidden, hide, d-none, invisible or anything ending in -hidden (closed mega-menus, inactive tab panels, validation messages waiting for an error). Untranslated blocks found there are listed on the version’s summary check in data.hiddenBlocks with the signal that hid them, so the percentage describes what a visitor sees while nothing is lost. Visually hidden text (sr-only, visually-hidden, screen-reader-text) stays counted: screen readers read it, so an untranslated skip link is a real defect. Text hidden by a stylesheet rule alone cannot be told apart from the markup and stays counted. Two further kinds of block are then left out of the count and recorded apart on the version’s summary check: blocks that match the ignored terms (data.ignoredBlocks: the ignoredTerms preference plus the site’s own name from its host, so “Liferay OEM Partner” or “Liferay vs. Adobe” with both names listed) and blocks that look like a name (data.nameLikeBlocks: at most five words besides connectors such as “vs.” or ”&”, every one capitalised or an acronym, none a function or action word such as “a”, “to”, “you” or “apply”, and no sentence punctuation at the end). “Digital Asset Management”, “Gartner Peer Insights™”, “IM Project Manager” and “Diamond Bar, CA 91765” are names; “Create An Account”, “Request a demo”, “Skip to Main Content” and “All Rights Reserved.” are copy and stay counted.
  5. Judge each version — untranslated / blocks at or above untranslatedFailPercent (default 50) is FAIL (400); at or above untranslatedWarningPercent (default 5) is WARNING (300). Every version also gets one check per distinct untranslated block, in document order and capped at 50 per version, so the list of strings to translate is always in the result: WARNING (303) when the version reached the warning threshold, INFORMATION (101) when it stays under it. A version whose <html lang> primary subtag differs from the hreflang that points at it is FAIL (402); a page without <html lang> is WARNING (301).
  6. Aggregate — ERROR, then FAIL (400, 401, 402), then WARNING, else SUCCESS (200).
Up to five untranslated blocks are kept per version in data.samples, sentence-like copy first (ending punctuation, or mostly lower-case words) and Title Case labels last, so “Please enter a valid email address.” is quoted before a product name.

What it audits

Every row maps to one checkRule* method and one CheckCode. CheckCode suffixes use status ranges: 100 information, 200 success, 300–399 warning, 400–499 fail, 500–599 error. Example messages, measured on https://www.liferay.com/ and https://liferay.dev/ on 2026-10-18: The result message (also the message of the 200 check) is a summary: the audited URL and its default language, how many localized versions were found, and the verdict in figures for the versions concerned. Check messages carry the detail.
  • result, pass — "https://www.liferay.com/" has its default language set to en-US. 6 localized versions (es-ES, it-IT, fr-FR, pt-BR, de-DE, ja-JP) were found; all of them keep untranslated text blocks under 5% (highest: de-DE at 4%).
  • result, untranslated — "https://petrobras.com.br/" has its default language set to pt-BR. 1 localized version (en-US) was found; it shows untranslated text blocks at 36% (48 out of 135).
  • 300 — "https://www.liferay.com/de/" (de-DE) still shows 36 of its 321 text blocks (11%) in en-US: 35 copied verbatim from "https://www.liferay.com/" and 1 recognised by language detection, e.g. "Request a demo", "by 233 verified users as of September 2026, Source:", "Please enter a valid email address.".
  • 400 — "https://dev.liferay.com/es/" (es-ES) is mostly the en-US page: 37 of its 41 text blocks (90%) are still in en-US, 37 copied verbatim from "https://liferay.dev/" and 0 recognised by language detection, e.g. "Explore the Liferay Community Universe.", "The Liferay Community has been integral to our journey.", "Open Source. For Life.".
  • 303 — "Please enter a valid email address." is still in en-US on "https://www.liferay.com/de/" (de-DE): it appears verbatim on "https://www.liferay.com/".
  • 101 — "Create An Account" is still in en-US on "https://www.liferay.com/es/" (es-ES): it appears verbatim on "https://www.liferay.com/". The version stays under the 5% warning threshold.
  • 100 — "https://www.37audits.com/" declares lang="en" and publishes no hreflang alternates or language-switcher links, so there is no localized version to compare.
Findings on a version carry variantUrl, hreflang, source (hreflang or switcher), statusCode, error, blocked, declaredLang, baseUrl, baseLang, totalBlocks, identicalCount, detectedCount, untranslatedCount, distinctUntranslated, percent, warningPercent, failPercent, samples, nameLikeCount, nameLikeBlocks, ignoredCount, ignoredBlocks, hiddenCount, hiddenBlocks (text → hiding signal, such as hidden, aria-hidden, style, <details> or class:atb-tab-panel-hidden) and languageDetection in data. A 303 or 101 finding carries variantUrl, hreflang, baseUrl, baseLang, text, reason (copied or detected), occurrences, xpath (the block’s first occurrence on the version, browser style: /html/body/div[2]/footer/span[1], with /text()[n] when the element mixes several text nodes), listed and distinctUntranslated.

Preferences

Set per monitored URL; the defaults apply to Quick Audit.

Output documentation

Risks and considerations

What untranslated text costs

  • A visitor who switched to their language and meets English buttons, validation messages or a whole English page reads it as a signal about the brand, not a technical glitch. The Liferay 2026 Website Localization Report puts the cost at lost purchases for 56% of multilingual consumers and lost brand trust for 32%.
  • Search engines index each hreflang version for its audience. A Spanish URL full of English text ranks for neither language well.

Names are set aside, not everything is a name

  • A Title Case block without a function or action word is treated as a name and left out of the count, so a forgotten button written that way (“Download Brochure”) is missed; one with such a word (“Create An Account”, “Apply Thank You”) is counted. Review data.nameLikeBlocks on the summary check when a locale passes unexpectedly, and add genuine names to ignoredTerms when they slip through (a job title containing “and” or “for”, a tagline).
  • On liferay.com the heuristic removes 29 of the 51 distinct texts reported on 2026-10-18, leaving the UI strings, the footer and the taglines, and every locale drops from 7–11% to about 3%.
  • Language detection is deliberately conservative: it runs only on blocks of eight words or more and needs a clear margin, because short marketing copy mixing brand names (“Explore o Headless CMS”) is otherwise attributed to English.

What this auditor does not judge

  • Tone, idiom, cultural fit, machine-translation quality, and date, currency or address formats: the article’s remaining findings need a fluent reader.
  • Only the audited URL and its own localized versions are compared; other pages of the site may differ.
  • Versions in the audited page’s own language (en-GB for an en-US page) are skipped, since their text is expected to match.
  • The language detector covers 75 languages; a version in a language without a model is scored by the verbatim comparison alone, which data.languageDetection says.

Cost and load

  • One audit fetches the page plus up to maxAlternates versions, five at a time, so cost is 2. Lower maxAlternates on sites with many locales if the load matters.