Xzero UI
Pro library

AI translatePro

A message card where picking a language streams the AI translation in place, sentence by sentence over the dimmed original, with a detected-language chip, a Show original toggle and a status line credited to the model's mark.

$ pnpm dlx shadcn@4.23.0 add @xzero-ui-pro/ai-translate
Live · keyboard ready

When to use

When not to use

Installation

Register both Xzero UI namespaces in your project's components.json:

{
  "registries": {
    "@xzero-ui": "https://mcp.xzero.co.uk/r/{name}.json",
    "@xzero-ui-pro": "https://mcp.xzero.co.uk/r/pro/{name}.json"
  }
}
npx shadcn@4.23.0 add @xzero-ui-pro/ai-translate

Install xzero-foundation once, and import @/components/xzero/foundation.css in your app root. No Xzero token is used by this snapshot. For sample portraits, photos, fonts, or logos, download https://mcp.xzero.co.uk/install-assets.mjs and run node xzero-install-assets.mjs <receiving-project>.

Usage

import { AiTranslate } from "@/components/xzero/ai-translate/ai-translate";

export function TranslatableMessage({ message }) {
  return (
    <AiTranslate
      text={message.body}
      sourceLanguage={message.language}
      author={{ name: message.author.name, role: message.author.title, avatar: message.author.avatarUrl }}
      timestamp={message.sentAtLabel}
      model={{ name: "Claude Haiku 5.5" }}
      translate={async function* ({ text, from, to, signal }) {
        const response = await fetch("/api/translate", { method: "POST", body: JSON.stringify({ text, from, to }), signal });
        if (!response.ok || !response.body) throw new Error("The translation service is unavailable.");
        const reader = response.body.pipeThrough(new TextDecoderStream()).getReader();
        while (true) {
          const { value, done } = await reader.read();
          if (done) return;
          yield value;
        }
      }}
      onValueChange={language => savePreferredLanguage(language)}
    />
  );
}

// Pre-translated content, no network
<AiTranslate text={post.fr} sourceLanguage="fr" translations={{ en: post.en, de: post.de }} author={null} />

API reference

AiTranslate

An inline translator for one message or paragraph. A byline with a detected language chip sits above the text; below it, a Translate picker (English, Deutsch, Français, Español, 日本語, Português), a Show original toggle and a status line credited to the model's mark. Picking a language dims the text on screen and streams the translation in place in fast chunks, each new sentence retiring one sentence of the old text, while the card's height springs to fit. Finished translations are cached, so Show original and switching back crossfade instantly. Failures put the previous text back and show Couldn't translate with Retry.

PropTypeDefaultDescription
textstringsample release note in FrenchThe text to translate, in its original language. Changing it clears cached translations and shows the new original.
sourceLanguage"en" | "de" | "fr" | "es" | "ja" | "pt"–Language of text. When omitted it is detected from the text with a light stopword check and the chip reads Detected; when passed the chip reads Written in.
languagesAiTranslateLanguageCode[]all sixLanguages offered in the picker, in order. The source language appears as Original.
valueAiTranslateLanguageCode–The language on screen; the source language means the original. Pair with onValueChange to control it, for example to remember a reader's preferred language.
defaultValueAiTranslateLanguageCodesourceLanguageStarting language when uncontrolled. A language other than the source translates on mount.
onValueChange(language: AiTranslateLanguageCode) => void–Called when the reader picks a language or toggles Show original.
translate(request: { text, from, to, signal: AbortSignal }) => Promise<string> | AsyncIterable<string>–Your translation call. Resolve the whole translation, or return an async iterable that yields text deltas as your model streams them. The signal aborts when the reader picks another language, shows the original or the card unmounts. A thrown Error's message is announced and kept as the error tooltip. Without it, translations are streamed as a simulation.
translationsPartial<Record<AiTranslateLanguageCode, string>>–Ready translations, streamed by the simulation. Useful for content you translated ahead of time; a missing language shows the error state.
onTranslate(result: { from, to, text }) => void–Called once a translation has fully arrived, before it is cached.
onError(error: Error, request: { from, to }) => void–Called when a translation fails. Aborted requests never call it.
author{ name: string; role?: string; avatar?: string } | nullsample authorWho wrote the text, shown with an avatar. Pass null to hide the byline; the language chip stays.
timestampstring–Shown after the role in the byline, for example "Oct 27, 09:14".
model{ name: string; mark?: ReactNode }{ name: "Claude Haiku 5.5", mark: Claude mark }The model credited in the status line. The mark leads every status; the name is the idle label.
autoPlaybooleantrue for the bare sampleTours English, Deutsch, 日本語 and Português, then the original, and loops. Pauses off screen and stops for good on the first pointer or key press inside the card. With reduced motion it translates once and stops.
simulatedbooleantrue without translateAdds "Simulated, nothing was sent" to the live status.
classNamestring–Class on the root section.

Keyboard interactions

KeysAction
TabMoves through the Translate picker, the Show original toggle and, after a failure, Retry.
Enter / Space / Arrow downOn the picker, opens the language menu with the current language checked.
Arrow keys, Home, EndMove through the languages; typing a letter jumps to a language. The list loops.
Enter / SpacePicks the highlighted language and closes the menu. Picking the language that just failed retries it.
EscapeCloses the menu with focus back on the picker.

Accessibility

Motion

Responsive behavior

Performance

Notes for AI

Also in ai

Guidance for AI tools

AI translate: A message card where picking a language streams the AI translation in place, sentence by sentence over the dimmed original, with a detected-language chip, a Show original toggle and a status line credited to the model's mark. Follow the declared prop types and do not invent props. Keep keyboard access, reduced motion support, and both light and dark themes intact when adapting it.

Full library index: https://ui.xzero.co.uk/llms.txt

Browse all components