fix(ui): design critique overhaul — a11y, honest metrics, i18n repair (critique 2026-08-30)
All checks were successful
Deploy to Production / Build and Deploy (push) Successful in 2m51s

P0 a11y: keyboard-accessible dropzone (role/tabIndex/Enter-Space), ARIA
combobox + listbox pattern for language selector, role=switch on glossary
toggle, role=status live region on notifications, aria-labels on password
toggles, visible-on-focus close buttons, RTL logical positioning (start-*).

Trust: third-party Memento promo removed from translate page, sidebar and
all 13 locales; fabricated stats (99.9%, Turbo, computed layout-integrity
bars) replaced with real measurements incl. API estimated remaining time;
silent download failure now surfaces an error notification.

Honesty: fake 100-byte file injections removed (format chips are now
informational); cancel-that-doesn't renamed 'Back to start' with hint;
Enterprise contact placeholder replaced with contact@wordly.art.

i18n: t() no longer returns raw keys (empty string + defaultValue support,
~30 dead || fallbacks now work); ~170 new keys EN+FR across new reviews/
teams namespaces, glossaries context tab, translate monitor, settings,
services, pricing, landing, fileUploader; split-key italic titles replace
lastIndexOf() surgery (zh/ja-safe); key-audit script added 0 missing.

Flow: active job persisted across refresh with polling resume (24h TTL);
client-side recent-jobs history with review links; review page linked from
complete state; settings/services added to dashboard nav; Business/
Enterprise regain glossary access (tier gate unified).

Typeset (sober-tool direction): 7.5-9px labels raised to 10-12px, /30
opacity to /45-/55, uppercase tracking reduced, trust footer legible,
country flags removed from language switcher, localized dates.

Cleanup: 5 orphaned translate components, dead site header/footer,
fossil tailwind.config.js, PipelineStepper, duplicate pill+H1 titles,
two-step confirm for cache clear, dead landing footer links.

Verified: next build exit 0, vitest 9/9, eslint 64 errors = HEAD
(no regression, -3 warnings), detector 4 -> 3 findings.
This commit is contained in:
2026-08-30 21:44:53 +02:00
parent 1a67241ad5
commit 50047ea8a2
79 changed files with 878 additions and 1590 deletions

View File

@@ -112,5 +112,6 @@
"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",
"dashboard.nav.teams": "Équipe"
"dashboard.nav.teams": "Équipe",
"dashboard.loading": "Chargement…"
}

View File

@@ -27,5 +27,7 @@
"fileUploader.foundTexts": "{count} textes trouvés à traduire",
"fileUploader.translatingItem": "Traduction {current}/{total} : « {preview} »",
"fileUploader.reconstructing": "Reconstruction du document…",
"fileUploader.translatingLocally": "Traduction locale avec WebLLM…"
"fileUploader.translatingLocally": "Traduction locale avec WebLLM…",
"fileUploader.error.invalidFormat": "Format non supporté. Formats acceptés : .xlsx, .docx, .pptx, .pdf",
"fileUploader.error.tooLarge": "Fichier trop volumineux (max 50 Mo)"
}

View File

