Files
office_translator/_bmad-output/planning-artifacts/ux-refactor-specification.md
sepehr b50419e2ec
All checks were successful
Deploy to Production / Build and Deploy (push) Successful in 2s
fix: integrate deepseek, resolve silent google api errors, fix google cloud keys
2026-05-17 17:11:06 +02:00

12 KiB
Raw Blame History

Cahier des Charges — Refonte UX/Logique Office Translator

Date : 2026-05-02 Auteur : Mary (Business Analyst) Statut : En attente de validation


Contexte

L'application Office Translator est un SaaS de traduction de documents (DOCX, XLSX, PPTX) avec un système de plans (Free, Starter, Pro, Business, Enterprise). Plusieurs incohérences UX ont ete identifiees entre la logique business attendue et l'implementation actuelle.

Stack technique : Next.js 16 + React 19 + Tailwind CSS (frontend), FastAPI + Python (backend), JWT (auth).


Changements requis

1. Retirer « Services » de la sidebar utilisateur

Probleme : L'entree « Services » (icone Zap) dans DashboardSidebar mene a /dashboard/services, une page qui affiche la liste des providers backend (Google, DeepL, OpenAI, etc.) avec des badges « Available ». L'utilisateur basique n'a aucune action a y faire — c'est une page d'information infrastructure.

Fichiers concernes :

  • frontend/src/app/dashboard/constants.ts — retirer l'entree Services de baseNavItems
  • frontend/src/app/dashboard/services/page.tsx — deplacer vers /admin/ ou garder uniquement accessible via URL directe pour les utilisateurs avances
  • frontend/src/app/dashboard/settings/page.tsx — retirer le lien « Translation Services » dans la carte de settings

Regles :

  • L'entree « Services » doit etre supprimee de baseNavItems (ligne 15 de constants.ts)
  • La page /dashboard/services peut rester accessible par URL mais ne doit plus apparaitre dans la navigation
  • Dans la page Settings, la carte « Translation Services » (lignes 66-83 de settings/page.tsx) doit etre supprimee
  • Le lien dans le dropdown mobile (si existant) doit aussi etre retire

Impact backend : Aucun. Les endpoints /api/v1/providers/available restent disponibles.


2. Refonte de la page Profil

Probleme : La page Profil (profile/page.tsx, 497 lignes) empile 6 sections heterogenes sans hierarchie visuelle claire. L'information est difficile a parcourir.

Structure actuelle :

  • Carte identite (nom, email, date, plan)
  • Carte abonnement (plan, prix, statut, renouvellement, portail Stripe)
  • Zone danger (annulation)
  • Carte utilisation (documents/pages avec barres de progression)
  • Features incluses (liste texte)
  • Preferences (langue UI : FR/EN uniquement)

Nouvelle structure proposee — 3 onglets :

Onglet 1 : « Mon Compte »

  • Avatar avec initiales + nom + email (en lecture seule)
  • Badge du plan actuel
  • Membre depuis (date)
  • Bouton « Modifier mon profil » (nom, email) — futur

Onglet 2 : « Abonnement »

  • Plan actuel avec prix
  • Statut de l'abonnement (Actif / Annulation en cours)
  • Date de renouvellement ou date de fin
  • Barres d'utilisation (documents/pages) avec reset date
  • Credits supplementaires (si > 0)
  • Bouton « Gerer la facturation » (portail Stripe)
  • Bouton « Changer de forfait »
  • Zone danger : annulation (avec confirmation, inchangee)
  • Features incluses dans le plan

Onglet 3 : « Preferences »

  • Langue de l'interface (voir section 3)
  • Langue cible par defaut pour les traductions
  • Theme (clair/sombre) — actuellement dans la sidebar, deplacer ici
  • Autres preferences futures

Fichiers concernes :

  • frontend/src/app/dashboard/profile/page.tsx — refonte complete
  • Creer frontend/src/app/dashboard/profile/components/ pour les sous-composants des onglets
  • frontend/src/app/dashboard/DashboardSidebar.tsx — retirer le ThemeToggle de la sidebar (deplacer dans Preferences)

Regles de design :

  • Utiliser le composant Tabs de Radix UI (deja disponible via shadcn/ui)
  • Chaque onglet doit avoir une icone et un titre clair
  • La zone danger (annulation) doit etre visuellement isolee, en bas de l'onglet Abonnement
  • Le contenu doit respecter le systeme i18n (pas de texte hardcode)
  • Responsive : les onglets se transforment en scroll horizontal sur mobile

