@web-ai-sdk/translator
Building block for the Web's Built-in Translator API (on-demand language packs). Block-level translation with inline placeholder serialization, casing restoration, and a snapshot-based restore.
Docs: https://web-ai-sdk.dev/docs/guides/translator/ · React: useTranslator
Status
Translator API is stable in Chrome 138+ on desktop. On Edge it is a developer preview starting at Canary/Dev 143+ behind edge://flags/#edge-translation-api (per the Edge Translator API docs) — not yet in Edge stable. On any other browser this library is a no-op. Your app stays callable, and the controller just resolves with blocksTranslated: 0.
Install
pnpm add @web-ai-sdk/translator
The React adapter ships as a subpath export, with no extra install. react is a peer dependency only when you import the /react entry.
How it works
- Find roots. Default selector is
[data-translate-root]. The page declares which subtrees are translatable. Override with roots (selector / Element / array / function).
- Find blocks inside each root. Paragraphs, headings, list items, blockquotes, and anything else that doesn't itself contain another block. Override
blockSelector.
- Serialize each block to a single string with numbered placeholders for inline children:
<x0>StateX</x0> is a <x1/> (paired for <strong> / <a>, self-closing for opaque inline elements like <code> / <kbd> / <br>).
- Translate the string through one cached
Translator.create() session per language pair.
- Rebuild the block by walking the translation and reattaching the original elements (cloned) around the translated text. Never parses model output as HTML; surprise markup ends up as text. Casing is restored across blocks via a global
lowercased → original map (PWA → pwa → PWA, StateX → STATEX → StateX).
Vanilla TypeScript / DOM
import { translate } from "@web-ai-sdk/translator";
const controller = translate({
sourceLanguage: "pt",
targetLanguage: "en",
onProgress: (event) => console.log(event),
});
const { blocksTranslated } = await controller.done;
controller.restore();
translate() returns synchronously with a controller; controller.done resolves when every block has been processed.
React
import { useTranslator } from "@web-ai-sdk/translator/react";
export function ReadInEnglish({ sourceLanguage }: { sourceLanguage: string }) {
const { state, progress, translate, restore } = useTranslator({
sourceLanguage,
targetLanguage: "en",
});
if (state === "unavailable") return null;
if (state === "translated") {
return <button type="button" onClick={restore}>Show original</button>;
}
return (
<button
type="button"
onClick={translate}
disabled={state === "working"}
>
{state === "working" ? formatProgress(progress) : "Read in English"}
</button>
);
}
State machine: unavailable | idle | working | translated. restore() flips back to idle. progress exposes loading-model / downloading-model / translating / block-translated / done events for richer UI.
API
translate(options): TranslateController
Start a translation run.
interface TranslateOptions {
sourceLanguage: string;
targetLanguage?: string;
roots?: string | Element | readonly Element[] | (() => Iterable<Element>);
blockSelector?: string;
opaqueInlineTags?: readonly string[];
onProgress?: (event: TranslateProgress) => void;
document?: Document;
}
interface TranslateController {
done: Promise<{ blocksTranslated: number }>;
cancel(): void;
restore(): void;
isTranslated(): boolean;
}
isTranslatorAvailable(): boolean
Feature-detect helper.
checkAvailability({ sourceLanguage, targetLanguage }): Promise<TranslatorAvailability | null>
Forwards to the spec's availability() call. Returns null if the global is missing or the call throws.
Lower-level helpers (advanced)
serializeBlock, rebuildBlock, buildCasingMap, restoreOriginalCasing, isUntranslatableToken, stripTokens, getTranslatorApi. Exported so you can compose the pieces (e.g. translate one block at a time, or apply only the casing restoration to a string).
Markup contract
Mark translatable subtrees with data-translate-root (or pass your own roots option):
<article data-translate-root>
<h1>...</h1>
<p>...</p>
</article>
The library walks each root and translates every leaf block inside it. Blocks inside <pre> are skipped, and inline <code> / <kbd> / <img> / <br> survive untouched.
License
MIT © Beto Muniz