@@ -189,5 +189,36 @@
"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."
"glossaries.detail.importErrorDesc": "Impossible de lire le fichier.",
"glossaries.titleBase": "Glossaires &",
"glossaries.titleAccent": "Contexte",
"glossaries.new": "Nouveau glossaire",
"glossaries.info.title": "Pour utiliser un glossaire dans une traduction :",
"glossaries.info.body": "cliquez sur « Utiliser » sur la carte souhaitée. Vous serez redirigé sur la page Traduire avec ce glossaire déjà sélectionné.",
"glossaries.info.cta": "Aller traduire",
"glossaries.context.title": "Consignes de contexte",
"glossaries.context.desc": "Ces instructions sont envoyées automatiquement à l'IA avant chaque traduction en mode Pro LLM.",
"glossaries.context.unsaved": "Non sauvegardé",
"glossaries.context.active": "Actif",
"glossaries.context.inactive": "Inactif",
"glossaries.context.suggestions": "Suggestions rapides — cliquez pour ajouter",
"glossaries.context.placeholder": "Ex : Vous traduisez des documents techniques HVAC. Utilisez une terminologie d'ingénierie précise et un ton professionnel…",
"glossaries.context.chars": "{count} caractères",
"glossaries.context.noPrompt": "Aucune consigne enregistrée",
"glossaries.context.clear": "Tout effacer",
"glossaries.context.saving": "Enregistrement…",
"glossaries.context.saved": "Enregistré",
"glossaries.context.save": "Enregistrer",
"glossaries.activeTarget": "Traduction active :",
"glossaries.searchPlaceholder": "Rechercher un glossaire…",
"glossaries.countMany": "{count} glossaires — cliquez sur une carte pour la modifier",
"glossaries.countNone": "Aucun glossaire — créez-en un pour commencer",
"glossaries.termsOne": "1 terme",
"glossaries.termsMany": "{count} termes",
"glossaries.badge.otherLang": "Autre langue",
"glossaries.card.edit": "Éditer",
"glossaries.card.use": "Utiliser",
"glossaries.createFirst": "Créer mon premier glossaire",
"glossaries.noResults": "Aucun résultat pour « {query} »",
"glossaries.card.terms": "termes"
}

View File

@@ -16,15 +16,16 @@ 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 reviews from "./reviews.json";
import services from "./services.json";
import settings from "./settings.json";
import teams from "./teams.json";
import translate from "./translate.json";
import translateComplete from "./translateComplete.json";
@@ -44,15 +45,16 @@ const messages: Record<string, string> = {
...langSelector,
...layout,
...login,
...memento,
...pricing,
...profile,
...providerSelector,
...providerTheme,
...register,
...resetPassword,
...reviews,
...services,
...settings,
...teams,
...translate,
...translateComplete,
};

View File

@@ -148,5 +148,6 @@
"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"
"landing.translate.preservingLayout": "Votre mise en page est en cours de préservation",
"landing.formats.pill": "COMPATIBILITÉ"
}

View File

@@ -3,5 +3,6 @@
"langSelector.noResults": "Aucun résultat",
"langSelector.source": "Source",
"langSelector.target": "Cible",
"langSelector.swap": "Inverser"
"langSelector.swap": "Inverser",
"langSelector.changeLanguage": "Changer la langue"
}

View File

@@ -1,7 +0,0 @@
{
"memento.title": "Découvrez Memento",
"memento.slogan": "Memento 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",
"memento.url": "https://memento-note.com/"
}

View File

@@ -147,5 +147,10 @@
"pricing.toast.paymentError": "Erreur lors de la création du paiement.",
"pricing.dashboard": "Tableau de bord",
"pricing.okSymbol": "✓",
"pricing.errSymbol": "✕"
"pricing.errSymbol": "✕",
"pricing.error.server": "Erreur serveur {status}",
"pricing.enterprise.subject": "Offre Enterprise",
"pricing.toast.close": "Fermer",
"pricing.header.titleBase": "Un forfait pour",
"pricing.header.titleAccent": "chaque besoin"
}

View File

@@ -0,0 +1,32 @@
{
"reviews.title": "Relecture",
"reviews.subtitle": "{file} — {total} segments ({pending} à relire, {approved} approuvés, {edited} modifiés)",
"reviews.approveAll": "Tout approuver",
"reviews.importXliff": "Importer XLIFF",
"reviews.rebuild": "Reconstruire et télécharger",
"reviews.col.source": "Source",
"reviews.col.target": "Traduction",
"reviews.col.status": "Statut",
"reviews.status.approved": "Approuvé",
"reviews.status.edited": "Modifié",
"reviews.status.pending": "À relire",
"reviews.save": "Enregistrer",
"reviews.cancel": "Annuler",
"reviews.emptyCell": "— vide —",
"reviews.action.edit": "Modifier la traduction",
"reviews.action.approve": "Approuver tel quel",
"reviews.action.reset": "Remettre à relire",
"reviews.approvedTitle": "Segments approuvés",
"reviews.approvedDesc": "{count} segment(s) approuvé(s).",
"reviews.rebuiltTitle": "Document reconstruit",
"reviews.rebuiltDesc": "{count} segment(s) relu(s) appliqué(s).",
"reviews.rebuildFailed": "Reconstruction impossible",
"reviews.xliffExportFailed": "Export XLIFF échoué",
"reviews.xliffImported": "XLIFF importé",
"reviews.xliffImportedDesc": "{count} segment(s) mis à jour.",
"reviews.xliffImportFailed": "Import XLIFF échoué",
"reviews.error.title": "Erreur",
"reviews.error.load": "Erreur de chargement",
"reviews.error.update": "Mise à jour impossible",
"reviews.error.download": "Téléchargement échoué (HTTP {status})"
}

