Skip to content
June 17, 20269 min readGuides

The browser can translate your app now. Here's what it can't do.

In June 2026 the "do I still need a translation tool?" question got a new edge. On June 2, Microsoft announced that Microsoft Edge 148 ships a built-in Translator API backed by on-device models covering more than 145 languages, free and private. Google's Chrome has had the same API stable since Chrome 138 (June 2025), today covering 37 languages and language variants. Two of the most-used browsers on the planet can now translate any page, locally, for nothing.

So it is worth asking honestly: if the browser translates your app for free, do you still need a translation management system?

The short answer: the browser translates, the TMS governs. They are not competing for the same job. This post is the honest version of why, with a working recipe that puts the browser to work as a sanctioned fallback while your translations stay governed.

Key facts
  • Chrome: Translator API and Language Detector API stable since Chrome 138 (June 2025), 37 languages and variants, desktop only
  • Edge: Translator API available from Edge 148 (announced June 2, 2026), on-device, 145+ languages
  • How it works: a task-specific model is downloaded on first use, then runs locally. Free, offline-capable, nothing leaves the device
  • Not a standard yet: only the two Chromium browsers ship it. Mozilla filed a negative standards position; WebKit has taken none
  • What it is: a display-time enhancement for rendered text. What it is not: keys, review state, terminology, quality scoring, SSR, SEO, or mobile coverage

What the browser actually does, and does well

Credit where it is due. The built-in Translator API is a genuinely nice piece of platform engineering, and the demo is short. Feature-detect it, check that the language pair is available, create a translator, translate:

// Progressive enhancement: only runs where the API exists.
if ('Translator' in self) {
  const pair = { sourceLanguage: 'en', targetLanguage: 'de' }

  const availability = await Translator.availability(pair)
  // 'unavailable' | 'downloadable' | 'downloading' | 'available'

  if (availability !== 'unavailable') {
    const translator = await Translator.create({
      ...pair,
      monitor (m) {
        m.addEventListener('downloadprogress', (e) => {
          console.log(`Model download: ${Math.round(e.loaded * 100)}%`)
        })
      }
    })

    const out = await translator.translate('Where is the nearest bus stop?')
    console.log(out) // "Wo ist die nächste Bushaltestelle?"

    translator.destroy() // free the model when you are done
  }
}

That is the whole thing. The model for a language pair downloads once, then stays available across sites, and the API works the same way in Edge (documented here), including a translateStreaming() variant for token-by-token output. What you get is real:

  • Instant. After the one-time model download, translation happens locally with no network round-trip.
  • Free. No API keys, no per-character billing, no quota.
  • Private. The text never leaves the device and is not collected to train models.
  • Offline. Once the model is downloaded, it works with no connection.

For "a visitor landed on a page in a language you never shipped and wants to read it anyway," this is excellent. It is the modern, on-device version of the translate bar, available to your code. If that is your need, use it and stop reading.

But that is a different need from running a localized product. Here is where the line is.


The governance gap

A translation management system is not in the business of turning one string into another. It is in the business of making translation a controlled, reviewable, deliverable process across a team and over time. The browser API does none of that, by design, and it is worth being concrete rather than dismissive about exactly what is missing.

No keys, no structure. The API translates a string you hand it. It has no idea that this string is checkout.pay_now, that it lives in the checkout namespace, that it has a plural sibling, or that it already has an approved French translation you spent money on. It translates text, not your content model. Everything downstream of "we have a structured catalog of keys" simply does not exist here.

No review state, no approval evidence. There is no record that a human looked at the German translation of your refund policy and signed off on it. That record is increasingly not optional: under the EU AI Act, the path that keeps machine-translated public-interest content out of the visible-disclosure obligation runs through documented human review, and a translation produced fresh in the visitor's browser has, by definition, no such review attached (we wrote up that obligation and the review-workflow exemption separately). Compliance review cannot happen client-side after the fact.

