perf+security: fix build, secure downloads, dedupe translations, refactor i18n
All checks were successful
Deploy to Production / Build and Deploy (push) Successful in 2m49s
All checks were successful
Deploy to Production / Build and Deploy (push) Successful in 2m49s
Frontend:
- Fix Framer Motion / motion-dom build error by pinning framer-motion to
11.18.2 (compatible with React 19 and Next.js 16).
- Add cross-env and build:local script to bypass standalone symlink errors
on Windows without Developer Mode.
- Allow NEXT_OUTPUT=default to disable standalone output for local builds.
- Refactor i18n: split 14,177-line src/lib/i18n.tsx into per-locale,
per-namespace JSON files under src/lib/i18n/messages/.
- Load English synchronously; other locales loaded on demand via dynamic
imports (reduces initial bundle, improves maintainability).
- Remove unused next-intl message files src/messages/en.json and fr.json.
Backend:
- Remove insecure legacy /api/v1/download/{filename} and /api/v1/cleanup/{filename}
endpoints. The job-based /api/v1/download/{job_id} already enforces ownership.
- Deduplicate texts in TranslationService.translate_batch before sending them
to the provider, reducing API calls for repeated strings.
- Pin httpx to <0.28 to fix TestClient incompatibility with starlette 0.35.1.
- Add pytest-cov and ruff dev dependencies/config.
DevOps:
- Remove hardcoded Grafana password from docker-compose.yml and
docker-compose.monitoring.yml; use GRAFANA_PASSWORD env var.
- Change default TRANSLATION_SERVICE from ollama to google in
docker-compose.yml (Ollama is an optional profile).
- Add GRAFANA_PASSWORD to .env.example.
- Add .coverage and frontend/pnpm-workspace.yaml to .gitignore.
Tests:
- Update API versioning tests for removed legacy endpoints.
- Add tests/test_translation_service.py for deduplication behavior.
Verified:
- pnpm run build:local passes.
- uv run pytest tests/test_providers/* tests/test_translation_service.py
tests/test_story_3_5_api_versioning.py tests/test_download_endpoint.py
tests/test_translators/test_excel_translator.py: provider/translator tests
pass; one pre-existing French error-message test still fails (message is
returned in English, unrelated to this change).
This commit is contained in:
48
frontend/src/lib/i18n/messages/fr/admin.json
Normal file
48
frontend/src/lib/i18n/messages/fr/admin.json
Normal file
@@ -0,0 +1,48 @@
|
||||
{
|
||||
"admin.login.title": "Administration",
|
||||
"admin.login.required": "Connexion requise",
|
||||
"admin.login.password": "Mot de passe administrateur",
|
||||
"admin.login.connecting": "Connexion...",
|
||||
"admin.login.access": "Accéder au panneau admin",
|
||||
"admin.login.restricted": "Accès réservé aux administrateurs",
|
||||
"admin.layout.checking": "Vérification de l'authentification...",
|
||||
"admin.dashboard.title": "Dashboard Admin",
|
||||
"admin.dashboard.subtitle": "Panneau de contrôle administrateur",
|
||||
"admin.dashboard.refresh": "Actualiser",
|
||||
"admin.dashboard.refreshTooltip": "Actualiser les données du dashboard",
|
||||
"admin.dashboard.config": "Configuration système",
|
||||
"admin.dashboard.maxFileSize": "Taille max fichier :",
|
||||
"admin.dashboard.translationService": "Service de traduction :",
|
||||
"admin.dashboard.formats": "Formats :",
|
||||
"admin.nav.dashboard": "Dashboard",
|
||||
"admin.nav.users": "Utilisateurs",
|
||||
"admin.nav.pricing": "Tarifs & Stripe",
|
||||
"admin.nav.providers": "Fournisseurs",
|
||||
"admin.nav.system": "Système",
|
||||
"admin.nav.logs": "Logs",
|
||||
"admin.users.title": "Gestion des Utilisateurs",
|
||||
"admin.users.subtitle": "Visualiser et gérer les comptes utilisateurs",
|
||||
"admin.users.planUpdated": "Plan mis à jour",
|
||||
"admin.users.planChanged": "Le plan a été changé vers \\\"{plan}\\\" avec succès.",
|
||||
"admin.users.unknownError": "Erreur inconnue",
|
||||
"admin.users.error": "Erreur",
|
||||
"admin.users.planUpdateError": "Impossible de mettre à jour le plan : {message}",
|
||||
"admin.users.noKeys": "Aucune clé",
|
||||
"admin.users.noKeysDesc": "Cet utilisateur n'a pas de clés API actives.",
|
||||
"admin.users.keysRevoked": "Clés révoquées",
|
||||
"admin.users.keysRevokedDesc": "{count} clé(s) API révoquée(s) avec succès.",
|
||||
"admin.users.revokeError": "Impossible de révoquer les clés : {message}",
|
||||
"admin.users.retry": "Réessayer",
|
||||
"admin.system.title": "Système",
|
||||
"admin.system.subtitle": "Surveiller l'état du système et gérer les ressources",
|
||||
"admin.system.quotas": "Quotas de traduction",
|
||||
"admin.system.resetQuotas": "Réinitialiser les quotas mensuels",
|
||||
"admin.system.resetting": "Réinitialisation...",
|
||||
"admin.system.reset": "Réinitialiser",
|
||||
"admin.system.allOperational": "Tous les systèmes opérationnels",
|
||||
"admin.system.issuesDetected": "Problèmes système détectés",
|
||||
"admin.system.waitingData": "En attente de données...",
|
||||
"admin.system.purging": "Purge...",
|
||||
"admin.system.clean": "Nettoyer",
|
||||
"admin.system.purge": "Purger"
|
||||
}
|
||||
72
frontend/src/lib/i18n/messages/fr/apiKeys.json
Normal file
72
frontend/src/lib/i18n/messages/fr/apiKeys.json
Normal file
@@ -0,0 +1,72 @@
|
||||
{
|
||||
"apiKeys.webhook.title": "Intégration Webhook",
|
||||
"apiKeys.webhook.descriptionBefore": "Passez un paramètre ",
|
||||
"apiKeys.webhook.descriptionAfter": " pour recevoir une requête POST lorsque votre traduction est terminée.",
|
||||
"apiKeys.webhook.codeParam": "webhook_url",
|
||||
"apiKeys.title": "Clés API",
|
||||
"apiKeys.subtitle": "Gérez vos clés API pour l'accès programmatique à l'API de traduction.",
|
||||
"apiKeys.loading": "Chargement...",
|
||||
"apiKeys.sectionTitle": "API & Automatisation",
|
||||
"apiKeys.sectionDesc": "Générez et gérez vos clés API pour les workflows d'automatisation",
|
||||
"apiKeys.keysUsed": "{total} sur {max} clés utilisées",
|
||||
"apiKeys.maxReached": "Nombre maximum de clés atteint. Révoquez une clé pour en générer une nouvelle.",
|
||||
"apiKeys.canGenerate": "Vous pouvez générer {count} clé supplémentaire",
|
||||
"apiKeys.canGeneratePlural": "Vous pouvez générer {count} clés supplémentaires",
|
||||
"apiKeys.generateNew": "Générer une nouvelle clé",
|
||||
"apiKeys.keyRevoked": "Clé révoquée",
|
||||
"apiKeys.keyRevokedDesc": "La clé API a été révoquée avec succès.",
|
||||
"apiKeys.keyNotFound": "Clé introuvable",
|
||||
"apiKeys.keyNotFoundDesc": "La clé API n'existe plus. Elle a peut-être déjà été révoquée.",
|
||||
"apiKeys.error": "Erreur",
|
||||
"apiKeys.revokeError": "Impossible de révoquer la clé API. Veuillez réessayer.",
|
||||
"apiKeys.limitReached": "Limite atteinte",
|
||||
"apiKeys.limitReachedDesc": "Vous avez atteint le maximum de 10 clés API. Révoquez une clé existante pour en générer une nouvelle.",
|
||||
"apiKeys.proRequired": "Fonctionnalité Pro requise",
|
||||
"apiKeys.proRequiredDesc": "Les clés API sont une fonctionnalité Pro. Veuillez mettre à niveau votre compte.",
|
||||
"apiKeys.generateError": "Impossible de générer la clé API. Veuillez réessayer.",
|
||||
"apiKeys.upgrade.title": "Clés API",
|
||||
"apiKeys.upgrade.subtitle": "Automatisez vos traductions avec l'accès API",
|
||||
"apiKeys.upgrade.feat1": "Générez des clés API illimitées",
|
||||
"apiKeys.upgrade.feat2": "Automatisez la traduction de documents",
|
||||
"apiKeys.upgrade.feat3": "Notifications webhook",
|
||||
"apiKeys.upgrade.feat4": "Modes de traduction IA",
|
||||
"apiKeys.upgrade.proFeature": "Les clés API sont une fonctionnalité {pro}. Passez à un forfait supérieur pour débloquer l'automatisation API.",
|
||||
"apiKeys.upgrade.pro": "Pro",
|
||||
"apiKeys.upgrade.cta": "Passer à Pro",
|
||||
"apiKeys.dialog.maxTitle": "Nombre maximum de clés atteint",
|
||||
"apiKeys.dialog.maxDesc": "Vous avez atteint le maximum de 10 clés API. Veuillez révoquer une clé existante avant d'en générer une nouvelle.",
|
||||
"apiKeys.dialog.close": "Fermer",
|
||||
"apiKeys.dialog.generated": "Clé API générée !",
|
||||
"apiKeys.dialog.generatedDesc": "Votre nouvelle clé API a été créée. Copiez-la maintenant — elle ne sera plus affichée.",
|
||||
"apiKeys.dialog.important": "Important :",
|
||||
"apiKeys.dialog.importantDesc": "C'est la seule fois où vous verrez cette clé. Conservez-la en toute sécurité.",
|
||||
"apiKeys.dialog.apiKey": "Clé API",
|
||||
"apiKeys.dialog.name": "Nom :",
|
||||
"apiKeys.dialog.done": "Terminé",
|
||||
"apiKeys.dialog.copied": "J'ai copié la clé",
|
||||
"apiKeys.dialog.generateTitle": "Générer une nouvelle clé API",
|
||||
"apiKeys.dialog.generateDesc": "Créez une nouvelle clé API pour l'accès programmatique à l'API de traduction.",
|
||||
"apiKeys.dialog.keyName": "Nom de la clé (facultatif)",
|
||||
"apiKeys.dialog.keyNamePlaceholder": "ex : Production, Staging",
|
||||
"apiKeys.dialog.keyNameHint": "Un nom descriptif pour vous aider à identifier cette clé plus tard.",
|
||||
"apiKeys.dialog.nameTooLong": "Le nom ne doit pas dépasser {max} caractères",
|
||||
"apiKeys.dialog.nameInvalid": "Le nom ne peut contenir que des lettres, chiffres, espaces, tirets et tirets bas",
|
||||
"apiKeys.dialog.cancel": "Annuler",
|
||||
"apiKeys.dialog.generating": "Génération...",
|
||||
"apiKeys.dialog.generate": "Générer la clé",
|
||||
"apiKeys.table.name": "Nom",
|
||||
"apiKeys.table.prefix": "Préfixe",
|
||||
"apiKeys.table.created": "Créée",
|
||||
"apiKeys.table.lastUsed": "Dernière utilisation",
|
||||
"apiKeys.table.never": "Jamais",
|
||||
"apiKeys.table.actions": "Actions",
|
||||
"apiKeys.table.revoke": "Révoquer",
|
||||
"apiKeys.table.copyPrefix": "Copier le préfixe de la clé",
|
||||
"apiKeys.table.revokeKey": "Révoquer la clé",
|
||||
"apiKeys.revokeDialog.title": "Révoquer la clé API",
|
||||
"apiKeys.revokeDialog.desc": "Êtes-vous sûr de vouloir révoquer la clé « {name} » ? Cette action est irréversible.",
|
||||
"apiKeys.revokeDialog.confirm": "Oui, révoquer",
|
||||
"apiKeys.revokeDialog.cancel": "Annuler",
|
||||
"apiKeys.noKeysGenerated": "Aucune clé générée",
|
||||
"apiKeys.copied": "Copié !"
|
||||
}
|
||||
3
frontend/src/lib/i18n/messages/fr/auth.json
Normal file
3
frontend/src/lib/i18n/messages/fr/auth.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"auth.brandName": "Wordly"
|
||||
}
|
||||
12
frontend/src/lib/i18n/messages/fr/checkout.json
Normal file
12
frontend/src/lib/i18n/messages/fr/checkout.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"checkout.activating": "Activation en cours…",
|
||||
"checkout.activatingDesc": "Nous mettons à jour votre abonnement, veuillez patienter.",
|
||||
"checkout.paymentConfirmed": "Paiement confirmé !",
|
||||
"checkout.subscriptionActivated": "Abonnement activé !",
|
||||
"checkout.planActivated": "Forfait {plan} activé !",
|
||||
"checkout.redirectingToProfile": "Redirection vers votre profil…",
|
||||
"checkout.paymentReceived": "Paiement reçu",
|
||||
"checkout.redirecting": "Redirection…",
|
||||
"checkout.syncError": "Erreur de synchronisation",
|
||||
"checkout.networkError": "Erreur réseau. Votre paiement est confirmé — rechargez votre profil."
|
||||
}
|
||||
4
frontend/src/lib/i18n/messages/fr/common.json
Normal file
4
frontend/src/lib/i18n/messages/fr/common.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"common.loading": "Chargement...",
|
||||
"common.backToHome": "Retour à l'accueil"
|
||||
}
|
||||
25
frontend/src/lib/i18n/messages/fr/context.json
Normal file
25
frontend/src/lib/i18n/messages/fr/context.json
Normal file
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"context.proTitle": "Fonctionnalité Pro",
|
||||
"context.proDesc": "Le contexte et les glossaires professionnels sont disponibles avec les forfaits Pro, Business et Enterprise. Ils permettent d'obtenir des traductions plus précises grâce à des instructions et un vocabulaire spécifiques à votre domaine.",
|
||||
"context.viewPlans": "Voir les forfaits",
|
||||
"context.title": "Contexte & Glossaire",
|
||||
"context.subtitle": "Améliorez la qualité de traduction avec des instructions et un vocabulaire spécifiques à votre domaine.",
|
||||
"context.presets.title": "Glossaires professionnels",
|
||||
"context.presets.desc": "Chargez un glossaire complet avec instructions et terminologie spécialisée",
|
||||
"context.instructions.title": "Instructions de contexte",
|
||||
"context.instructions.desc": "Instructions que l'IA suivra pendant la traduction",
|
||||
"context.instructions.placeholder": "Ex : Vous traduisez des documents techniques HVAC. Utilisez une terminologie d'ingénierie précise...",
|
||||
"context.glossary.title": "Glossaire technique",
|
||||
"context.glossary.desc": "Format : source=cible (un par ligne). Les glossaires chargés via preset sont modifiables.",
|
||||
"context.glossary.terms": "termes dans le glossaire",
|
||||
"context.clearAll": "Tout effacer",
|
||||
"context.saving": "Enregistrement...",
|
||||
"context.save": "Enregistrer",
|
||||
"context.presets.createGlossary": "Créer le glossaire",
|
||||
"context.presets.created": "Glossaire créé",
|
||||
"context.presets.createdDesc": "Le glossaire \\\"{name}\\\" a été créé avec {count} termes.",
|
||||
"context.presets.hint": "Cliquez sur un preset pour créer un glossaire avec des termes spécifiques au domaine. Gérez vos glossaires dans la section Glossaires.",
|
||||
"context.glossary.manage": "Gérer les glossaires",
|
||||
"context.saved": "Enregistré",
|
||||
"context.savedDesc": "Vos instructions de contexte ont été enregistrées."
|
||||
}
|
||||
7
frontend/src/lib/i18n/messages/fr/cookieConsent.json
Normal file
7
frontend/src/lib/i18n/messages/fr/cookieConsent.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"cookieConsent.title": "Cookies sur Wordly",
|
||||
"cookieConsent.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 l'audience et améliorer le produit.",
|
||||
"cookieConsent.acceptAll": "Tout accepter",
|
||||
"cookieConsent.essentialOnly": "Nécessaires uniquement",
|
||||
"cookieConsent.learnMore": "En savoir plus"
|
||||
}
|
||||
115
frontend/src/lib/i18n/messages/fr/dashboard.json
Normal file
115
frontend/src/lib/i18n/messages/fr/dashboard.json
Normal file
@@ -0,0 +1,115 @@
|
||||
{
|
||||
"dashboard.nav.translate": "Traduire",
|
||||
"dashboard.nav.profile": "Mon Profil",
|
||||
"dashboard.nav.settings": "Paramètres",
|
||||
"dashboard.nav.context": "Contexte",
|
||||
"dashboard.nav.services": "Services",
|
||||
"dashboard.nav.apiKeys": "Clés API",
|
||||
"dashboard.nav.glossaries": "Glossaires & Contexte",
|
||||
"dashboard.header.title": "Dashboard",
|
||||
"dashboard.header.subtitle": "Gérez vos traductions",
|
||||
"dashboard.header.toggleMenu": "Menu",
|
||||
"dashboard.header.profileTitle": "Mon profil",
|
||||
"dashboard.sidebar.theme": "Thème",
|
||||
"dashboard.sidebar.signOut": "Déconnexion",
|
||||
"dashboard.sidebar.backHome": "Retour à l'accueil",
|
||||
"dashboard.sidebar.upgradeToPro": "Passer Pro →",
|
||||
"dashboard.translate.pageTitle": "Traduire un document",
|
||||
"dashboard.translate.pageSubtitle": "Importez un fichier et choisissez la langue cible",
|
||||
"dashboard.translate.errorNotificationTitle": "Erreur",
|
||||
"dashboard.translate.dropzone.uploadAria": "Zone de dépôt de fichier",
|
||||
"dashboard.translate.dropzone.title": "Glissez-déposez votre fichier ici",
|
||||
"dashboard.translate.dropzone.subtitle": "ou cliquez pour sélectionner (DOCX, XLSX, PPTX, PDF)",
|
||||
"dashboard.translate.dropzone.replaceFile": "Remplacer le fichier",
|
||||
"dashboard.translate.language.source": "Langue source",
|
||||
"dashboard.translate.language.target": "Langue cible",
|
||||
"dashboard.translate.language.loading": "Chargement des langues…",
|
||||
"dashboard.translate.language.autoDetect": "Auto-détection",
|
||||
"dashboard.translate.language.selectPlaceholder": "Sélectionner…",
|
||||
"dashboard.translate.language.loadErrorPrefix": "Erreur de chargement des langues",
|
||||
"dashboard.translate.provider.loading": "Chargement des fournisseurs…",
|
||||
"dashboard.translate.provider.noneConfigured": "Aucun fournisseur configuré",
|
||||
"dashboard.translate.provider.modelTitle": "Modèle",
|
||||
"dashboard.translate.provider.sectionTitle": "Fournisseur",
|
||||
"dashboard.translate.provider.llmDivider": "IA · Context-Aware",
|
||||
"dashboard.translate.provider.llmDividerPro": "IA · Context-Aware (Pro)",
|
||||
"dashboard.translate.provider.upgrade": "Passer Pro",
|
||||
"dashboard.translate.provider.upgradeSuffix": "pour débloquer la traduction IA",
|
||||
"dashboard.translate.provider.tabStandard": "Standard",
|
||||
"dashboard.translate.provider.tabLLM": "Multi-Modèles IA",
|
||||
"dashboard.translate.translateImages": "Traduire les images",
|
||||
"dashboard.translate.translateImagesDesc": "Extraire et traduire le texte des images (vision requise)",
|
||||
"dashboard.translate.trust.zeroRetention": "Rétention zéro",
|
||||
"dashboard.translate.trust.deletedAfter": "Fichiers supprimés après traitement",
|
||||
"dashboard.translate.actions.uploading": "Chargement…",
|
||||
"dashboard.translate.actions.translate": "Traduire",
|
||||
"dashboard.translate.actions.filePrefix": "Fichier : ",
|
||||
"dashboard.translate.actions.cancel": "Annuler",
|
||||
"dashboard.translate.actions.tryAgain": "Réessayer",
|
||||
"dashboard.translate.steps.uploading": "Chargement du fichier…",
|
||||
"dashboard.translate.steps.starting": "Démarrage de la traduction…",
|
||||
"dashboard.translate.complete.title": "Traduction terminée !",
|
||||
"dashboard.translate.complete.descNamed": "Votre fichier {name} a été traduit avec succès.",
|
||||
"dashboard.translate.complete.descGeneric": "Votre fichier a été traduit avec succès.",
|
||||
"dashboard.translate.complete.downloading": "Téléchargement…",
|
||||
"dashboard.translate.complete.download": "Télécharger",
|
||||
"dashboard.translate.complete.newTranslation": "Nouvelle traduction",
|
||||
"dashboard.translate.complete.toastOkTitle": "Succès",
|
||||
"dashboard.translate.complete.toastOkDesc": "{name} a été téléchargé avec succès.",
|
||||
"dashboard.translate.complete.toastFailTitle": "Échec",
|
||||
"dashboard.translate.complete.toastFailDesc": "La traduction a échoué. Veuillez réessayer.",
|
||||
"dashboard.translate.sourceDocument": "Document source",
|
||||
"dashboard.translate.configuration": "Configuration",
|
||||
"dashboard.translate.translating": "Traduction en cours",
|
||||
"dashboard.translate.liveMonitor": "Moniteur en direct",
|
||||
"dashboard.translate.summary": "Résumé",
|
||||
"dashboard.translate.engine": "Moteur",
|
||||
"dashboard.translate.confidence": "Confiance",
|
||||
"dashboard.translate.cancel": "Annuler",
|
||||
"dashboard.translate.segments": "Segments",
|
||||
"dashboard.translate.characters": "Caractères",
|
||||
"dashboard.translate.elapsed": "Écoulé",
|
||||
"dashboard.translate.segPerMin": "Seg/min",
|
||||
"dashboard.translate.highQuality": "Haute qualité",
|
||||
"dashboard.translate.quality": "Qualité",
|
||||
"dashboard.translate.completed": "Traduction terminée",
|
||||
"dashboard.translate.replace": "Remplacer",
|
||||
"dashboard.translate.pdfMode.title": "Mode de traduction PDF",
|
||||
"dashboard.translate.pdfMode.preserveLayout": "Préserver la mise en page",
|
||||
"dashboard.translate.pdfMode.textOnly": "Texte uniquement",
|
||||
"dashboard.translate.pdfMode.preserveLayoutDesc": "Conserve les images, tableaux et formatage. Idéal pour les PDF simples.",
|
||||
"dashboard.translate.pdfMode.textOnlyDesc": "Traduit tout le texte parfaitement. Sortie propre, sans problème de mise en page.",
|
||||
"dashboard.translate.pipeline.upload": "Upload",
|
||||
"dashboard.translate.pipeline.analyze": "Analyse",
|
||||
"dashboard.translate.pipeline.translate": "Traduction",
|
||||
"dashboard.translate.pipeline.rebuild": "Reconstruction",
|
||||
"dashboard.translate.pipeline.finalize": "Finalisation",
|
||||
"dashboard.translate.progress.processingFallback": "Traitement en cours…",
|
||||
"dashboard.translate.progress.connectionLost": "Connexion perdue. Reconnexion…",
|
||||
"dashboard.translate.progress.failedTitle": "Traduction échouée",
|
||||
"dashboard.translate.error.unexpected": "Une erreur inattendue est survenue. Réessayez.",
|
||||
"dashboard.translate.error.noResult": "La traduction n'a produit aucun résultat. Vérifiez que le document contient du texte, puis réessayez ou choisissez un autre moteur.",
|
||||
"dashboard.translate.error.apiKey": "Clé API invalide ou manquante. Contactez l'administrateur pour configurer les clés.",
|
||||
"dashboard.translate.error.quota": "Limite d'utilisation atteinte. Réessayez dans quelques minutes ou choisissez un autre moteur.",
|
||||
"dashboard.translate.error.timeout": "La connexion au service de traduction a expiré. Vérifiez votre réseau et réessayez.",
|
||||
"dashboard.translate.error.sessionExpired": "La session a expiré. Cliquez sur Réessayer pour relancer la traduction.",
|
||||
"dashboard.translate.error.empty": "Le document semble vide ou ne contient pas de texte traduisible (PDF image ?).",
|
||||
"dashboard.translate.error.unsupported": "Format de fichier non supporté ou fichier corrompu.",
|
||||
"dashboard.translate.error.connection": "Connexion perdue. Vérifiez votre réseau et réessayez.",
|
||||
"dashboard.translate.error.generic": "Traduction échouée : {detail}",
|
||||
"dashboard.translate.error.title": "Traduction échouée",
|
||||
"dashboard.translate.retry": "Réessayer la traduction",
|
||||
"dashboard.translate.newFile": "Nouveau fichier",
|
||||
"dashboard.translate.modeAI": "Mode IA",
|
||||
"dashboard.translate.modeClassic": "Mode Classique",
|
||||
"dashboard.translate.glossaryLLMHint": "Glossaires disponibles en mode IA",
|
||||
"dashboard.translate.submitting": "Envoi en cours...",
|
||||
"dashboard.translate.submit": "Lancer la traduction",
|
||||
"dashboard.translate.noFile": "Déposez d'abord un fichier",
|
||||
"dashboard.translate.noTargetLang": "Sélectionnez une langue cible",
|
||||
"dashboard.topbar.interfaceLabel": "Interface de traduction",
|
||||
"dashboard.topbar.premiumAccess": "Accès Premium",
|
||||
"dashboard.checkoutSyncError": "Erreur lors de la synchronisation du paiement.",
|
||||
"dashboard.networkRefresh": "Erreur réseau. Veuillez rafraîchir la page.",
|
||||
"dashboard.continueToTranslate": "Continuer vers la traduction"
|
||||
}
|
||||
31
frontend/src/lib/i18n/messages/fr/fileUploader.json
Normal file
31
frontend/src/lib/i18n/messages/fr/fileUploader.json
Normal file
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"fileUploader.uploadDocument": "Téléverser un document",
|
||||
"fileUploader.uploadDesc": "Glissez-déposez ou cliquez pour sélectionner un fichier (Excel, Word, PowerPoint)",
|
||||
"fileUploader.dropHere": "Déposez votre fichier ici…",
|
||||
"fileUploader.dragAndDrop": "Glissez-déposez votre document ici",
|
||||
"fileUploader.orClickBrowse": "ou cliquez pour parcourir",
|
||||
"fileUploader.preview": "Aperçu",
|
||||
"fileUploader.translationOptions": "Options de traduction",
|
||||
"fileUploader.configureSettings": "Configurez vos paramètres de traduction",
|
||||
"fileUploader.targetLanguage": "Langue cible",
|
||||
"fileUploader.selectLanguage": "Sélectionner une langue",
|
||||
"fileUploader.translationProvider": "Fournisseur de traduction",
|
||||
"fileUploader.selectProvider": "Sélectionner un fournisseur",
|
||||
"fileUploader.advancedOptions": "Options avancées",
|
||||
"fileUploader.translateImages": "Traduire les images",
|
||||
"fileUploader.translating": "Traduction…",
|
||||
"fileUploader.translateDocument": "Traduire le document",
|
||||
"fileUploader.processing": "Traitement…",
|
||||
"fileUploader.translationError": "Erreur de traduction",
|
||||
"fileUploader.translationComplete": "Traduction terminée !",
|
||||
"fileUploader.translationCompleteDesc": "Votre document a été traduit avec succès tout en préservant la mise en forme.",
|
||||
"fileUploader.download": "Télécharger le document traduit",
|
||||
"fileUploader.webgpuUnsupported": "WebGPU n'est pas pris en charge dans ce navigateur. Veuillez utiliser Chrome 113+ ou Edge 113+.",
|
||||
"fileUploader.webllmNotLoaded": "Modèle WebLLM non chargé. Allez dans Paramètres > Services de traduction pour charger un modèle.",
|
||||
"fileUploader.extracting": "Extraction des textes du document…",
|
||||
"fileUploader.noTranslatable": "Aucun texte traduisible trouvé dans le document",
|
||||
"fileUploader.foundTexts": "{count} textes trouvés à traduire",
|
||||
"fileUploader.translatingItem": "Traduction {current}/{total} : « {preview} »",
|
||||
"fileUploader.reconstructing": "Reconstruction du document…",
|
||||
"fileUploader.translatingLocally": "Traduction locale avec WebLLM…"
|
||||
}
|
||||
14
frontend/src/lib/i18n/messages/fr/forgotPassword.json
Normal file
14
frontend/src/lib/i18n/messages/fr/forgotPassword.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"forgotPassword.enterEmail": "Veuillez entrer votre adresse email",
|
||||
"forgotPassword.error": "Une erreur s'est produite",
|
||||
"forgotPassword.title": "Mot de passe oublié",
|
||||
"forgotPassword.checkEmail": "Vérifiez votre boîte mail",
|
||||
"forgotPassword.subtitle": "Entrez votre email pour recevoir un lien de réinitialisation",
|
||||
"forgotPassword.sentMessage": "Si un compte existe avec cette adresse, un email de réinitialisation a été envoyé.",
|
||||
"forgotPassword.emailLabel": "Adresse email",
|
||||
"forgotPassword.emailPlaceholder": "vous@exemple.com",
|
||||
"forgotPassword.sending": "Envoi en cours...",
|
||||
"forgotPassword.sendLink": "Envoyer le lien de réinitialisation",
|
||||
"forgotPassword.backToLogin": "Retour à la connexion",
|
||||
"forgotPassword.loading": "Chargement..."
|
||||
}
|
||||
189
frontend/src/lib/i18n/messages/fr/glossaries.json
Normal file
189
frontend/src/lib/i18n/messages/fr/glossaries.json
Normal file
@@ -0,0 +1,189 @@
|
||||
{
|
||||
"glossaries.yourGlossaries": "Vos glossaires",
|
||||
"glossaries.title": "Glossaires & Contexte",
|
||||
"glossaries.description": "Gérez vos glossaires et instructions de contexte pour des traductions plus précises.",
|
||||
"glossaries.createNew": "Créer nouveau",
|
||||
"glossaries.empty": "Aucun glossaire",
|
||||
"glossaries.emptyDesc": "Créez votre premier glossaire ou chargez un préréglage professionnel ci-dessus",
|
||||
"glossaries.defineTerms": "termes",
|
||||
"glossaries.aboutTitle": "À propos des glossaires",
|
||||
"glossaries.aboutDesc": "Les glossaires vous permettent de définir des traductions exactes pour des termes spécifiques. Lors de la traduction, les termes du glossaire garantissent des traductions cohérentes et précises.",
|
||||
"glossaries.aboutFormat": "Chaque terme a un mot source et des traductions en plusieurs langues. Sélectionnez un glossaire dans la page de traduction pour l'appliquer.",
|
||||
"glossaries.toast.created": "Glossaire créé",
|
||||
"glossaries.toast.createdDesc": "Le glossaire \\\"{name}\\\" a été créé.",
|
||||
"glossaries.toast.imported": "Glossaire importé",
|
||||
"glossaries.toast.importedDesc": "Le glossaire \\\"{name}\\\" a été importé.",
|
||||
"glossaries.toast.updated": "Glossaire mis à jour",
|
||||
"glossaries.toast.updatedDesc": "Le glossaire \\\"{name}\\\" a été mis à jour.",
|
||||
"glossaries.toast.deleted": "Glossaire supprimé",
|
||||
"glossaries.toast.deletedDesc": "Le glossaire a été supprimé.",
|
||||
"glossaries.toast.error": "Erreur",
|
||||
"glossaries.toast.errorCreate": "Impossible de créer le glossaire",
|
||||
"glossaries.toast.errorImport": "Impossible d'importer le glossaire",
|
||||
"glossaries.toast.errorUpdate": "Impossible de mettre à jour le glossaire",
|
||||
"glossaries.toast.errorDelete": "Impossible de supprimer le glossaire",
|
||||
"glossaries.dialog.title": "Nouveau glossaire",
|
||||
"glossaries.dialog.description": "Créez un glossaire pour vos traductions",
|
||||
"glossaries.dialog.nameLabel": "Nom",
|
||||
"glossaries.dialog.namePlaceholder": "Mon glossaire",
|
||||
"glossaries.dialog.tabTemplates": "Modèles",
|
||||
"glossaries.dialog.tabFile": "Fichier",
|
||||
"glossaries.dialog.tabManual": "Manuel",
|
||||
"glossaries.dialog.cancel": "Annuler",
|
||||
"glossaries.dialog.creating": "Création…",
|
||||
"glossaries.dialog.importing": "Import…",
|
||||
"glossaries.dialog.importBtn": "Importer",
|
||||
"glossaries.dialog.selectPrompt": "Sélectionner",
|
||||
"glossaries.dialog.createBtn": "Créer",
|
||||
"glossaries.dialog.createEmpty": "Créer vide",
|
||||
"glossaries.dialog.terms": "termes",
|
||||
"glossaries.dialog.templatesDesc": "Choisissez un modèle prédéfini",
|
||||
"glossaries.dialog.templatesEmpty": "Aucun modèle disponible",
|
||||
"glossaries.dialog.dropTitle": "Glissez un fichier CSV ici",
|
||||
"glossaries.dialog.dropOr": "ou",
|
||||
"glossaries.dialog.dropFormats": "CSV, TSV, TXT",
|
||||
"glossaries.termEditor.addTerm": "Ajouter un terme",
|
||||
"glossaries.termEditor.maxReached": "Maximum {max} termes par glossaire atteint.",
|
||||
"glossaries.dialog.formatTitle": "Format",
|
||||
"glossaries.dialog.formatDesc": "source,target (un par ligne)",
|
||||
"glossaries.dialog.formatNote": "Première ligne ignorée si en-tête détecté",
|
||||
"glossaries.dialog.errorFormat": "Format non supporté",
|
||||
"glossaries.dialog.errorSize": "Fichier trop volumineux",
|
||||
"glossaries.dialog.errorEmpty": "Fichier vide",
|
||||
"glossaries.dialog.errorRead": "Erreur de lecture",
|
||||
"glossaries.dialog.parsing": "Analyse…",
|
||||
"glossaries.dialog.termsImported": "termes importés",
|
||||
"glossaries.dialog.changeFile": "Changer le fichier",
|
||||
"glossaries.dialog.retry": "Réessayer",
|
||||
"glossaries.edit.title": "Modifier le glossaire",
|
||||
"glossaries.edit.description": "Modifiez le nom, la paire de langues et les termes du glossaire.",
|
||||
"glossaries.edit.nameLabel": "Nom du glossaire",
|
||||
"glossaries.edit.namePlaceholder": "Entrez le nom du glossaire...",
|
||||
"glossaries.edit.sourceLang": "Langue source",
|
||||
"glossaries.edit.targetLang": "Langue cible",
|
||||
"glossaries.edit.termsLabel": "Termes ({count} valides)",
|
||||
"glossaries.edit.exportCsv": "Exporter CSV",
|
||||
"glossaries.edit.importCsv": "Importer CSV",
|
||||
"glossaries.edit.cancel": "Annuler",
|
||||
"glossaries.edit.saving": "Enregistrement...",
|
||||
"glossaries.edit.saveChanges": "Enregistrer les modifications",
|
||||
"glossaries.edit.importFailedTitle": "Échec de l'importation",
|
||||
"glossaries.edit.importFailedMaxDesc": "Le CSV contient {count} termes, le maximum est de {max}. Veuillez réduire le nombre de termes.",
|
||||
"glossaries.edit.importSuccessTitle": "Importation réussie",
|
||||
"glossaries.edit.importSuccessDesc": "{count} termes importés avec succès.",
|
||||
"glossaries.edit.importFailedEmptyDesc": "Aucun terme valide trouvé dans le fichier CSV.",
|
||||
"glossaries.edit.importFailedReadDesc": "Impossible de lire le fichier CSV.",
|
||||
"glossaries.delete.title": "Supprimer le glossaire",
|
||||
"glossaries.delete.description": "Êtes-vous sûr de vouloir supprimer ce glossaire ?",
|
||||
"glossaries.delete.warning": "Cette action est irréversible",
|
||||
"glossaries.delete.warningDesc": "Toutes les paires de termes seront définitivement supprimées.",
|
||||
"glossaries.delete.cancel": "Annuler",
|
||||
"glossaries.delete.deleting": "Suppression...",
|
||||
"glossaries.delete.deleteBtn": "Supprimer",
|
||||
"glossaries.upgrade.title": "Glossaires",
|
||||
"glossaries.upgrade.description": "Personnalisez vos traductions avec une terminologie personnalisée",
|
||||
"glossaries.upgrade.feature1": "Créez plusieurs glossaires",
|
||||
"glossaries.upgrade.feature2": "Définissez des paires de termes source→cible",
|
||||
"glossaries.upgrade.feature3": "Importez/exportez via CSV",
|
||||
"glossaries.upgrade.feature4": "Appliquez aux traductions LLM",
|
||||
"glossaries.upgrade.proFeatureBefore": "Les glossaires sont une fonctionnalité ",
|
||||
"glossaries.upgrade.proFeatureAfter": ". Passez à un forfait supérieur pour débloquer la terminologie personnalisée.",
|
||||
"glossaries.upgrade.proLabel": "Pro",
|
||||
"glossaries.upgrade.upgradeBtn": "Passer à Pro",
|
||||
"glossaries.loading": "Chargement...",
|
||||
"glossaries.howItWorks.title": "Comment ces paramètres sont utilisés",
|
||||
"glossaries.howItWorks.step1Title": "Configurez ici",
|
||||
"glossaries.howItWorks.step1Desc": "Rédigez vos instructions de contexte ou créez/importez un glossaire de termes.",
|
||||
"glossaries.howItWorks.step2Title": "Activez dans Traduire",
|
||||
"glossaries.howItWorks.step2Desc": "Sur la page de traduction, dans la colonne de droite, sélectionnez votre glossaire.",
|
||||
"glossaries.howItWorks.warning": "Les instructions de contexte s'appliquent automatiquement à toutes vos traductions IA une fois enregistrées. Les glossaires doivent être sélectionnés manuellement sur la page Traduire.",
|
||||
"glossaries.howItWorks.goToTranslate": "Aller à Traduire",
|
||||
"glossaries.status.unsaved": "Non enregistré",
|
||||
"glossaries.status.active": "Actif · s'applique à toutes les traductions IA",
|
||||
"glossaries.status.inactive": "Inactif",
|
||||
"glossaries.instructions.whatForBold": "À quoi ça sert ?",
|
||||
"glossaries.instructions.whatForDesc": "Ces instructions sont envoyées automatiquement à l'IA avant chaque traduction, sans que vous ayez besoin de faire quoi que ce soit sur la page Traduire. Utilisez-les pour guider le style, le registre ou la terminologie générale.",
|
||||
"glossaries.instructions.example": "Exemple : « Vous traduisez des rapports financiers. Soyez formel, précis et conservez tous les chiffres. »",
|
||||
"glossaries.instructions.charCount": "{count} caractères",
|
||||
"glossaries.instructions.emptyHint": "Vide — aucune instruction envoyée à l'IA",
|
||||
"glossaries.instructions.clearAll": "Tout effacer",
|
||||
"glossaries.instructions.saving": "Enregistrement…",
|
||||
"glossaries.instructions.saved": "Enregistré",
|
||||
"glossaries.presets.whatForBold": "À quoi ça sert ?",
|
||||
"glossaries.presets.whatForDesc": "Cliquer sur une carte crée un glossaire pré-rempli avec les termes spécialisés du domaine. Ce glossaire apparaîtra dans vos glossaires ci-dessous, et vous pourrez le sélectionner manuellement sur la page Traduire pour forcer des traductions de termes précis.",
|
||||
"glossaries.presets.clickHint": "Cliquez sur une carte → glossaire créé → sélectionnez-le dans Traduire",
|
||||
"glossaries.presets.creating": "Création…",
|
||||
"glossaries.presets.alreadyImported": "Déjà importé",
|
||||
"glossaries.presets.it.title": "IT / Logiciel",
|
||||
"glossaries.presets.it.desc": "Développement, infrastructure, DevOps",
|
||||
"glossaries.presets.legal.title": "Juridique / Contrats",
|
||||
"glossaries.presets.legal.desc": "Droit des affaires, contentieux",
|
||||
"glossaries.presets.medical.title": "Médical / Santé",
|
||||
"glossaries.presets.medical.desc": "Pharmacologie, chirurgie, diagnostic",
|
||||
"glossaries.presets.finance.title": "Finance / Comptabilité",
|
||||
"glossaries.presets.finance.desc": "IFRS, bilans, fiscalité",
|
||||
"glossaries.presets.marketing.title": "Marketing / Publicité",
|
||||
"glossaries.presets.marketing.desc": "Digital, branding, analytics",
|
||||
"glossaries.presets.hr.title": "RH / Ressources Humaines",
|
||||
"glossaries.presets.hr.desc": "Contrats, politiques, recrutement",
|
||||
"glossaries.presets.scientific.title": "Scientifique / Recherche",
|
||||
"glossaries.presets.scientific.desc": "Publications, thèses, articles",
|
||||
"glossaries.presets.ecommerce.title": "E-commerce / Vente",
|
||||
"glossaries.presets.ecommerce.desc": "Boutiques en ligne, catalogues, CRM",
|
||||
"glossaries.grid.title": "Vos",
|
||||
"glossaries.grid.titleHighlight": "glossaires",
|
||||
"glossaries.grid.countWithAction": "{count} glossaire({plural}) — cliquez sur une carte pour la modifier",
|
||||
"glossaries.grid.emptyAction": "Créez votre premier glossaire ou importez un preset ci-dessus",
|
||||
"glossaries.grid.activeTranslation": "Traduction active :",
|
||||
"glossaries.grid.goToTranslate": "Aller à Traduire pour activer",
|
||||
"glossaries.badge.compatible": "Compatible",
|
||||
"glossaries.badge.otherTarget": "Autre cible",
|
||||
"glossaries.card.editTerms": "Modifier les termes",
|
||||
"glossaries.card.created": "Créé",
|
||||
"glossaries.card.term": "terme",
|
||||
"glossaries.card.delete": "Supprimer",
|
||||
"glossaries.grid.searchPlaceholder": "Rechercher un glossaire…",
|
||||
"glossaries.grid.noResults": "Aucun résultat pour cette recherche.",
|
||||
"glossaries.detail.backToList": "Retour aux glossaires",
|
||||
"glossaries.detail.save": "Enregistrer",
|
||||
"glossaries.detail.savedTitle": "Enregistré",
|
||||
"glossaries.detail.savedDesc": "Le glossaire a été mis à jour.",
|
||||
"glossaries.detail.settingsTitle": "Paramètres",
|
||||
"glossaries.detail.sourceLang": "Langue source",
|
||||
"glossaries.detail.targetLang": "Langue cible",
|
||||
"glossaries.detail.termsTitle": "Termes",
|
||||
"glossaries.detail.terms": "termes",
|
||||
"glossaries.detail.searchTerms": "Filtrer…",
|
||||
"glossaries.detail.noTerms": "Aucun terme pour l'instant.",
|
||||
"glossaries.detail.addFirstTerm": "Ajouter le premier terme",
|
||||
"glossaries.detail.addTerm": "Ajouter un terme",
|
||||
"glossaries.detail.maxReached": "Limite maximale atteinte",
|
||||
"glossaries.detail.source": "Source",
|
||||
"glossaries.detail.target": "Cible",
|
||||
"glossaries.detail.sourcePlaceholder": "terme source",
|
||||
"glossaries.detail.targetPlaceholder": "terme cible",
|
||||
"glossaries.detail.csvTitle": "CSV",
|
||||
"glossaries.detail.csvDesc": "Exportez vos termes en CSV ou importez-en de nouveaux (remplace la liste actuelle).",
|
||||
"glossaries.detail.export": "Exporter",
|
||||
"glossaries.detail.import": "Importer",
|
||||
"glossaries.detail.dangerTitle": "Zone danger",
|
||||
"glossaries.detail.dangerDesc": "La suppression est définitive. Tous les termes associés seront perdus.",
|
||||
"glossaries.detail.deleteGlossary": "Supprimer ce glossaire",
|
||||
"glossaries.detail.confirmDelete": "Confirmer la suppression ?",
|
||||
"glossaries.detail.confirm": "Confirmer",
|
||||
"glossaries.detail.cancel": "Annuler",
|
||||
"glossaries.detail.sourceLangNote": "'La source originale est en francais. Pour les autres langues, on lit le champ translations du terme (si disponible).'",
|
||||
"glossaries.detail.sourceLocked": "fixé",
|
||||
"glossaries.detail.sourceLockedNote": "Les templates ne stockent la source qu'en français. Le multilingue source est sur la roadmap.",
|
||||
"glossaries.detail.targetLangNote": "Choisissez une langue pour voir les traductions correspondantes, ou « Multilingue » pour la valeur par défaut.",
|
||||
"glossaries.detail.notFoundTitle": "Glossaire introuvable",
|
||||
"glossaries.detail.notFoundDesc": "Ce glossaire n'existe pas ou vous n'y avez pas accès.",
|
||||
"glossaries.detail.maxTermsTitle": "Limite atteinte",
|
||||
"glossaries.detail.maxTermsDesc": "Maximum {max} termes par glossaire.",
|
||||
"glossaries.detail.importEmptyTitle": "Fichier vide",
|
||||
"glossaries.detail.importEmptyDesc": "Aucun terme détecté dans ce fichier.",
|
||||
"glossaries.detail.importedTitle": "Importé",
|
||||
"glossaries.detail.importedDesc": "{count} termes importés.",
|
||||
"glossaries.detail.importErrorTitle": "Erreur de lecture",
|
||||
"glossaries.detail.importErrorDesc": "Impossible de lire le fichier."
|
||||
}
|
||||
60
frontend/src/lib/i18n/messages/fr/index.ts
Normal file
60
frontend/src/lib/i18n/messages/fr/index.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
// Auto-generated by scripts/generate_i18n_index.py
|
||||
// Merges all namespace JSON files for locale "fr".
|
||||
|
||||
import admin from "./admin.json";
|
||||
import apiKeys from "./apiKeys.json";
|
||||
import auth from "./auth.json";
|
||||
import checkout from "./checkout.json";
|
||||
import common from "./common.json";
|
||||
import context from "./context.json";
|
||||
import cookieConsent from "./cookieConsent.json";
|
||||
import dashboard from "./dashboard.json";
|
||||
import fileUploader from "./fileUploader.json";
|
||||
import forgotPassword from "./forgotPassword.json";
|
||||
import glossaries from "./glossaries.json";
|
||||
import landing from "./landing.json";
|
||||
import langSelector from "./langSelector.json";
|
||||
import layout from "./layout.json";
|
||||
import login from "./login.json";
|
||||
import memento from "./memento.json";
|
||||
import pricing from "./pricing.json";
|
||||
import profile from "./profile.json";
|
||||
import providerSelector from "./providerSelector.json";
|
||||
import providerTheme from "./providerTheme.json";
|
||||
import register from "./register.json";
|
||||
import resetPassword from "./resetPassword.json";
|
||||
import services from "./services.json";
|
||||
import settings from "./settings.json";
|
||||
import translate from "./translate.json";
|
||||
import translateComplete from "./translateComplete.json";
|
||||
|
||||
const messages: Record<string, string> = {
|
||||
...admin,
|
||||
...apiKeys,
|
||||
...auth,
|
||||
...checkout,
|
||||
...common,
|
||||
...context,
|
||||
...cookieConsent,
|
||||
...dashboard,
|
||||
...fileUploader,
|
||||
...forgotPassword,
|
||||
...glossaries,
|
||||
...landing,
|
||||
...langSelector,
|
||||
...layout,
|
||||
...login,
|
||||
...memento,
|
||||
...pricing,
|
||||
...profile,
|
||||
...providerSelector,
|
||||
...providerTheme,
|
||||
...register,
|
||||
...resetPassword,
|
||||
...services,
|
||||
...settings,
|
||||
...translate,
|
||||
...translateComplete,
|
||||
};
|
||||
|
||||
export default messages;
|
||||
152
frontend/src/lib/i18n/messages/fr/landing.json
Normal file
152
frontend/src/lib/i18n/messages/fr/landing.json
Normal file
@@ -0,0 +1,152 @@
|
||||
{
|
||||
"landing.nav.why": "Pourquoi nous ?",
|
||||
"landing.nav.formats": "Formats",
|
||||
"landing.nav.pricing": "Tarification",
|
||||
"landing.nav.login": "Connexion",
|
||||
"landing.nav.startFree": "Essai gratuit",
|
||||
"landing.hero.tag": "IA Documentaire Professionnelle",
|
||||
"landing.hero.titleLine1": "Traduisez vos documents.",
|
||||
"landing.hero.titleLine2": "Mise en forme parfaite.",
|
||||
"landing.hero.description": "Le seul traducteur qui préserve les SmartArt, graphiques, tables des matières, formes, en-têtes et pieds de page — exactement tels qu'ils étaient.",
|
||||
"landing.hero.ctaMain": "Essai gratuit — 2 docs/mois",
|
||||
"landing.hero.ctaSec": "Voir les offres",
|
||||
"landing.hero.deleted": "Fichiers supprimés après 60 min",
|
||||
"landing.hero.noHidden": "Pas de prix cachés",
|
||||
"landing.hero.preview": "Aperçu avant paiement",
|
||||
"landing.hero.formattedOk": "Formatage OK",
|
||||
"landing.hero.aiActive": "Traduction IA active",
|
||||
"landing.steps.title": "Comment ça marche ?",
|
||||
"landing.steps.subtitle": "Trois étapes. Zéro perte de format.",
|
||||
"landing.steps.step1.num": "01",
|
||||
"landing.steps.step1.title": "Déposez votre fichier",
|
||||
"landing.steps.step1.desc": "Glissez-déposez votre document Excel, Word, PowerPoint ou PDF.",
|
||||
"landing.steps.step2.num": "02",
|
||||
"landing.steps.step2.title": "Choisissez la langue",
|
||||
"landing.steps.step2.desc": "Sélectionnez la langue cible et le moteur de traduction — classique ou IA contextuelle.",
|
||||
"landing.steps.step3.num": "03",
|
||||
"landing.steps.step3.title": "Téléchargez le résultat",
|
||||
"landing.steps.step3.desc": "Récupérez votre document traduit avec un formatage identique à l'original.",
|
||||
"landing.features.tag": "Moteur de Traduction IA",
|
||||
"landing.features.title": "La traduction qui comprend votre métier",
|
||||
"landing.features.description": "Nos modèles IA analysent le contexte, respectent votre terminologie et traduisent même le texte dans vos images.",
|
||||
"landing.features.context.title": "Contexte métier",
|
||||
"landing.features.context.desc": "Décrivez votre domaine et obtenez des traductions adaptées, pas génériques.",
|
||||
"landing.features.glossary.title": "Glossaires métier",
|
||||
"landing.features.glossary.desc": "Définissez vos termes techniques. CTA restera « Air Handling Unit », jamais « Call To Action ».",
|
||||
"landing.features.vision.title": "Vision sur images",
|
||||
"landing.features.vision.desc": "Le texte incrusté dans vos images, diagrammes et graphiques est détecté et traduit.",
|
||||
"landing.features.demo.source": "Source (FR)",
|
||||
"landing.features.demo.google": "Google Translate",
|
||||
"landing.features.demo.ours": "Notre IA",
|
||||
"landing.layout.title": "Votre mise en forme,",
|
||||
"landing.layout.title2": "parfaitement préservée",
|
||||
"landing.layout.subtitle": "Les autres traducteurs détruisent votre mise en page. Pas nous.",
|
||||
"landing.layout.p1.title": "SmartArt et diagrammes",
|
||||
"landing.layout.p1.desc": "Organigrammes, flux, hiérarchies — tout traduit à l'identique.",
|
||||
"landing.layout.p2.title": "Tables des matières",
|
||||
"landing.layout.p2.desc": "Entrées de TDM, numéros et renvois mis à jour correctement.",
|
||||
"landing.layout.p3.title": "Graphiques",
|
||||
"landing.layout.p3.desc": "Titres, étiquettes, légendes et séries — tout est traduit.",
|
||||
"landing.layout.p4.title": "Formes et zones de texte",
|
||||
"landing.layout.p4.desc": "Rectangles, blocs arrondis, légendes — localisé partout.",
|
||||
"landing.layout.p5.title": "En-têtes et pieds de page",
|
||||
"landing.layout.p5.desc": "En-têtes, pieds de page et notes ne sont jamais oubliés.",
|
||||
"landing.layout.p6.title": "130+ langues",
|
||||
"landing.layout.p6.desc": "Google Traduction, DeepL et moteurs IA haute performance.",
|
||||
"landing.formats.title": "Chaque format,",
|
||||
"landing.formats.title2": "chaque élément",
|
||||
"landing.formats.subtitle": "Nous traduisons ce que les autres oublient. Votre entreprise mérite une documentation irréprochable.",
|
||||
"landing.formats.word.name": "Word",
|
||||
"landing.formats.word.i1": "Paragraphes et titres",
|
||||
"landing.formats.word.i2": "Tableaux et graphiques",
|
||||
"landing.formats.word.i3": "Diagrammes SmartArt",
|
||||
"landing.formats.word.i4": "Table des matières",
|
||||
"landing.formats.word.i5": "En-têtes et pieds",
|
||||
"landing.formats.word.i6": "Formes et zones de texte",
|
||||
"landing.formats.word.i7": "Notes de bas de page",
|
||||
"landing.formats.excel.name": "Excel",
|
||||
"landing.formats.excel.i1": "Valeurs de cellules",
|
||||
"landing.formats.excel.i2": "Noms de feuilles",
|
||||
"landing.formats.excel.i3": "Graphiques et étiquettes",
|
||||
"landing.formats.excel.i4": "En-têtes et pieds",
|
||||
"landing.formats.excel.i5": "Cellules fusionnées",
|
||||
"landing.formats.pptx.name": "PowerPoint",
|
||||
"landing.formats.pptx.i1": "Texte et notes",
|
||||
"landing.formats.pptx.i2": "Graphiques et diagrammes",
|
||||
"landing.formats.pptx.i3": "Formes et zones de texte",
|
||||
"landing.formats.pptx.i4": "Masques de diapositives",
|
||||
"landing.formats.pptx.i5": "Animations préservées",
|
||||
"landing.formats.pdf.name": "PDF",
|
||||
"landing.formats.pdf.i1": "PDF textuels",
|
||||
"landing.formats.pdf.i2": "Mise en page préservée",
|
||||
"landing.formats.pdf.i3": "Images conservées",
|
||||
"landing.formats.pdf.i4": "Tableaux maintenus",
|
||||
"landing.formats.pdf.i5": "Sortie DOCX ou PDF",
|
||||
"landing.pricing.title": "Des prix simples et honnêtes",
|
||||
"landing.pricing.subtitle": "Ce que vous voyez est ce que vous payez. Aucun frais caché.",
|
||||
"landing.pricing.monthly": "Mensuel",
|
||||
"landing.pricing.annual": "Annuel",
|
||||
"landing.pricing.bestValue": "Le plus populaire",
|
||||
"landing.pricing.month": "/mois",
|
||||
"landing.pricing.footer": "Le prix affiché est le prix que vous payez. Aucun frais caché après traduction.",
|
||||
"landing.pricing.starter.name": "Starter",
|
||||
"landing.pricing.starter.desc": "Pour les particuliers et petits projets",
|
||||
"landing.pricing.starter.f1": "50 documents / mois",
|
||||
"landing.pricing.starter.f2": "Jusqu'à 50 pages par doc",
|
||||
"landing.pricing.starter.f3": "Google Traduction + DeepL",
|
||||
"landing.pricing.starter.f4": "Fichiers jusqu'à 10 Mo",
|
||||
"landing.pricing.starter.cta": "Commencer",
|
||||
"landing.pricing.pro.name": "Pro",
|
||||
"landing.pricing.pro.desc": "Pour les professionnels exigeants",
|
||||
"landing.pricing.pro.f1": "200 documents / mois",
|
||||
"landing.pricing.pro.f2": "Jusqu'à 200 pages par doc",
|
||||
"landing.pricing.pro.f3": "Traduction par IA",
|
||||
"landing.pricing.pro.f4": "Google + DeepL inclus",
|
||||
"landing.pricing.pro.f5": "Glossaires et prompts",
|
||||
"landing.pricing.pro.f6": "Support prioritaire",
|
||||
"landing.pricing.pro.cta": "Essayer Pro",
|
||||
"landing.pricing.business.name": "Business",
|
||||
"landing.pricing.business.desc": "Pour les équipes avec des besoins élevés",
|
||||
"landing.pricing.business.f1": "1 000 documents / mois",
|
||||
"landing.pricing.business.f2": "Jusqu'à 500 pages par doc",
|
||||
"landing.pricing.business.f3": "IA Premium (Claude)",
|
||||
"landing.pricing.business.f4": "Tous les fournisseurs + API",
|
||||
"landing.pricing.business.f5": "Webhooks et automatisation",
|
||||
"landing.pricing.business.f6": "5 postes d'équipe",
|
||||
"landing.pricing.business.cta": "Nous contacter",
|
||||
"landing.pricing.free.name": "Gratuit",
|
||||
"landing.pricing.free.desc": "Idéal pour découvrir l'application",
|
||||
"landing.pricing.free.cta": "Choisir ce plan",
|
||||
"landing.pricing.enterprise.name": "Entreprise",
|
||||
"landing.pricing.enterprise.desc": "Solutions sur mesure pour les grandes organisations",
|
||||
"landing.pricing.enterprise.cta": "Nous contacter",
|
||||
"landing.cta.title": "Commencez à traduire en 30 secondes",
|
||||
"landing.cta.subtitle": "Aucune carte bancaire requise. Essayez gratuitement dès maintenant et redonnez vie à vos documents multilingues.",
|
||||
"landing.cta.button": "Créer un compte gratuit",
|
||||
"landing.cta.secure": "Sécurisé par chiffrement AES-256",
|
||||
"landing.footer.desc": "Spécialiste de la traduction documentaire intelligente. Nous marions l'art de la mise en page et la science de l'IA contextuelle.",
|
||||
"landing.footer.product": "Produit",
|
||||
"landing.footer.resources": "Ressources",
|
||||
"landing.footer.legal": "Légal",
|
||||
"landing.footer.rights": "© 2026 Wordly.art — Tous droits réservés.",
|
||||
"landing.hero.contextEngine": "Traduction détectée : Terme technique de maintenance pour systèmes CVC...",
|
||||
"landing.hero.liveAnalysis": "Analyse en direct",
|
||||
"landing.hero.termsDetected": "termes détectés",
|
||||
"landing.steps.process": "PROCESSUS",
|
||||
"landing.translate.newProject": "Nouveau projet",
|
||||
"landing.translate.title": "Traduire un document",
|
||||
"landing.translate.subtitle": "Importez un fichier et choisissez la langue cible",
|
||||
"landing.translate.sourceDocument": "Document source",
|
||||
"landing.translate.configuration": "Configuration",
|
||||
"landing.translate.sourceLang": "Langue source",
|
||||
"landing.translate.targetLang": "Langue cible",
|
||||
"landing.translate.provider": "Fournisseur",
|
||||
"landing.translate.startTranslation": "Lancer la traduction",
|
||||
"landing.translate.zeroRetention": "Rétention zéro",
|
||||
"landing.translate.filesDeleted": "Fichiers supprimés après traitement",
|
||||
"landing.translate.dropHere": "Glissez-déposez ici",
|
||||
"landing.translate.supportedFormats": "Fichiers DOCX, XLSX, PPTX ou PDF supportés",
|
||||
"landing.translate.aiAnalysis": "Analyse IA Active",
|
||||
"landing.translate.processing": "Traitement en cours",
|
||||
"landing.translate.preservingLayout": "Votre mise en page est en cours de préservation"
|
||||
}
|
||||
7
frontend/src/lib/i18n/messages/fr/langSelector.json
Normal file
7
frontend/src/lib/i18n/messages/fr/langSelector.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"langSelector.search": "Rechercher…",
|
||||
"langSelector.noResults": "Aucun résultat",
|
||||
"langSelector.source": "Source",
|
||||
"langSelector.target": "Cible",
|
||||
"langSelector.swap": "Inverser"
|
||||
}
|
||||
5
frontend/src/lib/i18n/messages/fr/layout.json
Normal file
5
frontend/src/lib/i18n/messages/fr/layout.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"layout.nav.apiAccess": "Accès API",
|
||||
"layout.footer.terms": "CGU",
|
||||
"layout.footer.privacy": "Confidentialité"
|
||||
}
|
||||
18
frontend/src/lib/i18n/messages/fr/login.json
Normal file
18
frontend/src/lib/i18n/messages/fr/login.json
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"login.errorTitle": "Erreur de connexion",
|
||||
"login.welcomeBack": "Bienvenue",
|
||||
"login.signInToContinue": "Connectez-vous pour continuer à traduire",
|
||||
"login.email": "Email",
|
||||
"login.emailPlaceholder": "vous@exemple.com",
|
||||
"login.password": "Mot de passe",
|
||||
"login.forgotPassword": "Mot de passe oublié ?",
|
||||
"login.passwordPlaceholder": "••••••••",
|
||||
"login.signingIn": "Connexion en cours...",
|
||||
"login.signIn": "Se connecter",
|
||||
"login.noAccount": "Pas encore de compte ?",
|
||||
"login.signUpFree": "Créer un compte gratuit",
|
||||
"login.orContinueWith": "ou continuer avec l'e-mail",
|
||||
"login.google.connecting": "Connexion…",
|
||||
"login.google.errorGeneric": "Une erreur est survenue avec la connexion Google.",
|
||||
"login.google.errorFailed": "La connexion Google a échoué. Veuillez réessayer."
|
||||
}
|
||||
6
frontend/src/lib/i18n/messages/fr/memento.json
Normal file
6
frontend/src/lib/i18n/messages/fr/memento.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"memento.title": "Découvrez Momento",
|
||||
"memento.slogan": "Momento n'est pas qu'une simple application de notes. C'est un écosystème intelligent qui connecte, analyse et développe vos idées en temps réel grâce à 6 types d'agents IA et une recherche sémantique de pointe.",
|
||||
"memento.ctaFree": "Commencer gratuitement",
|
||||
"memento.ctaMore": "Voir plus"
|
||||
}
|
||||
151
frontend/src/lib/i18n/messages/fr/pricing.json
Normal file
151
frontend/src/lib/i18n/messages/fr/pricing.json
Normal file
@@ -0,0 +1,151 @@
|
||||
{
|
||||
"pricing.nav.back": "Retour",
|
||||
"pricing.nav.home": "Accueil",
|
||||
"pricing.nav.mySubscription": "Mon abonnement",
|
||||
"pricing.header.badge": "Modèles IA mis à jour — Mars 2026",
|
||||
"pricing.header.title": "Un forfait pour chaque besoin",
|
||||
"pricing.header.subtitle": "Traduisez vos documents Word, Excel et PowerPoint en conservant la mise en page originale. Sans jamais saisir de clé API.",
|
||||
"pricing.billing.monthly": "Mensuel",
|
||||
"pricing.billing.yearly": "Annuel",
|
||||
"pricing.plans.free.name": "Gratuit",
|
||||
"pricing.plans.starter.name": "Starter",
|
||||
"pricing.plans.pro.name": "Pro",
|
||||
"pricing.plans.business.name": "Business",
|
||||
"pricing.plans.enterprise.name": "Entreprise",
|
||||
"pricing.plans.free.description": "Parfait pour découvrir l'application",
|
||||
"pricing.plans.starter.description": "Pour les particuliers et petits projets",
|
||||
"pricing.plans.pro.description": "Pour les professionnels et équipes en croissance",
|
||||
"pricing.plans.business.description": "Pour les équipes et organisations",
|
||||
"pricing.plans.enterprise.description": "Solutions sur mesure pour grandes organisations",
|
||||
"pricing.plans.pro.highlight": "Le plus populaire",
|
||||
"pricing.plans.pro.badge": "POPULAIRE",
|
||||
"pricing.plans.enterprise.badge": "SUR DEVIS",
|
||||
"pricing.plans.free.feat1": "5 documents / mois",
|
||||
"pricing.plans.free.feat2": "Jusqu'à 15 pages par document",
|
||||
"pricing.plans.free.feat3": "Google Traduction inclus",
|
||||
"pricing.plans.free.feat4": "Toutes les langues (130+)",
|
||||
"pricing.plans.free.feat5": "Support communautaire",
|
||||
"pricing.plans.starter.feat1": "50 documents / mois",
|
||||
"pricing.plans.starter.feat2": "Jusqu'à 50 pages par document",
|
||||
"pricing.plans.starter.feat3": "Google Traduction + DeepL",
|
||||
"pricing.plans.starter.feat4": "Fichiers jusqu'à 10 Mo",
|
||||
"pricing.plans.starter.feat5": "Support par e-mail",
|
||||
"pricing.plans.starter.feat6": "Historique 30 jours",
|
||||
"pricing.plans.pro.feat1": "200 documents / mois",
|
||||
"pricing.plans.pro.feat2": "Jusqu'à 200 pages par document",
|
||||
"pricing.plans.pro.feat3": "Traduction IA Essentielle",
|
||||
"pricing.plans.pro.feat4": "Google Traduction + DeepL",
|
||||
"pricing.plans.pro.feat5": "Fichiers jusqu'à 25 Mo",
|
||||
"pricing.plans.pro.feat6": "Glossaires personnalisés",
|
||||
"pricing.plans.pro.feat7": "Support prioritaire",
|
||||
"pricing.plans.pro.feat8": "Historique 90 jours",
|
||||
"pricing.plans.business.feat1": "1 000 documents / mois",
|
||||
"pricing.plans.business.feat2": "Jusqu'à 500 pages par document",
|
||||
"pricing.plans.business.feat3": "IA Essentielle + Premium (Claude Haiku)",
|
||||
"pricing.plans.business.feat4": "Tous les fournisseurs de traduction",
|
||||
"pricing.plans.business.feat5": "Fichiers jusqu'à 50 Mo",
|
||||
"pricing.plans.business.feat6": "Accès API (10 000 appels/mois)",
|
||||
"pricing.plans.business.feat7": "Webhooks de notification",
|
||||
"pricing.plans.business.feat8": "Support dédié",
|
||||
"pricing.plans.business.feat9": "Historique 1 an",
|
||||
"pricing.plans.business.feat10": "Analytiques avancées",
|
||||
"pricing.plans.enterprise.feat1": "Documents illimités",
|
||||
"pricing.plans.enterprise.feat2": "Tous les modèles IA (GPT-5, Claude Opus 4.6…)",
|
||||
"pricing.plans.enterprise.feat3": "Déploiement on-premise ou cloud dédié",
|
||||
"pricing.plans.enterprise.feat4": "SLA 99,9 % garanti",
|
||||
"pricing.plans.enterprise.feat5": "Support 24/7 dédié",
|
||||
"pricing.plans.enterprise.feat6": "Marque blanche (white-label)",
|
||||
"pricing.plans.enterprise.feat7": "Équipes illimitées",
|
||||
"pricing.plans.enterprise.feat8": "Intégrations sur mesure",
|
||||
"pricing.card.onRequest": "Sur devis",
|
||||
"pricing.card.free": "Gratuit",
|
||||
"pricing.card.perMonth": "/mois",
|
||||
"pricing.card.billedYearly": "Facturé {price} € / an",
|
||||
"pricing.card.documents": "Documents",
|
||||
"pricing.card.pagesMax": "Pages max",
|
||||
"pricing.card.aiTranslation": "Traduction IA",
|
||||
"pricing.card.unlimited": "Illimité",
|
||||
"pricing.card.perMonthStat": "/ mois",
|
||||
"pricing.card.perDoc": "p / doc",
|
||||
"pricing.card.aiEssential": "Essentielle",
|
||||
"pricing.card.aiEssentialPremium": "Essentielle + Premium",
|
||||
"pricing.card.aiCustom": "Sur mesure",
|
||||
"pricing.card.myPlan": "Mon forfait",
|
||||
"pricing.card.managePlan": "Gérer mon forfait",
|
||||
"pricing.card.startFree": "Commencer gratuitement",
|
||||
"pricing.card.contactUs": "Nous contacter",
|
||||
"pricing.card.choosePlan": "Choisir ce forfait",
|
||||
"pricing.card.processing": "Traitement…",
|
||||
"pricing.comparison.title": "Comparaison détaillée",
|
||||
"pricing.comparison.subtitle": "Tout ce qui est inclus dans chaque forfait",
|
||||
"pricing.comparison.feature": "Fonctionnalité",
|
||||
"pricing.comparison.docsPerMonth": "Documents / mois",
|
||||
"pricing.comparison.pagesMaxPerDoc": "Pages max / document",
|
||||
"pricing.comparison.maxFileSize": "Taille max fichier",
|
||||
"pricing.comparison.googleTranslation": "Google Traduction",
|
||||
"pricing.comparison.deepl": "DeepL",
|
||||
"pricing.comparison.aiEssential": "Traduction IA Essentielle",
|
||||
"pricing.comparison.aiPremium": "Traduction IA Premium",
|
||||
"pricing.comparison.apiAccess": "Accès API",
|
||||
"pricing.comparison.priorityProcessing": "Traitement prioritaire",
|
||||
"pricing.comparison.support": "Support",
|
||||
"pricing.comparison.support.community": "Communauté",
|
||||
"pricing.comparison.support.email": "E-mail",
|
||||
"pricing.comparison.support.priority": "Prioritaire",
|
||||
"pricing.comparison.support.dedicated": "Dédié",
|
||||
"pricing.comparison.mb": "Mo",
|
||||
"pricing.credits.title": "Crédits supplémentaires",
|
||||
"pricing.credits.subtitle": "Besoin de plus ? Achetez des crédits à l'unité, sans abonnement.",
|
||||
"pricing.credits.perPage": "1 crédit = 1 page traduite.",
|
||||
"pricing.credits.bestValue": "Le meilleur rapport",
|
||||
"pricing.credits.unit": "crédits",
|
||||
"pricing.credits.centsPerCredit": "cts / crédit",
|
||||
"pricing.credits.buy": "Acheter",
|
||||
"pricing.trust.encryption.title": "Chiffrement de bout en bout",
|
||||
"pricing.trust.encryption.sub": "TLS 1.3 + AES-256 au repos",
|
||||
"pricing.trust.languages.title": "130+ langues",
|
||||
"pricing.trust.languages.sub": "Dont arabe, persan, hébreu (RTL)",
|
||||
"pricing.trust.parallel.title": "Traitement parallèle",
|
||||
"pricing.trust.parallel.sub": "IA multi-thread ultra-rapide",
|
||||
"pricing.trust.availability.title": "Disponible 24/7",
|
||||
"pricing.trust.availability.sub": "Uptime garanti 99,9 %",
|
||||
"pricing.aiModels.title": "Nos modèles IA — Mars 2026",
|
||||
"pricing.aiModels.essential.title": "Traduction IA Essentielle",
|
||||
"pricing.aiModels.essential.plan": "Forfait Pro",
|
||||
"pricing.aiModels.essential.descPrefix": "Basée sur",
|
||||
"pricing.aiModels.essential.descSuffix": "— le modèle IA le plus rentable de 2026. Qualité comparable aux modèles frontier à une fraction du coût.",
|
||||
"pricing.aiModels.essential.modelName": "notre modèle IA Essentiel",
|
||||
"pricing.aiModels.essential.context": "163K tokens de contexte",
|
||||
"pricing.aiModels.essential.value": "Excellent rapport qualité/prix",
|
||||
"pricing.aiModels.premium.title": "Traduction IA Premium",
|
||||
"pricing.aiModels.premium.plan": "Forfait Business",
|
||||
"pricing.aiModels.premium.descPrefix": "Basée sur",
|
||||
"pricing.aiModels.premium.descSuffix": "d'Anthropic — précis sur les documents juridiques, médicaux et techniques complexes.",
|
||||
"pricing.aiModels.premium.context": "200K tokens de contexte",
|
||||
"pricing.aiModels.premium.precision": "Meilleure précision",
|
||||
"pricing.faq.title": "Questions fréquentes",
|
||||
"pricing.faq.q1": "Puis-je changer de forfait à tout moment ?",
|
||||
"pricing.faq.a1": "Oui. Le passage à un forfait supérieur est immédiat et proratisé. La rétrogradation prend effet à la fin de la période en cours.",
|
||||
"pricing.faq.q2": "Qu'est-ce que la « Traduction IA Essentielle » ?",
|
||||
"pricing.faq.a2": "C'est notre moteur IA. Il comprend le contexte de vos documents, préserve la mise en page et gère les termes techniques bien mieux qu'une traduction classique.",
|
||||
"pricing.faq.q3": "Quelle est la différence entre IA Essentielle et IA Premium ?",
|
||||
"pricing.faq.a3": "L'IA Essentielle utilise un modèle optimisé (excellent rapport qualité/prix). L'IA Premium utilise Claude Sonnet 4.6 d'Anthropic, plus précis sur les documents juridiques, médicaux et techniques complexes.",
|
||||
"pricing.faq.q4": "Mes documents sont-ils conservés après traduction ?",
|
||||
"pricing.faq.a4": "Les fichiers traduits sont disponibles selon votre forfait (30 jours Starter, 90 jours Pro, 1 an Business). Ils sont chiffrés au repos et en transit.",
|
||||
"pricing.faq.q5": "Que se passe-t-il si je dépasse mon quota mensuel ?",
|
||||
"pricing.faq.a5": "Vous pouvez acheter des crédits supplémentaires à l'unité, ou upgrader votre forfait. Vous êtes notifié à 80 % d'utilisation.",
|
||||
"pricing.faq.q6": "Y a-t-il une version d'essai gratuite des forfaits payants ?",
|
||||
"pricing.faq.a6": "Le forfait Gratuit est permanent et sans carte bancaire. Pour les forfaits Pro et Business, contactez-nous pour un accès d'essai de 14 jours.",
|
||||
"pricing.faq.q7": "Quels formats de fichiers sont supportés ?",
|
||||
"pricing.faq.a7": "Word (.docx), Excel (.xlsx/.xls), PowerPoint (.pptx), et bientôt PDF. Tous les plans supportent les mêmes formats.",
|
||||
"pricing.cta.title": "Prêt à commencer ?",
|
||||
"pricing.cta.subtitle": "Commencez gratuitement, sans carte bancaire. Passez à un forfait supérieur quand vous en avez besoin.",
|
||||
"pricing.cta.createAccount": "Créer un compte gratuit",
|
||||
"pricing.cta.login": "Se connecter",
|
||||
"pricing.toast.demo": "Mode démo — Stripe n'est pas encore configuré. En production, vous seriez redirigé vers le paiement pour activer le forfait {planId}.",
|
||||
"pricing.toast.networkError": "Erreur réseau. Veuillez réessayer.",
|
||||
"pricing.toast.paymentError": "Erreur lors de la création du paiement.",
|
||||
"pricing.dashboard": "Tableau de bord",
|
||||
"pricing.okSymbol": "✓",
|
||||
"pricing.errSymbol": "✕"
|
||||
}
|
||||
58
frontend/src/lib/i18n/messages/fr/profile.json
Normal file
58
frontend/src/lib/i18n/messages/fr/profile.json
Normal file
@@ -0,0 +1,58 @@
|
||||
{
|
||||
"profile.header.title": "Mon Profil",
|
||||
"profile.header.subtitle": "Gérez votre compte et vos préférences.",
|
||||
"profile.tabs.account": "Compte",
|
||||
"profile.tabs.subscription": "Abonnement",
|
||||
"profile.tabs.preferences": "Préférences",
|
||||
"profile.account.user": "Utilisateur",
|
||||
"profile.account.memberSince": "Membre depuis",
|
||||
"profile.plan.label": "Forfait",
|
||||
"profile.plan.free": "Gratuit",
|
||||
"profile.plan.starter": "Starter",
|
||||
"profile.plan.pro": "Pro",
|
||||
"profile.plan.business": "Business",
|
||||
"profile.plan.enterprise": "Enterprise",
|
||||
"profile.plan.pricePerMonth": "{price} €/mois",
|
||||
"profile.subscription.canceling": "Annulation en cours",
|
||||
"profile.subscription.active": "Actif",
|
||||
"profile.subscription.unknown": "Inconnu",
|
||||
"profile.subscription.accessUntil": "Accès jusqu'au",
|
||||
"profile.subscription.renewalOn": "Renouvellement le",
|
||||
"profile.subscription.upgradePlan": "Passer à un forfait payant",
|
||||
"profile.subscription.changePlan": "Changer de forfait",
|
||||
"profile.subscription.manageBilling": "Gérer la facturation",
|
||||
"profile.subscription.billingUnavailable": "Portail de facturation non disponible.",
|
||||
"profile.subscription.billingError": "Erreur lors de l'accès au portail.",
|
||||
"profile.subscription.cancelSuccess": "Abonnement annulé. Vous conservez l'accès jusqu'à la fin de la période.",
|
||||
"profile.subscription.cancelError": "Erreur lors de l'annulation.",
|
||||
"profile.subscription.networkError": "Erreur réseau.",
|
||||
"profile.usage.title": "Utilisation ce mois-ci",
|
||||
"profile.usage.resetOn": "Reset le",
|
||||
"profile.usage.documents": "Documents",
|
||||
"profile.usage.pages": "Pages",
|
||||
"profile.usage.extraCredits": "crédit supplémentaire",
|
||||
"profile.usage.extraCreditsPlural": "crédits supplémentaires",
|
||||
"profile.usage.quotaReached": "Quota atteint",
|
||||
"profile.usage.quotaReachedDesc": "Passez à un forfait supérieur pour continuer.",
|
||||
"profile.usage.unlockMore": "Débloquez plus de traductions avec un forfait payant.",
|
||||
"profile.usage.viewPlans": "Voir les forfaits",
|
||||
"profile.usage.includedInPlan": "Inclus dans votre forfait",
|
||||
"profile.danger.title": "Zone danger",
|
||||
"profile.danger.description": "L'annulation prend effet à la fin de votre période en cours. Vous conservez l'accès jusqu'à cette date.",
|
||||
"profile.danger.confirm": "Êtes-vous sûr(e) ? Cette action ne peut pas être annulée.",
|
||||
"profile.danger.confirmCancel": "Confirmer l'annulation",
|
||||
"profile.danger.cancelSubscription": "Annuler mon abonnement",
|
||||
"profile.danger.keep": "Non, garder",
|
||||
"profile.prefs.interfaceLang": "Langue de l'interface",
|
||||
"profile.prefs.interfaceLangDesc": "La langue est détectée automatiquement selon votre navigateur. Vous pouvez la changer manuellement.",
|
||||
"profile.prefs.defaultTargetLang": "Langue cible par défaut",
|
||||
"profile.prefs.selectLanguage": "Sélectionner une langue",
|
||||
"profile.prefs.defaultTargetLangDesc": "Cette langue sera pré-sélectionnée lors de vos traductions.",
|
||||
"profile.prefs.save": "Enregistrer",
|
||||
"profile.prefs.theme": "Thème",
|
||||
"profile.prefs.themeDesc": "Choisissez l'apparence de l'interface",
|
||||
"profile.prefs.cache": "Cache",
|
||||
"profile.prefs.cacheDesc": "Effacer le cache local peut résoudre certains problèmes d'affichage.",
|
||||
"profile.prefs.clearing": "Effacement...",
|
||||
"profile.prefs.clearCache": "Effacer le cache"
|
||||
}
|
||||
7
frontend/src/lib/i18n/messages/fr/providerSelector.json
Normal file
7
frontend/src/lib/i18n/messages/fr/providerSelector.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"providerSelector.noClassic": "Aucun traducteur standard disponible.",
|
||||
"providerSelector.noLlm": "Aucun modèle IA configuré.",
|
||||
"providerSelector.costOne": "Coût : 1 crédit par page",
|
||||
"providerSelector.costFive": "Coût : 5 crédits par page (Facteur Premium)",
|
||||
"providerSelector.unlockContextual": "Débloquez la traduction contextuelle haut de gamme pour vos documents entiers."
|
||||
}
|
||||
29
frontend/src/lib/i18n/messages/fr/providerTheme.json
Normal file
29
frontend/src/lib/i18n/messages/fr/providerTheme.json
Normal file
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"providerTheme.deepseek.badge": "Essentielle",
|
||||
"providerTheme.deepseek.subBadge": "Technique & Éco",
|
||||
"providerTheme.deepseek.desc": "Traduction ultra-précise et économique, idéale pour les documents techniques et le code.",
|
||||
"providerTheme.openai.badge": "Premium",
|
||||
"providerTheme.openai.subBadge": "Haute Fidélité",
|
||||
"providerTheme.openai.desc": "Le standard mondial de l'IA. Cohérence textuelle maximale et respect strict du style.",
|
||||
"providerTheme.minimax.badge": "Avancée",
|
||||
"providerTheme.minimax.subBadge": "Performance",
|
||||
"providerTheme.minimax.desc": "Vitesse d'exécution incroyable et excellente compréhension des structures complexes.",
|
||||
"providerTheme.openrouter.badge": "Express",
|
||||
"providerTheme.openrouter.subBadge": "Multi-Modèles",
|
||||
"providerTheme.openrouter.desc": "Accès unifié aux meilleurs modèles open-source optimisés pour la traduction.",
|
||||
"providerTheme.openrouter_premium.badge": "Ultra",
|
||||
"providerTheme.openrouter_premium.subBadge": "Maximum Context",
|
||||
"providerTheme.openrouter_premium.desc": "Traduction assistée par les modèles de pointe (GPT-4o, Claude Sonnet 4.6) pour documents longs.",
|
||||
"providerTheme.zai.badge": "Spécialisée",
|
||||
"providerTheme.zai.subBadge": "Finance & Droit",
|
||||
"providerTheme.zai.desc": "Modèle affiné pour les terminologies métiers exigeantes (juridique, finance).",
|
||||
"providerTheme.default.badge": "Moderne",
|
||||
"providerTheme.default.subBadge": "Raisonnement IA",
|
||||
"providerTheme.default.desc": "Traduction par grand modèle linguistique (LLM) avec analyse sémantique avancée.",
|
||||
"providerTheme.classic.google.label": "Google Traduction",
|
||||
"providerTheme.classic.google.desc": "Traduction ultra-rapide couvrant plus de 130 langues. Recommandé pour les flux généraux.",
|
||||
"providerTheme.classic.deepl.label": "DeepL Pro",
|
||||
"providerTheme.classic.deepl.desc": "Traduction haute précision réputée pour sa fluidité et ses formulations naturelles.",
|
||||
"providerTheme.classic.google_cloud.label": "Google Cloud API",
|
||||
"providerTheme.classic.google_cloud.desc": "Moteur cloud professionnel optimisé pour le traitement de gros volumes de documents."
|
||||
}
|
||||
29
frontend/src/lib/i18n/messages/fr/register.json
Normal file
29
frontend/src/lib/i18n/messages/fr/register.json
Normal file
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"register.title": "Créer un compte",
|
||||
"register.subtitle": "Commencez à traduire gratuitement",
|
||||
"register.error.failed": "L'inscription a échoué",
|
||||
"register.name.label": "Nom",
|
||||
"register.name.placeholder": "Votre nom",
|
||||
"register.name.error": "Le nom doit contenir au moins 2 caractères",
|
||||
"register.email.label": "Adresse email",
|
||||
"register.email.placeholder": "vous@exemple.com",
|
||||
"register.email.error": "Adresse email invalide",
|
||||
"register.password.label": "Mot de passe",
|
||||
"register.password.error": "Le mot de passe doit contenir au moins 8 caractères, une majuscule, une minuscule et un chiffre",
|
||||
"register.password.show": "Afficher le mot de passe",
|
||||
"register.password.hide": "Masquer le mot de passe",
|
||||
"register.password.strengthLabel": "Force :",
|
||||
"register.password.strength.weak": "Faible",
|
||||
"register.password.strength.medium": "Moyen",
|
||||
"register.password.strength.strong": "Fort",
|
||||
"register.confirmPassword.label": "Confirmer le mot de passe",
|
||||
"register.confirmPassword.error": "Les mots de passe ne correspondent pas",
|
||||
"register.confirmPassword.show": "Afficher",
|
||||
"register.confirmPassword.hide": "Masquer",
|
||||
"register.submit.creating": "Création du compte...",
|
||||
"register.submit.create": "Créer mon compte",
|
||||
"register.hasAccount": "Vous avez déjà un compte ?",
|
||||
"register.login": "Se connecter",
|
||||
"register.terms.prefix": "En créant un compte, vous acceptez notre",
|
||||
"register.terms.link": "utilisation du service"
|
||||
}
|
||||
22
frontend/src/lib/i18n/messages/fr/resetPassword.json
Normal file
22
frontend/src/lib/i18n/messages/fr/resetPassword.json
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"resetPassword.passwordRequirements": "Le mot de passe doit contenir au moins 8 caractères, une majuscule, une minuscule et un chiffre",
|
||||
"resetPassword.passwordMismatch": "Les mots de passe ne correspondent pas",
|
||||
"resetPassword.tokenMissing": "Token manquant. Veuillez utiliser le lien reçu par email.",
|
||||
"resetPassword.error": "Une erreur s'est produite",
|
||||
"resetPassword.invalidLink": "Lien invalide",
|
||||
"resetPassword.invalidLinkMessage": "Ce lien de réinitialisation est invalide. Veuillez redemander un nouveau lien.",
|
||||
"resetPassword.requestNewLink": "Redemander un lien",
|
||||
"resetPassword.successTitle": "Mot de passe réinitialisé",
|
||||
"resetPassword.newPasswordTitle": "Nouveau mot de passe",
|
||||
"resetPassword.successSubtitle": "Vous allez être redirigé vers la connexion",
|
||||
"resetPassword.subtitle": "Définissez votre nouveau mot de passe",
|
||||
"resetPassword.successMessage": "Votre mot de passe a été réinitialisé avec succès. Vous allez être redirigé vers la page de connexion.",
|
||||
"resetPassword.newPassword": "Nouveau mot de passe",
|
||||
"resetPassword.showPassword": "Afficher le mot de passe",
|
||||
"resetPassword.hidePassword": "Masquer le mot de passe",
|
||||
"resetPassword.confirmPassword": "Confirmer le mot de passe",
|
||||
"resetPassword.resetting": "Réinitialisation...",
|
||||
"resetPassword.resetPassword": "Réinitialiser le mot de passe",
|
||||
"resetPassword.backToLogin": "Retour à la connexion",
|
||||
"resetPassword.loading": "Chargement..."
|
||||
}
|
||||
14
frontend/src/lib/i18n/messages/fr/services.json
Normal file
14
frontend/src/lib/i18n/messages/fr/services.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"services.title": "Fournisseurs de traduction",
|
||||
"services.subtitle": "Les fournisseurs sont configurés par l'administrateur. Vous pouvez voir ceux actuellement disponibles pour votre compte.",
|
||||
"services.loading": "Chargement des fournisseurs...",
|
||||
"services.noProviders": "Aucun fournisseur n'est actuellement configuré. Contactez votre administrateur.",
|
||||
"services.classic": "Traduction classique",
|
||||
"services.llmPro": "IA · Contextuelle (Pro)",
|
||||
"services.available": "Disponible",
|
||||
"services.model": "Modèle",
|
||||
"services.adminOnly.title": "La configuration des fournisseurs est réservée à l'administrateur",
|
||||
"services.adminOnly.desc": "Les clés API, la sélection de modèle et l'activation des fournisseurs sont gérées exclusivement par l'administrateur. Vous n'avez jamais besoin de saisir de clé API.",
|
||||
"services.fallback.google.label": "Google Traduction",
|
||||
"services.fallback.google.desc": "Traduction rapide, 130+ langues"
|
||||
}
|
||||
20
frontend/src/lib/i18n/messages/fr/settings.json
Normal file
20
frontend/src/lib/i18n/messages/fr/settings.json
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"settings.title": "Paramètres",
|
||||
"settings.subtitle": "Configuration générale de l'application",
|
||||
"settings.formats.title": "Formats supportés",
|
||||
"settings.formats.subtitle": "Types de documents que vous pouvez traduire",
|
||||
"settings.formats.formulas": "Formules",
|
||||
"settings.formats.styles": "Styles",
|
||||
"settings.formats.images": "Images",
|
||||
"settings.formats.headers": "En-têtes",
|
||||
"settings.formats.tables": "Tableaux",
|
||||
"settings.formats.slides": "Slides",
|
||||
"settings.formats.notes": "Notes",
|
||||
"settings.cache.title": "Cache",
|
||||
"settings.cache.desc": "Effacer le cache local peut résoudre certains problèmes d'affichage.",
|
||||
"settings.cache.clearing": "Effacement...",
|
||||
"settings.cache.clear": "Effacer le cache",
|
||||
"settings.formats.excel.name": "Excel",
|
||||
"settings.formats.word.name": "Word",
|
||||
"settings.formats.powerpoint.name": "PowerPoint"
|
||||
}
|
||||
93
frontend/src/lib/i18n/messages/fr/translate.json
Normal file
93
frontend/src/lib/i18n/messages/fr/translate.json
Normal file
@@ -0,0 +1,93 @@
|
||||
{
|
||||
"translate.glossary.selectGlossary": "Sélectionner un glossaire...",
|
||||
"translate.mode.label": "Mode de traduction",
|
||||
"translate.mode.classic": "Classique",
|
||||
"translate.mode.classicDesc": "Rapide",
|
||||
"translate.mode.proLlm": "Pro LLM",
|
||||
"translate.mode.proLlmDesc": "Contextuel",
|
||||
"translate.mode.tooltip": "Passez à Pro pour la traduction LLM",
|
||||
"translate.mode.upgradeLink": "Passer à Pro",
|
||||
"translate.mode.upgradeDesc": "pour des traductions alimentées par LLM",
|
||||
"translate.glossary.title": "Glossaire",
|
||||
"translate.glossary.select": "Sélectionner un glossaire",
|
||||
"translate.glossary.none": "Aucun",
|
||||
"translate.glossary.terms": "termes",
|
||||
"translate.glossary.proOnly": "Passez à Pro pour utiliser les glossaires",
|
||||
"translate.glossary.myGlossaries": "Mes glossaires",
|
||||
"translate.glossary.fromTemplate": "Créer depuis un template",
|
||||
"translate.glossary.noGlossaryForPair": "Aucun glossaire pour",
|
||||
"translate.glossary.noGlossaries": "Aucun glossaire",
|
||||
"translate.glossary.loading": "Chargement...",
|
||||
"translate.glossary.classicMode": "Moteur neutre sans glossaire (IA uniquement)",
|
||||
"translate.glossary.selectPlaceholder": "Sélectionner un glossaire...",
|
||||
"translate.glossary.multilingual": "MULTILINGUE",
|
||||
"translate.glossary.noGlossaryAvailable": "Aucun glossaire disponible",
|
||||
"translate.glossary.filterByLang": "Filtrer par langue",
|
||||
"translate.glossary.active": "Actif",
|
||||
"translate.glossary.inactive": "Inactif",
|
||||
"translate.glossary.availableTemplates": "Modèles disponibles",
|
||||
"translate.glossary.importing": "Importation...",
|
||||
"translate.glossary.imported": "(Importé)",
|
||||
"translate.glossary.noGlossaryForSource": "Aucun glossaire ni modèle pour la langue source",
|
||||
"translate.glossary.createGlossary": "Créer un glossaire",
|
||||
"translate.glossary.showAll": "Afficher tous les glossaires",
|
||||
"translate.glossary.activePreview": "Aperçu des correspondances actives :",
|
||||
"translate.glossary.total": "au total",
|
||||
"translate.glossary.moreTerms": "autres termes",
|
||||
"translate.glossary.noTerms": "Aucun terme dans ce glossaire.",
|
||||
"translate.glossary.sourceTerm": "Terme Source",
|
||||
"translate.glossary.translation": "Traduction",
|
||||
"translate.glossary.addTerm": "Ajouter le terme",
|
||||
"translate.glossary.disabledMode": "Moteur neutre sans glossaire appliqué",
|
||||
"translate.glossary.addTermError": "Erreur lors de l'ajout du terme",
|
||||
"translate.glossary.networkError": "Erreur réseau",
|
||||
"translate.glossary.importFailed": "Échec de l'importation ({status})",
|
||||
"translate.glossary.helpText": "Le glossaire force la traduction de termes précis. Choisissez un glossaire dont la langue source correspond à la langue d'origine de votre document.",
|
||||
"translate.glossary.sourceWarning": "Attention : Ce glossaire utilise la langue source",
|
||||
"translate.glossary.sourceWarningBut": "mais votre document est configuré en",
|
||||
"translate.glossary.targetWarning": "Incompatibilité de cible : Ce glossaire est prévu pour traduire vers",
|
||||
"translate.glossary.targetWarningBut": "mais votre document cible",
|
||||
"translate.glossary.targetWarningEnd": "Les termes risquent de ne pas être pertinents.",
|
||||
"translate.header.processing": "Traitement en cours",
|
||||
"translate.header.aiActive": "Analyse IA active",
|
||||
"translate.header.aiActiveDesc": "Votre mise en page est en cours de préservation par notre moteur contextuel.",
|
||||
"translate.header.completed": "Complété",
|
||||
"translate.header.completedTitle": "Traduction terminée",
|
||||
"translate.header.proSpace": "Espace Pro",
|
||||
"translate.header.translateDoc": "Traduire un document",
|
||||
"translate.header.translateDocDesc": "Conservez la mise en page d'origine grâce au moteur de traduction ultra-haute fidélité.",
|
||||
"translate.upload.nativeFormat": "Format natif",
|
||||
"translate.fileType.word": "Word (.docx)",
|
||||
"translate.fileType.excel": "Excel (.xlsx)",
|
||||
"translate.fileType.slides": "Slides (.pptx)",
|
||||
"translate.fileType.pdf": "PDF (.pdf)",
|
||||
"translate.startTranslation": "Lancer la traduction",
|
||||
"translate.submit": "Soumission…",
|
||||
"translate.chooseTargetLang": "Veuillez choisir une langue cible",
|
||||
"translate.pleaseLoadFile": "Veuillez charger un fichier d'abord",
|
||||
"translate.contextEngineActive": "Moteur contextuel actif",
|
||||
"translate.phase1": "Phase 1 : Initialisation",
|
||||
"translate.phase2": "Phase 2 : Reconstruction contextuelle",
|
||||
"translate.stat.segments": "segments",
|
||||
"translate.stat.precision": "précision",
|
||||
"translate.stat.speedLabel": "vitesse",
|
||||
"translate.stat.turbo": "Turbo",
|
||||
"translate.stat.time": "temps",
|
||||
"translate.complete.masterQuality": "✓ Qualité Maître",
|
||||
"translate.download": "Télécharger",
|
||||
"translate.newTranslation": "+ Nouvelle traduction",
|
||||
"translate.failedTitle": "Erreur lors de la traduction",
|
||||
"translate.retry": "Réessayer",
|
||||
"translate.uploadAnother": "Téléverser un autre fichier",
|
||||
"translate.monitor": "Moniteur IA",
|
||||
"translate.summary": "Récapitulatif",
|
||||
"translate.cancelProcess": "⟳ Annuler le processus",
|
||||
"translate.layoutIntegrity": "Intégrité Layout",
|
||||
"translate.secureHundred": "100% SÉCURISÉ",
|
||||
"translate.okHundred": "100% OK",
|
||||
"translate.preserveLayout": "Conserver la mise en page",
|
||||
"translate.preserveLayoutDesc": "Conserver la mise en page",
|
||||
"translate.textOnly": "Texte brut",
|
||||
"translate.textOnlyDesc": "Traduction rapide du texte uniquement",
|
||||
"translate.unavailableStandard": "Indisponible en mode Standard (IA uniquement)"
|
||||
}
|
||||
6
frontend/src/lib/i18n/messages/fr/translateComplete.json
Normal file
6
frontend/src/lib/i18n/messages/fr/translateComplete.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"translateComplete.highQuality": "Haute qualité",
|
||||
"translateComplete.segments": "Segments",
|
||||
"translateComplete.characters": "Caractères",
|
||||
"translateComplete.confidence": "Confiance"
|
||||
}
|
||||
Reference in New Issue
Block a user