3. Systeme de langues — Detection automatique + extension

3a. Detection automatique de la langue du navigateur

Probleme : Dans i18n.tsx:235, la locale par defaut est hardcodee a "fr". Aucune detection de navigator.language.

Implementation :

  • Au premier chargement (pas de locale sauvegardee dans localStorage), lire navigator.language ou navigator.languages[0]
  • Mapper le code retourne (ex: fr-FR, en-US, de-DE) vers la locale la plus proche disponible
  • Logique de mapping : d'abord match exact, puis prefix (ex: fr-FRfr, zh-CNzh)
  • Sauvegarder le resultat dans localStorage pour les visites suivantes
  • Si aucune correspondance, fallback sur "en" (anglais comme langue par defaut internationale)

Fichier concerne : frontend/src/lib/i18n.tsx

Code de reference pour la detection :

function detectBrowserLocale(): Locale {
  const browserLang = navigator.language || (navigator.languages?.[0] ?? '');
  const prefix = browserLang.split('-')[0].toLowerCase();

  // Match exact d'abord
  if (SUPPORTED_LOCALES.includes(prefix as Locale)) {
    return prefix as Locale;
  }

  // Fallback anglais
  return 'en';
}

3b. Ajout de langues pour l'interface (UI)

Probleme : Le type Locale est limite a "fr" | "en" et le systeme de messages est inline dans i18n.tsx.

Nouvelles locales a ajouter :

Code Langue Priorite
de Deutsch P1
es Espanol P1
it Italiano P1
pt Portugues P1
nl Nederlands P1
ru Русский P1
pl Polski P2
sv Svenska P2
ro Romana P2
el Ελληνικα P2
cs Cestina P2
hu Magyar P2
uk Украська P2
da Dansk P3
fi Suomi P3
no Norsk P3
zh 中文 P1
ja 日本語 P1
ko 한국어 P1
fa فارسی P1

Architecture i18n :

  1. Transformer le systeme inline en fichiers separess :
    • frontend/src/messages/en.json (existant, a completer)
    • frontend/src/messages/fr.json (existant, a completer)
    • frontend/src/messages/de.json (nouveau)
    • frontend/src/messages/es.json (nouveau)
    • etc.
  2. Charger les messages dynamiquement (lazy loading) pour ne pas penaliser le bundle
  3. Le type Locale doit etre genere dynamiquement ou etendu

