Building a Multi-Language Translation App with Claude API
What you need to build a translation app on Claude
If you're searching for how to build a multi-language translation app with the Claude API, the short answer is: you don't need a specialized translation model. Claude handles translation as a general-purpose language task, and with the right prompt structure it outperforms rule-based MT systems on idiom, tone, and context preservation — especially for product copy, support tickets, and conversational UI text.
The harder part isn't the translation quality itself. It's the engineering around it: handling dozens of language pairs from one codebase, streaming long documents without blocking the UI, preserving formatting (HTML, Markdown, placeholders), keeping glossaries consistent across requests, and doing all of this at a cost and latency that scales with real traffic. This article walks through that architecture.
Why Claude is a good fit for translation workloads
Traditional MT engines translate sentence by sentence and lose context across paragraphs. Claude reads the full input, understands register (formal vs. casual), and can follow instructions like "keep variable names like {username} untouched" or "translate to Brazilian Portuguese, not European Portuguese." This matters a lot for SaaS UI strings, legal disclaimers, and support replies where literal word-for-word translation produces awkward or wrong output.
Claude also handles many source and target languages in a single system prompt, so you don't need separate models or fine-tuned checkpoints per language pair — one prompt template covers dozens of languages.
Core architecture
A typical multi-language translation app has three layers:
- Input normalization — strip or tag HTML/Markdown, extract placeholders (
{{name}},%s, ICU plurals), detect source language if unknown. - Translation call — a single Claude request per batch of strings, with target language and tone specified in the prompt.
- Output validation — check that placeholders survived, that length constraints (e.g. UI button text) are respected, and that the output is valid JSON if you requested structured output.
Prompt structure that works well
System: You are a professional translator. Translate the user's text from {source_lang}
to {target_lang}. Preserve all placeholders exactly as written (e.g. {{name}}, %s).
Do not translate code blocks, URLs, or brand names. Return only the translated text,
no explanations.
User: {text_to_translate}
For batch translation, send structured input and ask for structured output:
curl https://api.anthropic.com/v1/messages \
-H "x-api-key: $API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "content-type: application/json" \
-d '{
"model": "claude-sonnet-4-5",
"max_tokens": 1024,
"system": "Translate each value in the JSON object to French. Keep keys unchanged. Preserve placeholders like {{name}}. Return valid JSON only.",
"messages": [
{"role": "user", "content": "{\"welcome\": \"Hello, {{name}}!\", \"logout\": \"Sign out\"}"}
]
}'
Asking for JSON in, JSON out makes the output directly consumable by your i18n pipeline without a separate parsing step.
Handling many target languages in one request
For apps that need the same string in 10+ languages (common in SaaS onboarding flows), it's cheaper and faster to request all target languages at once rather than firing N separate calls:
Translate the following text into: Spanish, French, German, Japanese, Portuguese.
Return a JSON object keyed by ISO language code.
Text: "Your trial ends in 3 days."
This cuts round trips significantly, though for very large batches you'll want to split by content size to stay within reasonable response times and avoid hitting the model's max output tokens on a single call.
Streaming for long documents
For document-length translations (contracts, articles, long support threads), streaming the response keeps the UI responsive instead of showing a spinner for 10+ seconds. If you're building on SubToAPI as your API layer, streaming works the same way as any other Claude call — server-sent events chunked as the model generates:
const res = await fetch("https://api.subtoapi.app/v1/messages", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.SUBTOAPI_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
model: "claude-sonnet-4-5",
max_tokens: 2048,
stream: true,
system: "Translate the following document to German. Preserve Markdown formatting.",
messages: [{ role: "user", content: documentText }]
})
});
Details on handling the stream chunks are in the streaming docs.
Preserving formatting and glossaries
Two issues come up repeatedly in production translation apps:
- Formatting drift — Claude may reformat Markdown lists or HTML tags slightly differently across languages. Fix this by explicitly instructing "preserve exact Markdown structure" and validating output with a formatting linter before saving.
- Glossary consistency — brand names, product features, and technical terms need consistent translations across sessions. Include a short glossary block in the system prompt (
"Translate 'workspace' as 'espace de travail', not 'poste de travail'") rather than relying on the model to infer consistency from scratch each time.
For apps that need function-level control — e.g., calling a glossary lookup tool before translating — Claude's tool use support lets you fetch approved terms mid-conversation; see tool use for the request format.
Cost and rate limits at scale
Translation apps tend to send many small, repetitive requests rather than a few large ones, which makes cost-per-call and rate limiting the main operational concerns, not raw model quality. Batching strings per request (as shown above) reduces call volume. Caching translations for strings that don't change (static UI labels) avoids re-translating the same content on every deploy.
If you're running this across a team — a frontend engineer testing locally, a CI pipeline translating build artifacts, and a staging environment — managing separate Anthropic credentials for each gets messy fast. SubToAPI issues scoped sub_live_ application keys per environment or team member, so you can revoke a leaked CI key without touching production, and see per-key usage in one dashboard. Setup takes a few minutes — see the quickstart — and plans start at Solo for solo builders, scaling to Team and Scale seats as more people need access. Check pricing for current tiers.
Getting started
- Define your prompt template with explicit placeholder-preservation rules.
- Batch strings by target language and request JSON output for easy parsing.
- Stream long-document translations to keep the UI responsive.
- Maintain a glossary block in your system prompt for brand/term consistency.
- Separate API keys by environment so a CI leak doesn't become a production incident.
A free trial is available at signup if you want to test the request/response shape before wiring up a full pipeline — the Messages docs cover the exact payload structure.
Questions
Does Claude support every language pair out of the box? Claude handles dozens of major languages well without fine-tuning, including less common pairs (e.g., Japanese to Portuguese) since it translates through meaning rather than a fixed language-pair model. Quality varies by language resource availability, so test low-resource languages before shipping.
Should I translate one string at a time or batch requests? Batch wherever possible. Sending 20 UI strings in one JSON request is cheaper and faster than 20 separate calls, and keeps terminology consistent within that batch.
How do I keep placeholders like {{name}} from being translated? State the rule explicitly in the system prompt and validate the output programmatically — check that every placeholder in the source string exists unchanged in the output before saving it.