feat: production deployment - full update with providers, admin, glossaries, pricing, tests

Major changes across backend, frontend, infrastructure:
- Provider system with model selection (Google, DeepL, OpenAI, Ollama, Google Cloud)
- Admin panel: user management, pricing, settings
- Glossary system with CSV import/export
- Subscription and tier quota management
- Security hardening (rate limiting, API key auth, path traversal fixes)
- Docker compose for dev, prod, and IONOS deployment
- Alembic migrations for new tables
- Frontend: dashboard, pricing page, landing page, i18n (en/fr)
- Test suite and verification scripts

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Sepehr Ramezani
2026-04-25 15:01:47 +02:00
parent 2ba4fedfc8
commit 26bd096a06
1178 changed files with 136435 additions and 3047 deletions

View File

@@ -12,7 +12,10 @@
"free": "Free",
"popular": "Popular",
"month": "month",
"onRequest": "On Request"
"onRequest": "On Request",
"monthly": "Monthly",
"yearly": "Yearly",
"save20": "Save 20%"
},
"nav": {
"features": "Features",
@@ -62,35 +65,43 @@
"pricing": {
"title": "Simple and Transparent Pricing",
"subtitle": "Start for free, scale as you need.",
"free": {
"name": "Free",
"description": "For occasional needs.",
"price": "$0",
"starter": {
"name": "Starter",
"description": "For individuals and small projects.",
"priceMonthly": "$9",
"priceYearly": "$7.20",
"billedYearly": "Billed $86.40 / year",
"features": [
"5 documents / month",
"Word & Excel only",
"Community support"
"50 documents / month",
"Standard translation (Google/DeepL)",
"Files up to 10 MB",
"Email support"
]
},
"pro": {
"name": "Pro",
"description": "For demanding professionals.",
"price": "$29",
"description": "For growing professionals.",
"priceMonthly": "$19",
"priceYearly": "$15.20",
"billedYearly": "Billed $182.40 / year",
"features": [
"50 documents / month",
"All formats (PPTX included)",
"Advanced AI models (GPT-4)",
"Priority support"
"200 documents / month",
"Essential AI (DeepSeek V3)",
"Files up to 25 MB",
"Custom glossaries"
]
},
"enterprise": {
"name": "Enterprise",
"business": {
"name": "Business",
"description": "For large teams.",
"priceMonthly": "$49",
"priceYearly": "$39.20",
"billedYearly": "Billed $470.40 / year",
"features": [
"Unlimited documents",
"API Access",
"SSO & Advanced Security",
"Dedicated Account Manager"
"1,000 documents / month",
"Premium AI (Claude Haiku)",
"API Access (10K calls)",
"Dedicated support"
]
}
},
@@ -103,10 +114,277 @@
"privacy": "Privacy",
"terms": "Terms",
"contact": "Contact",
"copyright": "© 2024 Office Translator. All rights reserved."
"copyright": "© 2026 Office Translator. All rights reserved."
},
"cookieConsent": {
"title": "Cookies on Office Translator",
"description": "We use essential cookies so the app works (session, security, language). With your permission, we also use optional cookies to measure traffic and improve the product. Choose “Accept all” to allow optional cookies, or “Essential only” to refuse them.",
"acceptAll": "Accept all",
"essentialOnly": "Essential only",
"learnMore": "Learn more on our site",
"footerHint": "You can clear site data in your browser at any time to see this message again."
},
"glossaries": {
"title": "Glossaries",
"description": "Manage custom terminology for your LLM translations.",
"createNew": "Create New Glossary",
"yourGlossaries": "Your Glossaries",
"yourGlossariesDesc": "Create and manage glossaries for consistent translations",
"count_one": "{count} glossary",
"count_other": "{count} glossaries",
"defineTerms": "Define term pairs to customize your LLM translations",
"empty": "No glossaries yet",
"emptyDesc": "Create your first glossary to customize translations",
"aboutTitle": "About Glossaries",
"aboutDesc": "Glossaries let you define custom terminology for your translations. When using LLM translation modes, your terms will be applied to ensure consistent translations.",
"aboutFormat": "Format: Each term has a source (original) and target (translation) pair.",
"dialog": {
"title": "New Glossary",
"description": "Import a predefined template, upload a file with your industry jargon, or enter terms manually.",
"nameLabel": "Glossary name",
"namePlaceholder": "e.g. Legal Glossary FR-EN",
"tabTemplates": "Predefined templates",
"tabFile": "Import file",
"tabManual": "Manual entry",
"templatesDesc": "Choose a predefined glossary covering your industry's terminology.",
"templatesEmpty": "No templates available.",
"terms": "terms",
"cancel": "Cancel",
"importing": "Importing…",
"creating": "Creating…",
"importBtn": "Import ({count} terms)",
"createBtn": "Create ({count} terms)",
"createEmpty": "Create glossary",
"selectPrompt": "Select a template",
"dropTitle": "Drop your file here",
"dropOr": "or click to browse",
"dropFormats": "CSV, XLSX, XLS, ODS • max 500 terms",
"parsing": "Parsing file…",
"termsImported": "{count} terms imported",
"changeFile": "Change file",
"retry": "Retry",
"formatTitle": "Expected format:",
"formatDesc": "Two columns with headers:",
"formatNote": "The first row can be a header (source/target) or raw data.",
"errorFormat": "Unsupported format. Use CSV, XLSX, XLS or ODS.",
"errorSize": "File too large (max {max} MB). Split into smaller files.",
"errorEmpty": "No valid terms found. Check that your file has two columns (source, target).",
"errorRead": "Unable to read the file. Check the format."
},
"toast": {
"created": "Glossary created",
"createdDesc": "\"{name}\" has been created successfully.",
"imported": "Template imported",
"importedDesc": "\"{name}\" has been imported successfully.",
"updated": "Glossary updated",
"updatedDesc": "\"{name}\" has been updated successfully.",
"deleted": "Glossary deleted",
"deletedDesc": "The glossary has been deleted successfully.",
"error": "Error",
"errorCreate": "Failed to create glossary. Please try again.",
"errorImport": "Failed to import template. Please try again.",
"errorUpdate": "Failed to update glossary. Please try again.",
"errorDelete": "Failed to delete glossary. Please try again."
},
"card": {
"term": "term",
"terms": "terms",
"created": "Created"
},
"termEditor": {
"sourceTerm": "Source Term",
"targetTranslation": "Target Translation",
"addTerm": "Add Term",
"maxReached": "Maximum {max} terms per glossary reached."
}
},
"auth": {
"loading": "Loading…",
"or": "or",
"brandName": "Office Translator",
"google": {
"continue": "Continue with Google",
"signUp": "Sign up with Google",
"connecting": "Connecting…",
"errorGeneric": "Something went wrong with Google sign-in.",
"errorFailed": "Google sign-in failed. Please try again.",
"devHint": "To enable Google, add {envKey} in {envFile} (see {exampleFile})."
},
"login": {
"title": "Welcome back",
"subtitle": "Sign in to keep translating",
"email": "Email",
"password": "Password",
"emailPlaceholder": "you@example.com",
"forgotPassword": "Forgot password?",
"signingIn": "Signing in…",
"signIn": "Sign in",
"errorTitle": "Sign-in error",
"noAccount": "Don't have an account?",
"signUpFree": "Sign up for free"
},
"register": {
"title": "Create an account",
"subtitle": "Start translating for free",
"name": "Name",
"namePlaceholder": "Your name",
"email": "Email address",
"emailPlaceholder": "you@example.com",
"password": "Password",
"confirmPassword": "Confirm password",
"createAccount": "Create my account",
"creating": "Creating account…",
"hasAccount": "Already have an account?",
"signIn": "Sign in",
"termsPrefix": "By creating an account, you agree to our",
"termsService": "terms of service",
"registerFailed": "Registration failed",
"errors": {
"nameMin": "Name must be at least 2 characters",
"emailInvalid": "Invalid email address",
"passwordShort": "Password too short (minimum 8 characters)",
"passwordMismatch": "Passwords do not match"
},
"passwordStrength": {
"label": "Strength",
"weak": "Weak",
"medium": "Medium",
"strong": "Strong"
},
"toggle": {
"hide": "Hide",
"show": "Show",
"hidePassword": "Hide password",
"showPassword": "Show password"
}
}
},
"dashboard": {
"nav": {
"overview": "Overview",
"profile": "My profile",
"translate": "Translate",
"apiKeys": "API keys",
"glossaries": "Glossaries"
},
"header": {
"title": "Dashboard",
"subtitle": "Manage your API and translation settings",
"toggleMenu": "Toggle menu",
"profileTitle": "My profile"
},
"sidebar": {
"theme": "Theme",
"signOut": "Sign out",
"backHome": "Back to home"
},
"tier": {
"free": "Free",
"starter": "Starter",
"pro": "Pro",
"business": "Business",
"enterprise": "Enterprise"
},
"translate": {
"errorNotificationTitle": "Translation error",
"pageTitle": "Translate document",
"pageSubtitle": "Upload an Excel, Word, or PowerPoint file, choose languages and provider, then download your translated file — layout preserved.",
"document": {
"title": "Document",
"descReady": "File ready — you can replace it or continue below.",
"descEmpty": "Drag and drop or browse. We support Office Open XML formats.",
"supportedFormats": "Supported formats: Excel (.xlsx), Word (.docx), PowerPoint (.pptx)"
},
"settings": {
"title": "Translation settings",
"description": "Languages and engine. LLM options require Pro when applicable.",
"howItWorks": "How it works",
"step1": "Add your file in the panel on the left (or above on small screens).",
"step2": "Pick source and target languages.",
"step3": "Choose a translation provider, then run the job.",
"runHint": "When you are ready, we upload the file and start translation. You can cancel while it runs."
},
"actions": {
"translate": "Translate document",
"uploading": "Uploading…",
"cancel": "Cancel",
"tryAgain": "Try again",
"statusComplete": "Translation complete",
"statusIssue": "Translation issue",
"statusProgress": "In progress",
"filePrefix": "File:"
},
"trust": {
"zeroRetention": "Zero data retention",
"deletedAfter": "Files deleted after 60 minutes"
},
"dropzone": {
"title": "Drag & drop your .xlsx, .docx, or .pptx file here",
"subtitle": "or click to browse",
"uploadAria": "Upload file",
"replaceFile": "Replace file"
},
"language": {
"loadErrorPrefix": "Failed to load languages:",
"source": "Source language",
"target": "Target language",
"loading": "Loading…",
"autoDetect": "Auto-detect",
"selectPlaceholder": "Select language"
},
"provider": {
"loading": "Loading providers…",
"noneConfigured": "No providers are configured. Ask your administrator to enable at least one in the admin settings.",
"sectionTitle": "Translation provider",
"llmDivider": "LLM · Context-aware",
"llmDividerPro": "Pro",
"upgrade": "Upgrade to Pro",
"upgradeSuffix": "to use LLM-powered translation.",
"modelTitle": "Model (set by admin):"
},
"progress": {
"failedTitle": "Translation failed",
"processingFallback": "Processing…",
"connectionLost": "Connection lost. Retrying…",
"timeSeconds": "{seconds}s remaining",
"timeMinutes": "{minutes} min remaining",
"timeMixed": "{minutes}m {seconds}s remaining",
"ariaProgress": "Translation progress"
},
"complete": {
"title": "Translation complete!",
"descNamed": "“{name}” has been translated successfully.",
"descGeneric": "Your document has been translated successfully.",
"downloading": "Downloading…",
"download": "Download translated file",
"newTranslation": "New translation",
"toastOkTitle": "Download complete",
"toastOkDesc": "{name} has been downloaded successfully.",
"toastFailTitle": "Download failed",
"toastFailDesc": "Could not download the translated file."
},
"steps": {
"uploading": "Uploading file…",
"starting": "Starting translation…",
"translating": "Translating…"
},
"errors": {
"jobNotFound": "Translation job not found",
"translationFailed": "Translation failed",
"connectionLost": "Lost connection to the translation service. Check your internet connection and try again."
}
}
},
"landing": {
"originalLabel": "Original",
"translatedLabel": "Translated"
"heroDoc": {
"fileName": "Technical_Brief_HVAC.docx",
"ribbonFile": "File",
"ribbonHome": "Home",
"ribbonInsert": "Insert",
"ribbonLayout": "Layout",
"badgeOriginal": "Original (EN)",
"badgeTranslated": "Translation (FR)",
"caption": "One Word document: English on the left, French on the right — same layout preserved."
}
}
}

View File

@@ -12,7 +12,10 @@
"free": "Gratuit",
"popular": "Populaire",
"month": "mois",
"onRequest": "Sur devis"
"onRequest": "Sur devis",
"monthly": "Mensuel",
"yearly": "Annuel",
"save20": "20 %"
},
"nav": {
"features": "Fonctionnalités",
@@ -62,35 +65,43 @@
"pricing": {
"title": "Tarification simple et transparente",
"subtitle": "Commencez gratuitement, évoluez selon vos besoins.",
"free": {
"name": "Gratuit",
"description": "Pour les besoins ponctuels.",
"price": "0€",
"starter": {
"name": "Starter",
"description": "Pour les particuliers et petits projets.",
"priceMonthly": "9€",
"priceYearly": "7.20€",
"billedYearly": "Facturé 86.40€ / an",
"features": [
"5 documents / mois",
"Word & Excel uniquement",
"Support communautaire"
"50 documents / mois",
"Traductions standards (Google/DeepL)",
"Fichiers jusqu'à 10 Mo",
"Support e-mail"
]
},
"pro": {
"name": "Pro",
"description": "Pour les professionnels exigeants.",
"price": "29€",
"description": "Pour les professionnels en croissance.",
"priceMonthly": "19€",
"priceYearly": "15.20€",
"billedYearly": "Facturé 182.40€ / an",
"features": [
"50 documents / mois",
"Tous formats (PPTX inclus)",
"Modèles IA avancés (GPT-4)",
"Support prioritaire"
"200 documents / mois",
"IA Essentielle (DeepSeek V3)",
"Fichiers jusqu'à 25 Mo",
"Glossaires personnalisés"
]
},
"enterprise": {
"name": "Entreprise",
"business": {
"name": "Business",
"description": "Pour les grandes équipes.",
"priceMonthly": "49€",
"priceYearly": "39.20€",
"billedYearly": "Facturé 470.40€ / an",
"features": [
"Documents illimités",
"Accès API",
"SSO & Sécurité avancée",
"Manager de compte dédié"
"1 000 documents / mois",
"IA Premium (Claude Haiku)",
"Accès API (10K appels)",
"Support dédié"
]
}
},
@@ -103,10 +114,277 @@
"privacy": "Confidentialité",
"terms": "CGU",
"contact": "Contact",
"copyright": "© 2024 Office Translator. Tous droits réservés."
"copyright": "© 2026 Office Translator. Tous droits réservés."
},
"cookieConsent": {
"title": "Cookies sur Office Translator",
"description": "Nous utilisons des cookies strictement nécessaires au fonctionnement du service (session, sécurité, langue). Avec votre accord, nous pouvons aussi utiliser des cookies optionnels pour mesurer laudience et améliorer le produit. Choisissez « Tout accepter » pour les autoriser, ou « Nécessaires uniquement » pour les refuser.",
"acceptAll": "Tout accepter",
"essentialOnly": "Nécessaires uniquement",
"learnMore": "En savoir plus sur notre site",
"footerHint": "Vous pouvez effacer les données du site dans votre navigateur à tout moment pour revoir ce message."
},
"glossaries": {
"title": "Glossaires",
"description": "Gérez la terminologie personnalisée pour vos traductions LLM.",
"createNew": "Créer un glossaire",
"yourGlossaries": "Vos glossaires",
"yourGlossariesDesc": "Créez et gérez des glossaires pour des traductions cohérentes",
"count_one": "{count} glossaire",
"count_other": "{count} glossaires",
"defineTerms": "Définissez des paires de termes pour personnaliser vos traductions LLM",
"empty": "Aucun glossaire",
"emptyDesc": "Créez votre premier glossaire pour personnaliser les traductions",
"aboutTitle": "À propos des glossaires",
"aboutDesc": "Les glossaires vous permettent de définir une terminologie personnalisée. En mode traduction LLM, vos termes seront appliqués pour garantir des traductions cohérentes.",
"aboutFormat": "Format : chaque terme possède une source (original) et une cible (traduction).",
"dialog": {
"title": "Nouveau glossaire",
"description": "Importez un template prédéfini, un fichier avec votre jargon métier, ou saisissez vos termes manuellement.",
"nameLabel": "Nom du glossaire",
"namePlaceholder": "ex : Glossaire Juridique FR-EN",
"tabTemplates": "Templates prédéfinis",
"tabFile": "Importer un fichier",
"tabManual": "Saisie manuelle",
"templatesDesc": "Choisissez un glossaire prédéfini couvrant la terminologie de votre domaine.",
"templatesEmpty": "Aucun template disponible.",
"terms": "termes",
"cancel": "Annuler",
"importing": "Importation…",
"creating": "Création…",
"importBtn": "Importer ({count} termes)",
"createBtn": "Créer ({count} termes)",
"createEmpty": "Créer le glossaire",
"selectPrompt": "Sélectionnez un template",
"dropTitle": "Glissez-déposez votre fichier ici",
"dropOr": "ou cliquez pour parcourir",
"dropFormats": "CSV, XLSX, XLS, ODS • max 500 termes",
"parsing": "Analyse du fichier…",
"termsImported": "{count} termes importés",
"changeFile": "Changer de fichier",
"retry": "Réessayer",
"formatTitle": "Format attendu :",
"formatDesc": "Deux colonnes avec en-têtes :",
"formatNote": "La première ligne peut être un en-tête (source/target) ou directement des données.",
"errorFormat": "Format non supporté. Utilisez CSV, XLSX, XLS ou ODS.",
"errorSize": "Fichier trop volumineux (max {max} Mo). Divisez le fichier.",
"errorEmpty": "Aucun terme valide trouvé. Vérifiez les deux colonnes (source, cible).",
"errorRead": "Impossible de lire le fichier. Vérifiez le format."
},
"toast": {
"created": "Glossaire créé",
"createdDesc": "« {name} » a été créé avec succès.",
"imported": "Template importé",
"importedDesc": "« {name} » a été importé avec succès.",
"updated": "Glossaire mis à jour",
"updatedDesc": "« {name} » a été mis à jour avec succès.",
"deleted": "Glossaire supprimé",
"deletedDesc": "Le glossaire a été supprimé avec succès.",
"error": "Erreur",
"errorCreate": "Impossible de créer le glossaire. Veuillez réessayer.",
"errorImport": "Impossible d'importer le template. Veuillez réessayer.",
"errorUpdate": "Impossible de mettre à jour le glossaire. Veuillez réessayer.",
"errorDelete": "Impossible de supprimer le glossaire. Veuillez réessayer."
},
"card": {
"term": "terme",
"terms": "termes",
"created": "Créé le"
},
"termEditor": {
"sourceTerm": "Terme source",
"targetTranslation": "Traduction cible",
"addTerm": "Ajouter un terme",
"maxReached": "Maximum {max} termes par glossaire atteint."
}
},
"auth": {
"loading": "Chargement…",
"or": "ou",
"brandName": "Office Translator",
"google": {
"continue": "Continuer avec Google",
"signUp": "S'inscrire avec Google",
"connecting": "Connexion…",
"errorGeneric": "Erreur lors de la connexion Google.",
"errorFailed": "La connexion Google a échoué. Veuillez réessayer.",
"devHint": "Pour activer Google : ajoutez {envKey} dans {envFile} (voir {exampleFile})."
},
"login": {
"title": "Bon retour",
"subtitle": "Connectez-vous pour continuer à traduire",
"email": "E-mail",
"password": "Mot de passe",
"emailPlaceholder": "vous@exemple.com",
"forgotPassword": "Mot de passe oublié ?",
"signingIn": "Connexion…",
"signIn": "Se connecter",
"errorTitle": "Erreur de connexion",
"noAccount": "Pas encore de compte ?",
"signUpFree": "Créer un compte gratuitement"
},
"register": {
"title": "Créer un compte",
"subtitle": "Commencez à traduire gratuitement",
"name": "Nom",
"namePlaceholder": "Votre nom",
"email": "Adresse e-mail",
"emailPlaceholder": "vous@exemple.com",
"password": "Mot de passe",
"confirmPassword": "Confirmer le mot de passe",
"createAccount": "Créer mon compte",
"creating": "Création du compte…",
"hasAccount": "Vous avez déjà un compte ?",
"signIn": "Se connecter",
"termsPrefix": "En créant un compte, vous acceptez notre",
"termsService": "conditions d'utilisation du service",
"registerFailed": "L'inscription a échoué",
"errors": {
"nameMin": "Le nom doit contenir au moins 2 caractères",
"emailInvalid": "Adresse e-mail invalide",
"passwordShort": "Mot de passe trop court (minimum 8 caractères)",
"passwordMismatch": "Les mots de passe ne correspondent pas"
},
"passwordStrength": {
"label": "Force",
"weak": "Faible",
"medium": "Moyen",
"strong": "Fort"
},
"toggle": {
"hide": "Masquer",
"show": "Afficher",
"hidePassword": "Masquer le mot de passe",
"showPassword": "Afficher le mot de passe"
}
}
},
"dashboard": {
"nav": {
"overview": "Vue densemble",
"profile": "Mon profil",
"translate": "Traduire",
"apiKeys": "Clés API",
"glossaries": "Glossaires"
},
"header": {
"title": "Tableau de bord",
"subtitle": "Gérez votre API et vos paramètres de traduction",
"toggleMenu": "Ouvrir le menu",
"profileTitle": "Mon profil"
},
"sidebar": {
"theme": "Thème",
"signOut": "Déconnexion",
"backHome": "Retour à laccueil"
},
"tier": {
"free": "Gratuit",
"starter": "Starter",
"pro": "Pro",
"business": "Business",
"enterprise": "Entreprise"
},
"translate": {
"errorNotificationTitle": "Erreur de traduction",
"pageTitle": "Traduire un document",
"pageSubtitle": "Importez un fichier Excel, Word ou PowerPoint, choisissez les langues et le moteur, puis téléchargez le document traduit — mise en page conservée.",
"document": {
"title": "Document",
"descReady": "Fichier prêt — vous pouvez le remplacer ou continuer ci-dessous.",
"descEmpty": "Glissez-déposez ou parcourez vos fichiers. Formats Office Open XML pris en charge.",
"supportedFormats": "Formats pris en charge : Excel (.xlsx), Word (.docx), PowerPoint (.pptx)"
},
"settings": {
"title": "Paramètres de traduction",
"description": "Langues et moteur. Les options LLM nécessitent Pro le cas échéant.",
"howItWorks": "Comment ça marche",
"step1": "Ajoutez votre fichier dans le panneau de gauche (ou au-dessus sur petit écran).",
"step2": "Choisissez la langue source et la langue cible.",
"step3": "Sélectionnez un fournisseur de traduction, puis lancez la tâche.",
"runHint": "Quand vous êtes prêt, nous envoyons le fichier et démarrons la traduction. Vous pouvez annuler pendant lexécution."
},
"actions": {
"translate": "Traduire le document",
"uploading": "Envoi en cours…",
"cancel": "Annuler",
"tryAgain": "Réessayer",
"statusComplete": "Traduction terminée",
"statusIssue": "Problème de traduction",
"statusProgress": "En cours",
"filePrefix": "Fichier :"
},
"trust": {
"zeroRetention": "Aucune conservation des données",
"deletedAfter": "Fichiers supprimés après 60 minutes"
},
"dropzone": {
"title": "Glissez-déposez votre fichier .xlsx, .docx ou .pptx ici",
"subtitle": "ou cliquez pour choisir un fichier",
"uploadAria": "Téléverser un fichier",
"replaceFile": "Remplacer le fichier"
},
"language": {
"loadErrorPrefix": "Impossible de charger les langues :",
"source": "Langue source",
"target": "Langue cible",
"loading": "Chargement…",
"autoDetect": "Détection automatique",
"selectPlaceholder": "Choisir une langue"
},
"provider": {
"loading": "Chargement des fournisseurs…",
"noneConfigured": "Aucun fournisseur nest configuré. Demandez à un administrateur den activer au moins un dans les réglages admin.",
"sectionTitle": "Fournisseur de traduction",
"llmDivider": "LLM · Contextuel",
"llmDividerPro": "Pro",
"upgrade": "Passer à Pro",
"upgradeSuffix": "pour utiliser la traduction par LLM.",
"modelTitle": "Modèle (défini par ladmin) :"
},
"progress": {
"failedTitle": "Échec de la traduction",
"processingFallback": "Traitement…",
"connectionLost": "Connexion perdue. Nouvelle tentative…",
"timeSeconds": "{seconds} s restantes",
"timeMinutes": "{minutes} min restantes",
"timeMixed": "{minutes} min {seconds} s restantes",
"ariaProgress": "Progression de la traduction"
},
"complete": {
"title": "Traduction terminée !",
"descNamed": "« {name} » a été traduit avec succès.",
"descGeneric": "Votre document a été traduit avec succès.",
"downloading": "Téléchargement…",
"download": "Télécharger le fichier traduit",
"newTranslation": "Nouvelle traduction",
"toastOkTitle": "Téléchargement terminé",
"toastOkDesc": "{name} a été téléchargé avec succès.",
"toastFailTitle": "Échec du téléchargement",
"toastFailDesc": "Impossible de télécharger le fichier traduit."
},
"steps": {
"uploading": "Envoi du fichier…",
"starting": "Démarrage de la traduction…",
"translating": "Traduction en cours…"
},
"errors": {
"jobNotFound": "Tâche de traduction introuvable",
"translationFailed": "La traduction a échoué",
"connectionLost": "Connexion au service de traduction perdue. Vérifiez votre connexion internet et réessayez."
}
}
},
"landing": {
"originalLabel": "Original",
"translatedLabel": "Traduit"
"heroDoc": {
"fileName": "Note_technique_CVC.docx",
"ribbonFile": "Fichier",
"ribbonHome": "Accueil",
"ribbonInsert": "Insertion",
"ribbonLayout": "Mise en page",
"badgeOriginal": "Original (EN)",
"badgeTranslated": "Traduction (FR)",
"caption": "Un seul fichier Word : langlais à gauche, le français à droite — la mise en forme reste identique."
}
}
}
}