Here is a bug that sails straight through most localization tooling. A French-Canadian page shows a menu full of English labels, and the automated check reports zero findings.
The reason is that most tooling relies on language detection. Take each sentence, count the function words ("the", "of", "and"), and flag confident English on a French page. That works for sentences. It fails for exactly the strings where localization bugs live: nav items, menu entries, buttons. "Quick Add On" is two to four words with no function words in it. No detector on earth can tell you what language "Power Bowls" is in.
Donobu asks a better question: does this exact string also appear on the English rendering of the same page?
Compare against the base, not against a dictionary
Donobu's product context stores a rendering of every page per locale, keyed by page, device, and locale. The base locale is the reference corpus. When the untranslated-content check runs on a target locale, it lines up each visible text segment against the same page's base rendering. A segment that appears verbatim in both is a missed translation. No language detection involved.
This has two consequences worth spelling out:
- Short labels get caught. The comparison needs two matching strings, not a statistically confident sentence.
- It works for Japanese, Chinese, and Korean. Stopword detection has nothing to say about a CJK page. Verbatim comparison does not care: leftover English on a Japanese page matches the base rendering and gets flagged.
The check also catches leaked i18n keys (raw {{checkout.cta.title}} tokens showing on a rendered page), pages that are largely untranslated, and full untranslated sentences. And when it is unsure, it stays silent. A report full of maybes is a report nobody reads.
Your glossary is respected too. Brand names and terms you declare as do-not-translate never appear as findings, so the report matches your localization guidelines instead of arguing with them.
One app, every locale
Sites address locales in four different ways: a country domain, a subdomain, a path prefix, or no URL marker at all, just Accept-Language. Donobu models all four, so example.de, fr.example.com, and example.com/fr-ca resolve to the same app instead of becoming disconnected knowledge bases. The cross-locale page map comes first from your own site's hreflang tags, which Donobu harvests from every captured page. If your site declares locale variants you have not configured for testing, Donobu tells you that too.
Every locale, every page, on demand
A locale sweep revisits every page Donobu knows about, under each target locale's real browser settings (language, region, timezone), and captures how it rendered. The core checks run without a single AI call, so sweeps are fast, cheap, and give the same answer every time. When you connect a model, an optional AI review sorts borderline candidates into real misses and intentional English, such as brand names, part numbers, and customer reviews, and caches its verdicts so repeat sweeps cost nothing:
- untranslated-content, described above
- datetime-number-format: dates, numbers, and times that contradict the locale's conventions
- placeholder-concatenation: template seams showing through
- truncation-overflow: text clipped in the target locale but fine in the base
That last one is comparative by design. An element overflowing in German but not in English is a localization bug. An element overflowing in both is a product bug, and the localization check stays quiet about it rather than blaming the translator.
The format check knows each locale's date order, decimal and grouping separators, and clock convention, and it only flags what is provably wrong. 13/02/2026 on an American-English page gets flagged. 01/02/2026 never does, because no tool can know which reading you meant.
Test before the translations exist
Pseudo-locale is a diagnostic rendering, not a translation. Donobu rewrites every text string on the page: brackets around it, accented look-alike characters, and about 40% extra length to simulate the expansion German and Finnish cause. Truncation and overflow bugs surface before you have paid for a single translated string, and hard-coded text betrays itself by rendering without accents next to everything else.
Wire it into CI
Two commands make locale coverage part of the pipeline instead of an occasional event:
donobu localize https://app.example.com --locales=de-DE,fr-FR --fail-on=error
donobu localize sweeps, runs the checks, and prints a page-by-locale matrix:
PAGE LOCALE STATUS ERR WARN INFO
/ de-DE findings 1 3 0
/ fr-FR ok 0 0 0
/pricing de-DE no-route 0 0 0
/pricing fr-FR not-captured 0 0 0
The exit code is a real gate: it fails the build only on new or re-confirmed findings at your chosen severity, and findings a human already dismissed never fail the build again. The matrix also catches a quiet failure most teams never test for: you asked for Italian and the site served English. That shows up as its own status.
For your functional suite, there is donobu test --locale=de-DE: the same Playwright tests, run under a locale's browser emulation, with every artifact tagged by locale. CI fans out one job per locale.
Reading the screen, and where people still judge
Some text never reaches the DOM as text: banners baked into images, labels drawn on a canvas. For those, a screenshot review reads the page with OCR and checks what a visitor actually sees, including unit spacing, locale-specific punctuation, and capitalization. It can also flag copy that reads unnaturally, as a low-severity note with a suggested rewrite, and you can turn that off when you want hard errors only.
A note is not a verdict, though. Whether copy truly lands in a market is a question of taste, and there people do the judging; that split is the subject of The Tedious 90% of Localization QA.
Want to see what a scan finds on your site? Run the free localization check, or talk to us about running this across every locale you ship. More on the service model at Localization QA.
