Translator
A translation in the chat with an editable source, language pickers, alternatives, transliteration and usage notes.
≈ 1,100 tokens per use (tool 690 · guidance 283 · typical payload 127)How token costs are estimated
Example: Translator
Translate this email to Spanish
English to Spanish
Original
141 characters
Hola, Marta: Gracias por enviar las muestras. Nos gustaría pedir 200 unidades del modelo azul. ¿Podría confirmar la fecha de entrega? Saludos cordiales, Daniel
Notes · 1
“¿Podría…?” uses the formal “usted”.
When to use
- The user asks to translate text: an email, a menu, a sign, a phrase they want to say.
- The user may want to tweak the source, switch languages or compare phrasings.
When not to use
- The user asks what a single word means in their own language. Answer in text.
- The text is a whole document. Translate it as a file.
- The agent doesn't have a translation from its translation tool yet. Get it first; never make up the target text.
How the agent should use it
- The translation comes from the tool.
target.text,alternativesandtransliterationare data-bound: they come from the agent's translation tool, never from the browser. The component never translates anything itself. - Pass the source text exactly as given, and set
source.detected: truewhen the language was inferred, so the source pane shows a "Detected" badge. - Set
formalityonly for languages that distinguish it (Spanish, French, German, Japanese, Korean…), to the register used. The card then offers a Formal / Informal toggle. - Add a
transliterationfor non-Latin targets when the user wants to say it aloud (romaji, pinyin). - Keep
notesto real usage differences, such as region or register, at most three. Useglossaryfor names and terms kept consistent (dish names, product terms). - On
translate, translate the returnedtextfromfromtoto(with theformality, when present) and re-render the card with the new props. Onchoose, use that phrasing from then on. - Lifecycle: a translation is a snapshot, so it needs none of
expiresAtorfetchedAt. When the agent answers atranslaterequest with a new card instead of re-rendering this one, mark the old call superseded by the new one.
Interaction
| Control | Does | Sends |
|---|---|---|
| Source text | Editable, grows with its content. The character count appears near the 5,000 limit (screen readers always get it). Editing marks the translation Out of date | nothing |
| Language pickers | Change either language (about 20 common ones by default, shown with Intl.DisplayNames). Marks it out of date | nothing |
| Swap languages | Flips the pair. When the translation is current, it becomes the source and the original becomes the translation, with no request | nothing |
| Formal / Informal | Changes the register. Marks it out of date | nothing |
| Translate | Shown when out of date. Shows "Translating…" until the props change. Ctrl or ⌘ + Enter in the source does the same | translate |
| An alternative | Previews that phrasing in place of the translation (Copy and Listen use it) | nothing |
| Use this version | Picks the previewed alternative | choose |
| Copy | Copies the shown translation | nothing |
| Listen | Reads the translation aloud with speech synthesis in the target language. Hidden when the browser has no speech synthesis or no voice for the language | nothing |
The translator never locks: the user can keep editing and asking for new translations.
The language pickers lead the card (the pair is also its screen-reader heading). A short translation (80 characters or fewer) is shown large, since it's the answer itself. notes and glossary sit behind one collapsed "Notes" / "Glossary" disclosure, so the translation leads.
Reading order and pronunciation
- The translation carries
langset to its language, so screen readers pronounce it correctly; the transliteration carrieslang="ja-Latn"(and so on), and the source textarea carries the source language. dircomes fromIntl.Localetext info (rtlfor Arabic or Hebrew), orautowhere the browser can't tell.- Two panes sit side by side on wide cards and stack on narrow ones.
Installation
npx shadcn@latest add https://your-site.com/r/translator.json
Usage
import { tool } from "ai"
import { translatorTool } from "@/components/genui/translator.schema"
const tools = {
[translatorTool.name]: tool({
description: translatorTool.description,
inputSchema: translatorTool.inputSchema,
}),
}When the user edits and presses Translate, the agent receives:
{ action: "translate", text: "Hi Marta, …", from: "en", to: "es", formality: "informal" }Translate it and re-render the same card with the new source and target (the card shows "Translating…" until the props change), or answer with a new card. Picking an alternative sends:
{ action: "choose", text: "駅はどちらですか?" }With LangGraph
Push it from a graph node with ui.push({ name: "translate_text", props: { toolCallId, ...args } }, { message }), and render the thread's UI messages with the Renderer. To answer a translate result in place, push again with the same id and the new props.
Restoring from history
Pass the last stored result as result. A translate result restores the edited text and languages with "Asked for a new translation."; a choose result restores the chosen alternative. It is never sent again, and the card stays usable.
<Translator
{...part.input}
result={part.state === "output-available" ? part.output : undefined}
/>Helpers
translator-utils.ts holds the pure logic, usable on the server: languageName (with a fallback when Intl.DisplayNames is missing), languageOptions, textDirection, charCount (graphemes), deriveView (out-of-date and swap detection), swapDraft and pickVoice.
Schema
| Prop | Type | Notes |
|---|---|---|
source | { text, language, detected? } | Text up to 5,000 characters; language is a BCP 47 tag |
target | { language, text? } | text is the translation. Data-bound |
alternatives | string[]? | Up to 3 other phrasings. Data-bound |
formality | "formal" | "informal" | null | For languages that distinguish it |
transliteration | string? | Latin-script reading. Data-bound |
notes | string[]? | Up to 3 usage notes |
glossary | { source, target }[]? | Up to 12 terms kept consistent |
languages | string[]? | Pickable BCP 47 tags. Defaults to 22 common ones; the current pair is always included |
Host props: status (streaming, ready, translating), onResult, result and className.