No terminology or brand-voice control. Your product calls it a "workspace," not an "office," and your tone is informal "du" in German, not formal "Sie." The browser model does not know your glossary or style guide, and there is no way to tell it: the Translator API is a sealed, task-specific translation model, not an instructable LLM, so it has no prompt or system-instruction channel to inject terminology, tone, or translation memory through. Every visitor can get a slightly different, off-brand rendering of the same button.

No quality estimation. When the model gets a translation wrong, nothing flags it. There is no confidence score, no routing of the doubtful cases to a human. Quality Estimation and a review workflow are exactly the safety net that on-device translation removes.

No format fidelity. Plurals, gendered forms, interpolation ({{count}} items, {{name}}), nested components inside a sentence: these are i18n problems, not sentence-translation problems. Hand a model a string with a placeholder in it and you are gambling on whether the placeholder survives in the right place. ICU plural rules are not something a display-time translate call reasons about.

No SSR, no SEO. This is the big one for a public site. The browser translates the DOM after it renders, in the visitor's browser. Search engine crawlers, link previews, and your own server-side rendering see the original language only. A page translated by the browser is, to Google, an untranslated page. You cannot rank in a market whose language only ever exists client-side.

Mobile coverage gaps. Chrome's Translator and Language Detector APIs are documented as desktop only; they do not work on mobile devices. A large share of your users are on phones, where this entire mechanism is currently absent.

Not a settled standard. Only the two Chromium browsers ship it. Mozilla has registered a negative position on the Web Translation API and is pursuing a counterproposal, and WebKit has not taken a position at all. The API is incubating in the W3C Web Machine Learning Working Group, which means its shape can still change. Building a core experience on it today is building on one engine family's preview, not on the web platform.

None of this makes the browser API bad. It makes it a display technology. The mistake would be to treat a display technology as a content-management technology.


The architecture: the browser translates, the TMS governs

Put the two where they belong and they stop competing:

  • The governed core. Your real translations live in a TMS: structured by key and namespace, translated by AI and machine translation with your glossary and style guide as context, scored by quality estimation, reviewed by humans where it matters, versioned, and delivered to every browser and crawler and phone via CDN. This is the content you stand behind, the content that ranks, the content an auditor can trace.

  • The sanctioned fallback. For the gaps, the languages you have not shipped yet, the brand-new key that went live an hour ago, the long-tail locale you will never staff, let the browser fill in. Instantly, for free, for the visitors whose browser can. It is a graceful degradation, not the product.

The trick is to wire them together so the fallback is temporary and self-healing: every time the browser has to translate something, that fact becomes a signal that flows back into the governed core and gets it properly translated for next time.


The recipe: i18next missing-key fallback, self-healing into Locize

Here is that loop, concretely, with i18next and Locize. The idea:

  1. A key has no translation in the active language. i18next renders the source-language text (its normal fallback) and fires saveMissing.
  2. saveMissing reports the key to Locize, where automatic translation and quality estimation get to work and a human can review.
  3. Meanwhile, for visitors on a supporting browser, the on-device Translator API translates the fallback text right there, so they see their language immediately instead of English.
  4. On the next publish, the proper, reviewed translation is in Locize and on the CDN. The key now exists in the target language, the fallback no longer triggers, and the governed translation has quietly replaced the on-device guess.

First, the i18next side. The Locize backend reports missing keys for you:

import i18next from 'i18next'
import { initReactI18next } from 'react-i18next'
import Backend from 'i18next-locize-backend'

i18next
  .use(Backend)
  .use(initReactI18next)
  .init({
    fallbackLng: 'en',
    saveMissing: true, // report keys missing in the active language to Locize
    backend: {
      projectId: '<your-project-id>',
      version: 'latest',
      // A write API key is only needed to REPORT missing keys. Never ship one
      // to end users. See the caveats below: do the reporting in dev/staging
      // or behind your own endpoint, not in the production client bundle.
      apiKey: import.meta.env.DEV ? '<dev-only-api-key>' : undefined
    }
  })

