Artificial IntelligenceAnthropicProjects

Project: Build a Multi-Language Translator App with Claude

TT
TopicTrick
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:

  1. Automatic language detection: Identify the source language without the user specifying it
  2. Context-aware translation: Translate with awareness of domain (legal, medical, technical, business, casual) and formality level
  3. Back-translation quality check: Translate back to the original language and score deviation from the original to measure translation quality
  4. 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

python
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

python
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 --reload

Use 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.