View File

@@ -10,5 +10,6 @@
"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"
"services.fallback.google.desc": "Traduction rapide, 130+ langues",
"services.pill": "Moteurs"
}

View File

@@ -16,5 +16,8 @@
"settings.cache.clear": "Effacer le cache",
"settings.formats.excel.name": "Excel",
"settings.formats.word.name": "Word",
"settings.formats.powerpoint.name": "PowerPoint"
"settings.formats.powerpoint.name": "PowerPoint",
"settings.pill": "Configuration",
"settings.cache.confirm": "Confirmer et recharger",
"settings.cache.cancelClear": "Annuler"
}

View File

@@ -0,0 +1,26 @@
{
"teams.title": "Espaces de travail",
"teams.subtitle": "Partagez vos glossaires avec votre équipe (plan Business — 5 sièges).",
"teams.needsBusiness": "Les espaces de travail nécessitent le plan Business. Passez au plan supérieur pour collaborer avec votre équipe.",
"teams.newWorkspace": "Nouvel espace de travail",
"teams.newPlaceholder": "Ex. Équipe traduction",
"teams.create": "Créer",
"teams.created": "Espace créé",
"teams.createdDesc": "Vous êtes propriétaire.",
"teams.createFailed": "Création impossible",
"teams.memberAdded": "Membre ajouté",
"teams.addFailed": "Ajout impossible",
"teams.removeFailed": "Retrait impossible",
"teams.removeMember": "Retirer du workspace",
"teams.owner": "Propriétaire",
"teams.seats": "{count} / {limit} sièges",
"teams.seatsUnlimited": "{count} sièges (illimité)",
"teams.role.member": "Membre",
"teams.role.admin": "Admin",
"teams.addMember": "Ajouter un membre (e-mail)",
"teams.emailPlaceholder": "collegue@entreprise.com",
"teams.invite": "Inviter",
"teams.empty": "Aucun espace pour l'instant — créez le premier ci-dessus.",
"teams.error.title": "Erreur",
"teams.error.load": "Chargement impossible"
}

View File

@@ -89,5 +89,25 @@
"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)"
"translate.unavailableStandard": "Indisponible en mode Standard (IA uniquement)",
"translate.header.aiActiveTitle": "Analyse IA",
"translate.header.aiActiveAccent": "active",
"translate.header.completedTitleBase": "Traduction",
"translate.header.completedTitleAccent": "terminée",
"translate.header.translateDocBase": "Traduire",
"translate.header.translateDocAccent": "un document",
"translate.header.workspace": "Espace de traduction",
"translate.monitor.source": "Source",
"translate.monitor.target": "Cible",
"translate.monitor.engine": "Moteur",
"translate.stat.progress": "progression",
"translate.stat.remaining": "temps restant",
"translate.stat.elapsed": "temps écoulé",
"translate.leaveScreen": "Revenir à l'accueil",
"translate.leaveScreenHint": "La traduction continue en arrière-plan.",
"translate.reviewCta": "Relire segment par segment",
"translate.downloadFailed": "Le téléchargement a échoué. Réessayez.",
"translate.recent.title": "Traductions récentes",
"translate.recent.review": "Relire",
"translate.upload.ariaDropzone": "Déposer un document : glisser-déposer ou appuyer sur Entrée pour parcourir"
}