Build a Multi-Language Translator App with Claude API

What Does This Translation App Do?
This project builds a FastAPI-backed translation service that accepts text in any language, detects the source language automatically, translates with awareness of formality level (formal, neutral, informal) and domain (legal, medical, marketing, technical), validates quality via back-translation, and returns structured JSON results — all powered by Claude's language understanding rather than a word-substitution engine.
Machine translation has existed for decades. The difference between traditional translation engines and Claude-powered translation becomes most apparent when text requires cultural sensitivity, tone preservation, or domain-specific precision. A legal contract translated by a generic translation engine may be grammatically correct but use the wrong formality register for a given jurisdiction. Marketing copy may translate accurately but lose all of its emotional impact.
Claude translates with awareness of context, tone, formality, and domain. This project builds a multi-language translator application with a FastAPI backend that handles detection, translation, quality validation, and tone adjustment — all through structured API endpoints.
What We Are Building
The translator application provides:
- Automatic language detection: Identify the source language without the user specifying it
- Context-aware translation: Translate with awareness of domain (legal, medical, technical, business, casual) and formality level
- Back-translation quality check: Translate back to the original language and score deviation from the original to measure translation quality
- REST API: A FastAPI service that any frontend can call
Prerequisites
- Python 3.9 or later
- pip install anthropic fastapi uvicorn pydantic
- An Anthropic API key set as ANTHROPIC_API_KEY
Core Translation Logic
import anthropic
import json
from enum import Enum
client = anthropic.Anthropic()
class Domain(str, Enum):
general = "general"
legal = "legal"
medical = "medical"
technical = "technical"
marketing = "marketing"
customer_service = "customer_service"
class Formality(str, Enum):
formal = "formal"
neutral = "neutral"
informal = "informal"
# ─── Tool Definitions ─────────────────────────────────────────────────────────
DETECT_LANGUAGE_TOOL = {
"name": "detect_language",
"description": "Detect the language of input text",
"input_schema": {
"type": "object",
"properties": {
"language_name": {"type": "string", "description": "Full language name, e.g. 'English', 'French'"},
"language_code": {"type": "string", "description": "ISO 639-1 code, e.g. 'en', 'fr', 'de'"},
"confidence": {"type": "number", "description": "Confidence score 0.0 to 1.0"},
"script": {"type": "string", "description": "Writing script, e.g. 'Latin', 'Cyrillic', 'Arabic'"}
},
"required": ["language_name", "language_code", "confidence"]
}
}
TRANSLATE_TOOL = {
"name": "translate_text",
"description": "Translate text from one language to another",
"input_schema": {
"type": "object",
"properties": {
"translated_text": {
"type": "string",
"description": "The translated text"
},
"source_language": {"type": "string", "description": "Detected or specified source language"},
"target_language": {"type": "string", "description": "Target language"},
"formality_applied": {
"type": "string",
"enum": ["formal", "neutral", "informal"],
"description": "The formality register used in the translation"
},
"translation_notes": {
"type": "array",
"items": {"type": "string"},
"description": "Notes on translation decisions, cultural adaptations, or terms without direct equivalents"
}
},
"required": ["translated_text", "source_language", "target_language", "formality_applied"]
}
}
QUALITY_CHECK_TOOL = {
"name": "quality_assessment",
"description": "Assess the quality of a translation",
"input_schema": {
"type": "object",
"properties": {
"accuracy_score": {
"type": "integer",
"description": "Score 1-10: how accurately the meaning is preserved"
},
"fluency_score": {
"type": "integer",
"description": "Score 1-10: how natural the translation reads"
},
"formality_score": {
"type": "integer",
"description": "Score 1-10: how well formality level is maintained"
},
"issues": {
"type": "array",
"items": {"type": "string"},
"description": "Specific translation issues found, if any"
},
"overall_quality": {
"type": "string",
"enum": ["excellent", "good", "acceptable", "poor"],
"description": "Overall translation quality assessment"
}
},
"required": ["accuracy_score", "fluency_score", "formality_score", "overall_quality"]
}
}
# ─── Core Functions ───────────────────────────────────────────────────────────
def detect_language(text: str) -> dict:
"""Detect the language of the input text."""
response = client.messages.create(
model="claude-haiku-4-5", # Fast, cheap for detection
max_tokens=256,
tools=[DETECT_LANGUAGE_TOOL],
tool_choice={"type": "tool", "name": "detect_language"},
messages=[{"role": "user", "content": f"Detect the language of this text:\n\n{text[:500]}"}]
)
for block in response.content:
if block.type == "tool_use":
return block.input
raise RuntimeError("Language detection failed")
def translate(
text: str,
target_language: str,
source_language: str = "auto",
domain: Domain = Domain.general,
formality: Formality = Formality.neutral
) -> dict:
"""Translate text to the target language with domain and formality awareness."""
domain_guidance = {
Domain.legal: "Use precise legal terminology appropriate for the target jurisdiction. Maintain formal register. Do not paraphrase legal concepts.",
Domain.medical: "Use accurate medical/clinical terminology. Maintain precision over readability for clinical content.",
Domain.technical: "Use technical terminology accurately. Preserve product names, API names, and technical identifiers untranslated.",
Domain.marketing: "Adapt cultural references and idiomatic expressions for the target culture. Prioritise emotional impact over literal accuracy.",
Domain.customer_service: "Keep language clear, empathetic, and appropriate for customer communication.",
Domain.general: "Balance accuracy and natural expression."
}
formality_guidance = {
Formality.formal: "Use formal vocabulary and grammar throughout. In languages with formal pronouns (vous, Sie, usted), use the formal form.",
Formality.neutral: "Use standard, professional language.",
Formality.informal: "Use conversational language. In languages with informal pronouns (tu, du, tú), use the informal form."
}
source_note = f"Source language: {source_language}" if source_language != "auto" else "Auto-detect the source language."
response = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=4096,
tools=[TRANSLATE_TOOL],
tool_choice={"type": "tool", "name": "translate_text"},
system=f"""You are a professional translator with expertise across multiple languages and domains.
Translation parameters:
- Target language: {target_language}
- Domain: {domain.value} — {domain_guidance[domain]}
- Formality: {formality.value} — {formality_guidance[formality]}
- {source_note}
Provide accurate, culturally appropriate translations. Note any translation decisions that deviate from literal meaning or terms without direct equivalents.""",
messages=[{"role": "user", "content": f"Translate the following text:\n\n{text}"}]
)
for block in response.content:
if block.type == "tool_use":
return block.input
raise RuntimeError("Translation failed")
def check_quality(
original_text: str,
translation: str,
source_language: str,
target_language: str
) -> dict:
"""
Check translation quality via back-translation comparison.
Translates the result back and compares with the original.
"""
# Back-translate
back_translation = translate(
text=translation,
target_language=source_language,
source_language=target_language
)
back_translated_text = back_translation.get("translated_text", "")
response = client.messages.create(
model="claude-haiku-4-5",
max_tokens=1024,
tools=[QUALITY_CHECK_TOOL],
tool_choice={"type": "tool", "name": "quality_assessment"},
messages=[
{
"role": "user",
"content": f"""Assess translation quality.
Original ({source_language}):
{original_text}
Translation ({target_language}):
{translation}
Back-translation to {source_language}:
{back_translated_text}
Compare the original with the back-translation to assess meaning preservation.
Also evaluate the direct translation for fluency."""
}
]
)
for block in response.content:
if block.type == "tool_use":
result = block.input
result["back_translation"] = back_translated_text
return result
raise RuntimeError("Quality check failed")FastAPI REST API
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from typing import Optional
app = FastAPI(title="AI Translator API", version="1.0")
class TranslationRequest(BaseModel):
text: str
target_language: str
source_language: Optional[str] = "auto"
domain: Optional[Domain] = Domain.general
formality: Optional[Formality] = Formality.neutral
quality_check: Optional[bool] = False
class TranslationResponse(BaseModel):
original_text: str
translated_text: str
source_language: str
target_language: str
domain: str
formality_applied: str
translation_notes: list
quality: Optional[dict] = None
@app.post("/translate", response_model=TranslationResponse)
async def translate_endpoint(request: TranslationRequest):
"""Translate text with optional quality checking."""
if len(request.text) > 10000:
raise HTTPException(status_code=400, detail="Text exceeds 10,000 character limit")
# Detect source language if auto
source_lang = request.source_language
if source_lang == "auto":
detection = detect_language(request.text)
source_lang = detection["language_name"]
# Translate
translation_result = translate(
text=request.text,
target_language=request.target_language,
source_language=source_lang,
domain=request.domain,
formality=request.formality
)
# Optional quality check
quality_result = None
if request.quality_check:
quality_result = check_quality(
original_text=request.text,
translation=translation_result["translated_text"],
source_language=source_lang,
target_language=request.target_language
)
return TranslationResponse(
original_text=request.text,
translated_text=translation_result["translated_text"],
source_language=source_lang,
target_language=request.target_language,
domain=request.domain.value,
formality_applied=translation_result["formality_applied"],
translation_notes=translation_result.get("translation_notes", []),
quality=quality_result
)
@app.post("/detect")
async def detect_endpoint(body: dict):
"""Detect the language of input text."""
text = body.get("text", "")
if not text:
raise HTTPException(status_code=400, detail="text is required")
return detect_language(text)
# Run with: uvicorn translator:app --reloadUse Haiku for Detection and Quality Checks
Language detection and back-translation quality checks are tasks where speed and cost matter more than reasoning depth. Claude Haiku 4.5 is significantly cheaper and faster than Sonnet, and handles these structured tasks reliably. Reserve Sonnet for the actual translation step where nuance and domain accuracy genuinely require the more capable model.
Extending the Project
- Batch translation: Add an endpoint that accepts an array of strings and translates them in a single API call, using the Batch API for 50% cost reduction on large workloads
- Glossary enforcement: Accept a custom glossary of terms (product names, branded terms) that must remain untranslated or use specific approved translations
- Translation memory: Cache previously translated segments in a database and reuse them for repeated phrases — significantly reducing cost and improving consistency in large document translation
- Streaming: Use the streaming API to display translation character by character in a web UI, improving perceived responsiveness for long documents
Summary
Claude's translation capability goes well beyond word-for-word substitution. The domain and formality parameters in this project illustrate what makes Claude-powered translation more useful for professional contexts than a generic translation engine.
- Use Haiku for detection and quality checks — fast structural tasks where model depth does not matter
- Use Sonnet for translation — where domain and cultural nuance require genuine language understanding
- Use back-translation quality checks for mission-critical content (legal, medical) where catching mistranslations is essential
- Pass domain and formality context in every translation request — it dramatically improves output relevance
Moving from beginner projects to intermediate IT professional builds, next up: Project: Build a RAG App with Claude — Query Your Own Documents.
For the Claude model selection behind this project (Haiku for detection, Sonnet for translation), see Claude Model Family: Opus, Sonnet, and Haiku and Claude API Pricing and Tokens Explained. To add structured output guarantees to translation results, see Claude Structured Outputs and JSON.
External Resources
- FastAPI documentation — the official guide for the web framework used to build the translation API in this project.
- Anthropic multilingual capabilities guide — official documentation on Claude's multilingual translation and language detection capabilities.
This post is part of the Anthropic AI Tutorial Series. Previous post: Project: Build a Code Review Assistant for GitHub PRs.
