Files
office_translator/schemas/translation.py
sepehr 526c87348f
All checks were successful
Deploy to Production / Build and Deploy (push) Successful in 2m20s
feat(translation): quality pipeline overhaul + new features (audit 2026-08-29)
Translation quality & format preservation:
- Word: merge adjacent same-format runs into one unit (sentence-level
  coherence like inline-tag handling); translate comments/balloons;
  dedupe textbox collection (was translated twice); RTL no longer
  overrides center/justify alignment; CJK/Arabic font hints (eastAsia/cs)
- PPTX: chart translations now actually reach the output file
  (ChartPart.blob is read-only — rewrite chart XML in the saved ZIP);
  CJK typeface hints (a:ea)
- Excel: sheet renames no longer break references — rewrite cell
  formulas (3D/quoted), defined names, data validations, cond. formats
- PDF: bold/italic honored (hebo/heit/hebi); table cells never merge;
  unchanged blocks left untouched (typography preserved, fixes duplicate
  hyperlinks); attempted/changed stats + route gate now cover PDF;
  CJK font paths; scanned PDFs via Mistral OCR (detection + admin settings)

Features:
- formality param (formal/informal) + automatic regional-variant prompts
- output_mode=bilingual docx (source above translation)
- per-user translation memory on Redis (falls back to LRU), context-hashed
- QA report + 0-100 confidence score in job status; L0 on by default
- OpenAI-compatible providers: whole chunk in ONE numbered-JSON request
  (~15x fewer calls) with per-item fallback; base prompt always present
  (custom prompt no longer replaces translation instructions)

Infra & marketing alignment:
- plan-based engine gating + vision gating (closes paid-engine leak);
  /providers/available filtered per plan; 107 languages exposed
- zh-CN/zh-TW validation fixed; libmagic disabled on Windows (native crash)
- admin: Mistral OCR settings + engine status dashboard; httpx<0.28 pin
  (TestClient breakage); Prometheus test fixture fixed
- marketing docs aligned with code (PDF+OCR, retention, engines, pricing)
- security: .env.ionos/.env.production/provider_settings.json removed

Tests: 1173 passed / 0 failed (6 network tests deselected: free Google
endpoint temporarily blocked from this machine)
2026-08-29 18:38:09 +02:00

244 lines
6.8 KiB
Python

"""
Pydantic models for translation endpoints
Story 3.6: Documentation OpenAPI (Swagger + ReDoc)
"""
from pydantic import BaseModel, Field
from typing import Optional, Literal, List, Dict, Any
from datetime import datetime
class TranslateResponseData(BaseModel):
"""Response data for translation request"""
id: str = Field(
...,
example="tr_abc123def456",
description="Identifiant unique du job de traduction"
)
status: Literal["processing"] = Field(
default="processing",
description="Statut du job (toujours 'processing' à la création)"
)
file_name: Optional[str] = Field(
None,
example="rapport_financier.xlsx",
description="Nom du fichier original"
)
source_lang: Optional[str] = Field(
None,
example="en",
description="Code langue source (ISO 639-1)"
)
target_lang: str = Field(
...,
example="fr",
description="Code langue cible (ISO 639-1)"
)
class Config:
json_schema_extra = {
"example": {
"id": "tr_abc123def456",
"status": "processing",
"file_name": "rapport_financier.xlsx",
"source_lang": "en",
"target_lang": "fr"
}
}
class TranslateResponseMeta(BaseModel):
"""Metadata for translation response"""
rate_limit_remaining: int = Field(
...,
description="Nombre de traductions restantes aujourd'hui",
example=4
)
estimated_time_seconds: Optional[int] = Field(
None,
description="Temps estimé pour la traduction en secondes",
example=15
)
class TranslateResponse(BaseModel):
"""Full response for translation request"""
data: TranslateResponseData
meta: TranslateResponseMeta
class Config:
json_schema_extra = {
"example": {
"data": {
"id": "tr_abc123def456",
"status": "processing",
"file_name": "rapport_financier.xlsx",
"source_lang": "en",
"target_lang": "fr"
},
"meta": {
"rate_limit_remaining": 4,
"estimated_time_seconds": 15
}
}
}
class TranslationStatusData(BaseModel):
"""Response data for translation status endpoint"""
id: str = Field(
...,
description="Identifiant unique du job de traduction",
example="tr_abc123def456"
)
status: Literal["queued", "processing", "completed", "failed"] = Field(
...,
description="Statut actuel du job"
)
progress_percent: int = Field(
default=0,
ge=0,
le=100,
description="Pourcentage de progression (0-100)",
example=65
)
current_step: str = Field(
default="Initializing",
description="Description de l'opération en cours",
example="Traduction de la diapositive 3/5"
)
file_name: Optional[str] = Field(
None,
description="Nom du fichier original",
example="presentation.pptx"
)
source_lang: Optional[str] = Field(
None,
description="Code langue source",
example="en"
)
target_lang: Optional[str] = Field(
None,
description="Code langue cible",
example="fr"
)
created_at: Optional[str] = Field(
None,
description="Date de création (ISO 8601)",
example="2024-01-15T10:30:00Z"
)
completed_at: Optional[str] = Field(
None,
description="Date de complétion (ISO 8601)",
example="2024-01-15T10:35:00Z"
)
failed_at: Optional[str] = Field(
None,
description="Date d'échec (ISO 8601)",
example=None
)
error_message: Optional[str] = Field(
None,
description="Message d'erreur si status='failed'",
example=None
)
quality: Optional[dict] = Field(
None,
description=(
"Rapport qualité post-traduction (job réussi) : "
"score 0-100, fidélité des nombres, ratio non traduit."
),
example={"score": 92, "numbers": {"source_numbers": 12, "preserved": 12, "fidelity": 1.0}, "untranslated_ratio": 0.02},
)
class Config:
json_schema_extra = {
"example": {
"id": "tr_abc123def456",
"status": "processing",
"progress_percent": 65,
"current_step": "Traduction de la diapositive 3/5",
"file_name": "presentation.pptx",
"source_lang": "en",
"target_lang": "fr",
"created_at": "2024-01-15T10:30:00Z",
"completed_at": None,
"failed_at": None,
"error_message": None
}
}
class TranslationStatusMeta(BaseModel):
"""Metadata for translation status response"""
estimated_remaining_seconds: Optional[int] = Field(
default=None,
description="Temps restant estimé en secondes",
example=30
)
class TranslationStatusResponse(BaseModel):
"""Full response for translation status endpoint"""
data: TranslationStatusData
meta: Optional[TranslationStatusMeta] = None
class Config:
json_schema_extra = {
"example": {
"data": {
"id": "tr_abc123def456",
"status": "processing",
"progress_percent": 65,
"current_step": "Traduction de la diapositive 3/5",
"file_name": "presentation.pptx",
"source_lang": "en",
"target_lang": "fr",
"created_at": "2024-01-15T10:30:00Z"
},
"meta": {
"estimated_remaining_seconds": 30
}
}
}
class LanguageItem(BaseModel):
"""Single language entry"""
code: str = Field(
...,
description="Code langue ISO 639-1",
example="fr"
)
name: str = Field(
...,
description="Nom de la langue en français",
example="Français"
)
class LanguageResponse(BaseModel):
"""Response model for supported languages"""
supported_languages: Dict[str, str] = Field(
...,
description="Dictionnaire des langues supportées (code -> nom)",
example={
"fr": "French",
"de": "German",
"es": "Spanish",
"it": "Italian"
}
)
note: Optional[str] = Field(
None,
description="Note sur la disponibilité des langues",
example="Les langues supportées peuvent varier selon le service de traduction configuré"
)