Fichiers concernes :

  • frontend/src/lib/i18n.tsx — refonte du systeme
  • frontend/src/messages/*.json — un fichier par langue
  • frontend/src/app/dashboard/profile/page.tsxUI_LANGUAGES a etendre

Contrainte RTL : Le persan (fa) est une langue droite-gauche (RTL). L'application doit :

  • Ajouter dir="rtl" sur <html> quand la locale est fa ou ar
  • Verifier que les composants Radix UI / Tailwind supportent le RTL (utilitaires rtl: de Tailwind)
  • Tester specifiquement les pages critiques en RTL (Translate, Profile, Settings)

3c. Ajout du persan dans les langues cibles de traduction

Probleme : Dans frontend/src/lib/api.ts (lignes 22-51), le persan (fa) est absent de la liste languages, alors que le backend le supporte deja dans middleware/validation.py (ligne 396).

Correction : Ajouter { code: "fa", name: "فارسی", flag: "🇮🇷" } dans le tableau languages de api.ts.

Note : La liste fallback dans useTranslationConfig.ts (ligne 38) inclut deja fa — seul le fichier api.ts est a corriger.


4. Automatisation du choix du provider de traduction

Probleme : Sur la page Translate, l'utilisateur doit manuellement choisir un provider (Google Traduction, DeepL, OpenAI, etc.) via le composant ProviderSelector. Pour un utilisateur basique, c'est une decision technique qu'il n'est pas qualifie pour prendre. Le backend a deja un systeme de fallback automatique.

Comportement actuel :

  • ProviderSelector.tsx affiche tous les providers disponibles
  • TranslationModeToggle.tsx propose un toggle Classic/LLM
  • useTranslationConfig.ts exige un provider selectionne pour valider la config (isConfigValid ligne 184-188)
  • Les providers LLM sont deja bloques pour les non-Pro

Nouveau comportement propose :

Pour les utilisateurs Free / Starter :

  • Le ProviderSelector et le TranslationModeToggle sont completement caches
  • Le provider est automatiquement selectionne : premier provider classic disponible
  • Si aucun provider classic n'est disponible, utiliser le premier provider de la liste
  • L'utilisateur ne voit que : fichier → langue source → langue cible → Traduire

Pour les utilisateurs Pro / Business / Enterprise :

  • Afficher un selecteur simplifie, pas technique :
    • Remplacer les noms techniques par des labels utilisateur :
      • « Traduction rapide » (classic) au lieu de « Google Traduction »
      • « Traduction intelligente » (LLM) au lieu de « OpenAI GPT-4 »
    • Pas de toggle Classic/LLM separe — juste un selecteur de mode avec descriptions claires
    • Les providers LLM sont accessibles sans cadenas (deja Pro)

Implementation technique :

Fichiers concernes :

  • frontend/src/app/dashboard/translate/ProviderSelector.tsx — cacher pour Free/Starter, simplifier pour Pro+
  • frontend/src/app/dashboard/translate/TranslationModeToggle.tsx — retirer, fusionner dans ProviderSelector simplifie
  • frontend/src/app/dashboard/translate/useTranslationConfig.ts — auto-selection du provider pour les utilisateurs basiques
  • frontend/src/app/dashboard/translate/page.tsx — adapter le layout selon le tier

Logique d'auto-selection (dans useTranslationConfig.ts) :

// Apres le fetch des providers disponibles
if (!isPro && availableProviders.length > 0) {
  // Auto-selectionner le premier provider classic
  const classicProvider = availableProviders.find(p => p.mode === 'classic');
  const fallback = availableProviders[0];
  setProvider((classicProvider ?? fallback).id);
}

Mise a jour de isConfigValid :

// Pour les non-Pro, le provider est auto-selectionne
// Donc isConfigValid ne depend plus de la selection manuelle
const isConfigValid = useMemo(() => {
  if (!hasFile || !targetLang) return false;
  if (isPro && !provider) return false; // Pro doit choisir
  return true; // Non-Pro : auto-selectionne
}, [hasFile, targetLang, isPro, provider]);

Nouveau composant simplifie pour Pro (remplace ProviderSelector + ModeToggle) :

// SimplifiedProviderSelector — uniquement pour Pro+
// Affiche :
// - "Traduction rapide" — Traduction automatique rapide (130+ langues)
// - "Traduction intelligente" — Traduction contextuelle par IA (Pro)

Plan d'implementation par ordre de priorite

Phase 1 — Quick Wins (impact eleve, complexite faible)

  1. Retirer « Services » de la sidebar et de la page Settings
  2. Ajouter fa (persan) dans api.ts
  3. Auto-selection du provider pour les utilisateurs Free/Starter
  4. Detection automatique de la langue du navigateur

Phase 2 — Refonte Profil (impact eleve, complexite moyenne)

  1. Refactorer la page Profil en 3 onglets
  2. Deplacer ThemeToggle dans l'onglet Preferences
  3. i18n-ifier tous les textes hardcodes du profil

Phase 3 — Extension i18n (impact eleve, complexite elevee)

  1. Refondre le systeme i18n en fichiers separes
  2. Ajouter les langues P1 (de, es, it, pt, nl, ru, zh, ja, ko, fa)
  3. Support RTL pour fa et ar
  4. Ajouter les langues P2 et P3 progressivement

Risques et mitigations

Risque Probabilite Impact Mitigation
Traductions i18n de mauvaise qualite Elevee Moyen Utiliser un service de traduction professionnel ou validation humaine pour les langues principales
Layout casse en RTL Moyen Eleve Tester avec fa et ar des le debut, ajouter les utilitaires Tailwind rtl:
Utilisateurs Pro perdus sans selecteur technique Faible Faible Garder le selecteur simplifie avec option avancee
Performance avec 20+ fichiers de langue Faible Faible Lazy loading des fichiers JSON par locale

Critères d'acceptation

  • Aucune entree « Services » visible dans la sidebar utilisateur
  • La page Profil est separee en 3 onglets fonctionnels
  • La langue de l'interface est detectee automatiquement depuis le navigateur
  • Le persan est disponible comme langue cible de traduction
  • Le persan est disponible comme langue de l'interface (avec support RTL)
  • L'utilisateur Free/Starter ne voit aucun selecteur de provider
  • L'utilisateur Pro voit un selecteur simplifie (non-technique)
  • Toutes les nouvelles langues UI ont des fichiers de messages complets
  • Le theme clair/sombre est accessible depuis l'onglet Preferences du profil