Translator

A translation in the chat with an editable source, language pickers, alternatives, transliteration and usage notes.

Pattern

≈ 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

Formality

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, alternatives and transliteration are 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: true when the language was inferred, so the source pane shows a "Detected" badge.
  • Set formality only for languages that distinguish it (Spanish, French, German, Japanese, Korean…), to the register used. The card then offers a Formal / Informal toggle.
  • Add a transliteration for non-Latin targets when the user wants to say it aloud (romaji, pinyin).
  • Keep notes to real usage differences, such as region or register, at most three. Use glossary for names and terms kept consistent (dish names, product terms).
  • On translate, translate the returned text from from to to (with the formality, when present) and re-render the card with the new props. On choose, use that phrasing from then on.
  • Lifecycle: a translation is a snapshot, so it needs none of expiresAt or fetchedAt. When the agent answers a translate request with a new card instead of re-rendering this one, mark the old call superseded by the new one.

Interaction

ControlDoesSends
Source textEditable, grows with its content. The character count appears near the 5,000 limit (screen readers always get it). Editing marks the translation Out of datenothing
Language pickersChange either language (about 20 common ones by default, shown with Intl.DisplayNames). Marks it out of datenothing
Swap languagesFlips the pair. When the translation is current, it becomes the source and the original becomes the translation, with no requestnothing
Formal / InformalChanges the register. Marks it out of datenothing
TranslateShown when out of date. Shows "Translating…" until the props change. Ctrl or ⌘ + Enter in the source does the sametranslate
An alternativePreviews that phrasing in place of the translation (Copy and Listen use it)nothing
Use this versionPicks the previewed alternativechoose
CopyCopies the shown translationnothing
ListenReads the translation aloud with speech synthesis in the target language. Hidden when the browser has no speech synthesis or no voice for the languagenothing

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 lang set to its language, so screen readers pronounce it correctly; the transliteration carries lang="ja-Latn" (and so on), and the source textarea carries the source language.
  • dir comes from Intl.Locale text info (rtl for Arabic or Hebrew), or auto where 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

app/api/chat/route.ts
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

PropTypeNotes
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
alternativesstring[]?Up to 3 other phrasings. Data-bound
formality"formal" | "informal" | nullFor languages that distinguish it
transliterationstring?Latin-script reading. Data-bound
notesstring[]?Up to 3 usage notes
glossary{ source, target }[]?Up to 12 terms kept consistent
languagesstring[]?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.

Source

On this page