@web-ai-sdk/translator
v0.4.0
Published
Building block for the Web's Built-in Translator API
Maintainers
Readme
@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
# or: npm i @web-ai-sdk/translator / bun add @web-ai-sdk/translatorThe 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 withroots(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 → originalmap (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;
// later, e.g. on "Show original" click
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; // default "en"
roots?: string | Element | readonly Element[] | (() => Iterable<Element>);
blockSelector?: string; // default block elements
opaqueInlineTags?: readonly string[]; // CODE, KBD, ... by default
onProgress?: (event: TranslateProgress) => void;
document?: Document; // default globalThis.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
