Project: Build a Multi-Language Translator App with Claude

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
1import anthropic
2import json
3from enum import Enum
4
5client = anthropic.Anthropic()
6
7
8class Domain(str, Enum):
9 general = "general"
10 legal = "legal"
11 medical = "medical"
12 technical = "technical"
13 marketing = "marketing"
14 customer_service = "customer_service"
15
16
17class Formality(str, Enum):
18 formal = "formal"
19 neutral = "neutral"
20 informal = "informal"
21
22
23# ─── Tool Definitions ─────────────────────────────────────────────────────────
24
25DETECT_LANGUAGE_TOOL = {
26 "name": "detect_language",
27 "description": "Detect the language of input text",
28 "input_schema": {
29 "type": "object",
30 "properties": {
31 "language_name": {"type": "string", "description": "Full language name, e.g. 'English', 'French'"},
32 "language_code": {"type": "string", "description": "ISO 639-1 code, e.g. 'en', 'fr', 'de'"},
33 "confidence": {"type": "number", "description": "Confidence score 0.0 to 1.0"},
34 "script": {"type": "string", "description": "Writing script, e.g. 'Latin', 'Cyrillic', 'Arabic'"}
35 },
36 "required": ["language_name", "language_code", "confidence"]
37 }
38}
39
40TRANSLATE_TOOL = {
41 "name": "translate_text",
42 "description": "Translate text from one language to another",
43 "input_schema": {
44 "type": "object",
45 "properties": {
46 "translated_text": {
47 "type": "string",
48 "description": "The translated text"
49 },
50 "source_language": {"type": "string", "description": "Detected or specified source language"},
51 "target_language": {"type": "string", "description": "Target language"},
52 "formality_applied": {
53 "type": "string",
54 "enum": ["formal", "neutral", "informal"],
55 "description": "The formality register used in the translation"
56 },
57 "translation_notes": {
58 "type": "array",
59 "items": {"type": "string"},
60 "description": "Notes on translation decisions, cultural adaptations, or terms without direct equivalents"
61 }
62 },
63 "required": ["translated_text", "source_language", "target_language", "formality_applied"]
64 }
65}
66
67QUALITY_CHECK_TOOL = {
68 "name": "quality_assessment",
69 "description": "Assess the quality of a translation",
70 "input_schema": {
71 "type": "object",
72 "properties": {
73 "accuracy_score": {
74 "type": "integer",
75 "description": "Score 1-10: how accurately the meaning is preserved"
76 },
77 "fluency_score": {
78 "type": "integer",
79 "description": "Score 1-10: how natural the translation reads"
80 },
81 "formality_score": {
82 "type": "integer",
83 "description": "Score 1-10: how well formality level is maintained"
84 },
85 "issues": {
86 "type": "array",
87 "items": {"type": "string"},
88 "description": "Specific translation issues found, if any"
89 },
90 "overall_quality": {
91 "type": "string",
92 "enum": ["excellent", "good", "acceptable", "poor"],
93 "description": "Overall translation quality assessment"
94 }
95 },
96 "required": ["accuracy_score", "fluency_score", "formality_score", "overall_quality"]
97 }
98}
99
100
101# ─── Core Functions ───────────────────────────────────────────────────────────
102
103def detect_language(text: str) -> dict:
104 """Detect the language of the input text."""
105 response = client.messages.create(
106 model="claude-haiku-4-5", # Fast, cheap for detection
107 max_tokens=256,
108 tools=[DETECT_LANGUAGE_TOOL],
109 tool_choice={"type": "tool", "name": "detect_language"},
110 messages=[{"role": "user", "content": f"Detect the language of this text:\n\n{text[:500]}"}]
111 )
112
113 for block in response.content:
114 if block.type == "tool_use":
115 return block.input
116 raise RuntimeError("Language detection failed")
117
118
119def translate(
120 text: str,
121 target_language: str,
122 source_language: str = "auto",
123 domain: Domain = Domain.general,
124 formality: Formality = Formality.neutral
125) -> dict:
126 """Translate text to the target language with domain and formality awareness."""
127
128 domain_guidance = {
129 Domain.legal: "Use precise legal terminology appropriate for the target jurisdiction. Maintain formal register. Do not paraphrase legal concepts.",
130 Domain.medical: "Use accurate medical/clinical terminology. Maintain precision over readability for clinical content.",
131 Domain.technical: "Use technical terminology accurately. Preserve product names, API names, and technical identifiers untranslated.",
132 Domain.marketing: "Adapt cultural references and idiomatic expressions for the target culture. Prioritise emotional impact over literal accuracy.",
133 Domain.customer_service: "Keep language clear, empathetic, and appropriate for customer communication.",
134 Domain.general: "Balance accuracy and natural expression."
135 }
136
137 formality_guidance = {
138 Formality.formal: "Use formal vocabulary and grammar throughout. In languages with formal pronouns (vous, Sie, usted), use the formal form.",
139 Formality.neutral: "Use standard, professional language.",
140 Formality.informal: "Use conversational language. In languages with informal pronouns (tu, du, tú), use the informal form."
141 }
142
143 source_note = f"Source language: {source_language}" if source_language != "auto" else "Auto-detect the source language."
144
145 response = client.messages.create(
146 model="claude-sonnet-4-6",
147 max_tokens=4096,
148 tools=[TRANSLATE_TOOL],
149 tool_choice={"type": "tool", "name": "translate_text"},
150 system=f"""You are a professional translator with expertise across multiple languages and domains.
151
152Translation parameters:
153- Target language: {target_language}
154- Domain: {domain.value} — {domain_guidance[domain]}
155- Formality: {formality.value} — {formality_guidance[formality]}
156- {source_note}
157
158Provide accurate, culturally appropriate translations. Note any translation decisions that deviate from literal meaning or terms without direct equivalents.""",
159 messages=[{"role": "user", "content": f"Translate the following text:\n\n{text}"}]
160 )
161
162 for block in response.content:
163 if block.type == "tool_use":
164 return block.input
165 raise RuntimeError("Translation failed")
166
167
168def check_quality(
169 original_text: str,
170 translation: str,
171 source_language: str,
172 target_language: str
173) -> dict:
174 """
175 Check translation quality via back-translation comparison.
176 Translates the result back and compares with the original.
177 """
178 # Back-translate
179 back_translation = translate(
180 text=translation,
181 target_language=source_language,
182 source_language=target_language
183 )
184
185 back_translated_text = back_translation.get("translated_text", "")
186
187 response = client.messages.create(
188 model="claude-haiku-4-5",
189 max_tokens=1024,
190 tools=[QUALITY_CHECK_TOOL],
191 tool_choice={"type": "tool", "name": "quality_assessment"},
192 messages=[
193 {
194 "role": "user",
195 "content": f"""Assess translation quality.
196
197Original ({source_language}):
198{original_text}
199
200Translation ({target_language}):
201{translation}
202
203Back-translation to {source_language}:
204{back_translated_text}
205
206Compare the original with the back-translation to assess meaning preservation.
207Also evaluate the direct translation for fluency."""
208 }
209 ]
210 )
211
212 for block in response.content:
213 if block.type == "tool_use":
214 result = block.input
215 result["back_translation"] = back_translated_text
216 return result
217 raise RuntimeError("Quality check failed")FastAPI REST API
1from fastapi import FastAPI, HTTPException
2from pydantic import BaseModel
3from typing import Optional
4
5app = FastAPI(title="AI Translator API", version="1.0")
6
7
8class TranslationRequest(BaseModel):
9 text: str
10 target_language: str
11 source_language: Optional[str] = "auto"
12 domain: Optional[Domain] = Domain.general
13 formality: Optional[Formality] = Formality.neutral
14 quality_check: Optional[bool] = False
15
16
17class TranslationResponse(BaseModel):
18 original_text: str
19 translated_text: str
20 source_language: str
21 target_language: str
22 domain: str
23 formality_applied: str
24 translation_notes: list
25 quality: Optional[dict] = None
26
27
28@app.post("/translate", response_model=TranslationResponse)
29async def translate_endpoint(request: TranslationRequest):
30 """Translate text with optional quality checking."""
31
32 if len(request.text) > 10000:
33 raise HTTPException(status_code=400, detail="Text exceeds 10,000 character limit")
34
35 # Detect source language if auto
36 source_lang = request.source_language
37 if source_lang == "auto":
38 detection = detect_language(request.text)
39 source_lang = detection["language_name"]
40
41 # Translate
42 translation_result = translate(
43 text=request.text,
44 target_language=request.target_language,
45 source_language=source_lang,
46 domain=request.domain,
47 formality=request.formality
48 )
49
50 # Optional quality check
51 quality_result = None
52 if request.quality_check:
53 quality_result = check_quality(
54 original_text=request.text,
55 translation=translation_result["translated_text"],
56 source_language=source_lang,
57 target_language=request.target_language
58 )
59
60 return TranslationResponse(
61 original_text=request.text,
62 translated_text=translation_result["translated_text"],
63 source_language=source_lang,
64 target_language=request.target_language,
65 domain=request.domain.value,
66 formality_applied=translation_result["formality_applied"],
67 translation_notes=translation_result.get("translation_notes", []),
68 quality=quality_result
69 )
70
71
72@app.post("/detect")
73async def detect_endpoint(body: dict):
74 """Detect the language of input text."""
75 text = body.get("text", "")
76 if not text:
77 raise HTTPException(status_code=400, detail="text is required")
78 return detect_language(text)
79
80
81# 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.
This post is part of the Anthropic AI Tutorial Series. Previous post: Project: Build a Code Review Assistant for GitHub PRs.
