How to Build a Claude-Powered Translation Tool
Building a translation tool with Claude means sending text to the Claude API with a prompt that specifies source language, target language, and tone, then handling the response as structured text (or JSON if you need metadata like confidence or alternate phrasings). Unlike dedicated translation APIs such as Google Translate or DeepL, Claude understands context, idiom, register, and domain-specific terminology, which makes it better suited for marketing copy, support tickets, legal snippets, or UI strings where literal word-for-word translation produces awkward results.
This guide walks through the actual implementation: prompt structure, handling multiple languages in one request, preserving formatting (HTML, Markdown, placeholders), streaming for long documents, and batching for cost efficiency.
Why use Claude instead of a dedicated translation API
Dedicated MT (machine translation) engines are fast and cheap for literal translation, but they struggle with:
- Tone and register — formal vs. casual, brand voice consistency
- Context-dependent terms — a word that means different things in a legal doc vs. a product UI
- Instructions mixed with content — "translate this but keep the CTA under 20 characters"
- Structured input — JSON keys, HTML tags, markdown, code comments that must stay intact
Claude handles all of these because it's a general-purpose language model that follows instructions, not a lookup-and-align system. The tradeoff is latency and cost per request, so the architecture matters.
Step 1: Design the core translation prompt
Keep the system prompt narrow and deterministic. Don't let the model add commentary, apologize, or explain its choices — you want raw translated output.
System: You are a professional translator. Translate the user's text from {source_lang} to {target_lang}.
Rules:
- Preserve all HTML tags, placeholders like {{name}}, and line breaks exactly.
- Match the register: {tone} (e.g. formal, casual, marketing).
- Do not add explanations, notes, or quotation marks around the output.
- If a term has no direct translation, use the closest natural equivalent used by native speakers.
Return only the translated text.
The user message is just the raw source text. Keeping instructions in the system prompt instead of repeating them per request makes the code cleaner and the behavior more consistent across calls.
Step 2: Call the API
Here's a minimal implementation using SubToAPI's Messages endpoint, which exposes Claude through a standard HTTPS API with your own sub_live_... key — useful if you want usage metadata and team billing on top of raw model access without managing Anthropic infrastructure directly.
async function translate(text, sourceLang, targetLang, tone = "neutral") {
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-3-5-sonnet-20241022",
max_tokens: 1024,
system: `You are a professional translator. Translate from ${sourceLang} to ${targetLang}.
Preserve HTML tags, placeholders, and line breaks exactly.
Match tone: ${tone}. Return only the translated text, no notes.`,
messages: [{ role: "user", content: text }],
}),
});
const data = await res.json();
return data.content[0].text;
}
const result = await translate(
"Welcome back, {{name}}! Your order #{{order_id}} has shipped.",
"English",
"Spanish",
"friendly"
);
See the quickstart for authentication setup and the Messages reference for the full request/response shape.
Step 3: Preserve formatting and placeholders
Production translation tools almost always deal with structured content: HTML emails, UI strings with interpolation variables, or markdown docs. The safest approach is to explicitly instruct Claude to treat certain patterns as inviolable, and to validate the output programmatically afterward.
function validatePlaceholders(original, translated) {
const extract = (s) => s.match(/\{\{.*?\}\}/g) || [];
const originalVars = extract(original).sort();
const translatedVars = extract(translated).sort();
return JSON.stringify(originalVars) === JSON.stringify(translatedVars);
}
If validation fails, retry once with a stricter instruction ("You dropped a placeholder — return the text again with every {{variable}} preserved exactly"). This is more reliable than trying to get it perfect on the first attempt every time.
Step 4: Handle multiple target languages efficiently
If you need the same text translated into five languages, don't make five sequential calls if latency matters. Run them in parallel:
async function translateToMany(text, sourceLang, targets, tone) {
const jobs = targets.map((lang) => translate(text, sourceLang, lang, tone));
const results = await Promise.all(jobs);
return targets.reduce((acc, lang, i) => {
acc[lang] = results[i];
return acc;
}, {});
}
This fans out to Claude concurrently instead of waiting on each request in turn, which matters a lot once you're translating onboarding flows or product catalogs into a dozen locales.
Step 5: Stream for long documents
For paragraph-length UI strings, a single blocking request is fine. For full documents — contracts, articles, release notes — streaming avoids long perceived wait times and lets you render translated text as it arrives. SubToAPI supports streaming responses over the same endpoint; see streaming for the event format and client handling.
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-3-5-sonnet-20241022",
max_tokens: 4096,
stream: true,
system: "Translate from English to Japanese. Return only the translation.",
messages: [{ role: "user", content: longDocument }],
}),
});
For very long inputs, chunk by paragraph rather than sending the whole document in one call — this keeps context windows manageable and lets you parallelize chunks, provided the chunking boundaries don't split sentences mid-thought.
Step 6: Add quality control for production use
A few practical safeguards that separate a demo from a shippable tool:
- Round-trip check: translate back to the source language and flag large semantic drift for human review.
- Glossary enforcement: inject a list of brand/product terms that must stay untranslated or use a fixed translation, directly in the system prompt.
- Length constraints: for UI strings with character limits, tell Claude the max length and verify it in code — don't rely on the model to count precisely.
- Logging: store source, target, tone, and output per request so you can audit quality issues after the fact.
Pricing and operational notes
Running translation at scale means a high volume of short requests. If you're prototyping or running a small team tool, SubToAPI's Solo plan at €9 covers individual use with API key access, usage metadata, and streaming. Teams translating across multiple products or locales typically want the Team plan (€19/seat) for shared dashboards and per-key usage tracking. You can try the full flow with a free trial at /signup before committing.
Questions
Does Claude translate better than Google Translate or DeepL? For literal, high-volume translation of simple sentences, dedicated MT engines are often faster and cheaper. Claude tends to outperform them on tone matching, idiomatic phrasing, and content that mixes instructions with text — at the cost of higher per-request latency and price.
How do I keep HTML tags and variables intact during translation? Instruct Claude explicitly to preserve tags and placeholders in the system prompt, then validate programmatically after the response (e.g., regex-match {{variable}} patterns) and retry if anything was dropped or altered.
Can I translate into multiple languages in one API call? Not reliably in a single call with consistent formatting — it's better to send one request per target language and run them concurrently with Promise.all (or your language's equivalent) to keep total latency low.