Now the display-time fallback. A small hook reads a key; if i18next had to fall back to another language, it asks the browser to translate the fallback text on-device, and swaps it in when ready:

import { useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'

// Fill a missing translation on-device, for free, while Locize gets the key
// translated for real. Pure progressive enhancement: where the Translator API
// is absent (mobile, Firefox, Safari), this simply returns the fallback text.
export function useGovernedT (key) {
  const { t, i18n } = useTranslation()

  // returnDetails tells us which language i18next actually used.
  const details = t(key, { returnDetails: true })
  const target = i18n.language
  const source = details.usedLng        // the language it fell back to
  const fallbackText = details.res      // text in that fallback language
  const missingInTarget = !!source && source !== target

  const [onDevice, setOnDevice] = useState(null)

  useEffect(() => {
    setOnDevice(null)
    if (!missingInTarget) return
    if (typeof self === 'undefined' || !('Translator' in self)) return

    let cancelled = false
    ;(async () => {
      const pair = { sourceLanguage: source, targetLanguage: target }
      const availability = await Translator.availability(pair)
      if (availability === 'unavailable') return

      const translator = await Translator.create(pair)
      const translated = await translator.translate(fallbackText)
      translator.destroy()

      if (!cancelled) setOnDevice(translated)
    })().catch(() => { /* stay on the fallback text on any error */ })

    return () => { cancelled = true }
  }, [key, source, target, fallbackText, missingInTarget])

  // Render the on-device translation once it is ready, the fallback text until then.
  return missingInTarget && onDevice ? onDevice : fallbackText
}

And in a component, it reads like any other translation:

function CheckoutButton () {
  const label = useGovernedT('checkout.pay_now')
  return <button className='btn-primary'>{label}</button>
}

A brand-new checkout.pay_now that only exists in English will: render "Pay now" instantly for everyone, show "Jetzt bezahlen" a moment later for a visitor on desktop Chrome or Edge, and report itself to Locize so that within one publish cycle every visitor, on every browser and every phone and every crawler, gets a reviewed, quality-scored German translation from the CDN. The fallback heals itself.

Honest caveats

  • Desktop Chrome 138+ and Edge 148+ only. Everyone else (Firefox, Safari, every phone) gets the fallback text, which is fine, that is the whole point of progressive enhancement. Do not let anything important depend on the on-device path firing.
  • Keep the write key out of the client. Reporting missing keys needs a write-scoped credential. Run saveMissing in development or staging, or proxy it through your own endpoint, rather than shipping an API key in a production bundle. The on-device translation itself needs no key and is safe to ship.
  • It is a stopgap, not the translation. The on-device output is unreviewed, unscored, and off-glossary. It buys you a better first impression while the governed translation is produced. Never treat it as final.
  • First use downloads a model. The very first translation for a language pair triggers a one-time model download, so the first visitor in a session may see the fallback text for a beat longer. The monitor callback lets you surface progress if you want.

One forward-looking note, and only a note: this same missing-key-to-fallback wiring is the natural seam where on-demand translation features tend to grow, so it is a useful pattern to have in place regardless of where the browser APIs land. No promises, just a good place to stand.


Where Locize fits

The browser is a fine translator. It is not a place to manage translations, and it was never trying to be. Locize is the governed core the fallback heals back into: structured keys and namespaces, automatic AI translation with your glossary and style guide, Quality Estimation on every AI translation, a review workflow and full history for the translations you stand behind, and CDN delivery so the result reaches every browser, every phone, and every crawler, not just the visitors who happen to be on desktop Chromium.

Use the browser for what it is genuinely good at, instant, free, private, display-time translation, and keep the governance where it belongs.

If you want the governed core under that fallback: create a free Locize account, turn on automatic translation and Quality Estimation, and wire saveMissing to report your missing keys. The browser translates. Your TMS governs.

Tired of managing translations by hand?

Locize is the translation management backend by the i18next team: CDN delivery, AI translation, in-context editing, no redeploys.

Start your free 14-day trial