Sprint 12 — Full FR i18n via react-i18next + EngagementDetailPage tab merge : Mimic est désormais entièrement en français pour les opérateurs CACIB, depuis l'infrastructure (react-i18next + JSON FR) jusqu'aux aria-labels et au footer. Toutes les dates basculées en fr-FR (jj/mm/aaaa). 13 commits livrés + le 2c7fcec du sprint 11 (build fix text-caption-*).
EngagementDetailPage : passé de 3 tabs à 2 tabs (Description + Simulations). Le contenu Schedule (dates de début/fin, statut, créé le, bouton Modifier) devient un header block dans le panel Description.
Acronymes en EN (locked) : RT, SOC, MITRE, ATT&CK, C2, BAS, TLS, MITM, JWT, IP, URL, API, ID. Brand "Mimic" gardé. "Red Team" reste en idiom.
Status enum : valeurs DB en EN inchangées (pending/active/done/etc.), traduction au rendering layer via i18n/status.ts + hook useTranslation() inside StatusBadge / SimulationStatusBadge (subscription réactive correcte).
Date format : formatDate() / formatDateTime() dans lib/format.ts utilisent toLocaleDateString('fr-FR') / toLocaleString('fr-FR'). Appliqués à 5 sites (engagements list/detail, users admin, templates list, simulations list).
Reviews 2 passes : design-review (4 MAJOR + 2 MINOR patched) → code-review (1 MAJOR + 4 MINOR patched). Tous APPROVED post-polish.
Dépendance — sprint 11
Cette branche est basée sur sprint/11-spectrum-ux (PR #13 toujours ouverte). Le diff inclura temporairement les commits sprint 11 — il se réduira automatiquement à sprint 12 seul une fois PR #13 mergée. Merger PR #13 avant celui-ci pour un diff propre.
Build : npm run build (= vite build) clean — PostCSS résout tous les @apply, aucune classe Tailwind non-résolue (leçon sprint 11 feedback_css_apply_tokens).
HMR cache : node_modules/.vite bustéavant la prise des screenshots de validation finale — élimine la suspicion de stale bundle (code-reviewer flag REG 1).
E2E : non re-joué (mur Vite proxy connu). Smoke manuel ci-dessous.
Comment tester en local
git checkout sprint/12-i18n-fr
make build && make start
# http://127.0.0.1:5000
make create-admin USER=alice PASS=changeme8 # si premier setup
Scénarios :
Login en FR — Mimic (brand), Connectez-vous pour accéder à vos engagements., fields Nom d'utilisateur / Mot de passe, bouton Se connecter.
Nav — Engagements / Templates / Utilisateurs / Se déconnecter. Footer FR : Mimic — Outillage interne Purple Team. Engagements autorisés uniquement.
Engagements list — title Engagements, subtitle Missions red team et leur statut., columns NOM / STATUT / DÉBUT / FIN / CRÉÉ PAR / ACTIONS, badges status FR (Planifié / Actif / Clôturé), dates jj/mm/aaaa, actions Voir / Modifier / Supprimer.
Engagement detail (2 tabs) — back link Retour aux engagements, badge status FR juste sous le titre (Actif), tabs Description (default) + Simulations avec count pill. Dans le panel Description : header block avec Date de début / Date de fin / Statut (badge) / Créé le + bouton MODIFIER, séparateur, puis Description text. Boutons header droite : EXPORTER + MODIFIER.
Engagement form (edit) — labels FR, validation messages FR (Le nom est requis, etc.), boutons Enregistrer les modifications / Annuler, toast Engagement mis à jour.
Simulation form — toutes les sections (Red Team / SOC), tous les boutons, banners success/warn en FR, status badges traduits réactivement.
Templates — list + form + picker modal en FR.
Users admin — rôles FR (admin / redteam / soc peuvent rester en values), labels en FR, dates jj/mm/aaaa.
MITRE matrix — modal title Matrice MITRE ATT&CK, filter Filtrer, empty state Aucun résultat, aria-labels Replier {{id}} / Déplier {{id}}.
C2 config card — title Configuration C2, fields en FR, checkbox Vérifier le certificat TLS + helper text MITM en FR (depuis sprint 10).
Q2 : Acronymes restent en EN (RT/SOC/MITRE/etc., brand Mimic, Red Team idiom)
Q3 : Status enum DB en EN, traduction au rendering layer via i18n/status.ts
Q4 : Format date fr-FR (jj/mm/aaaa)
Q5 : Ton toasts/alerts bref (Engagement créé, pas Engagement enregistré avec succès)
Q6 : EngagementDetailPage 3→2 tabs (Schedule fusionné dans Description)
Patterns notables
i18n init avant App mount : main.tsx importe ./i18n avant <App /> pour éviter les premières renders avec keys raw.
Réactivité badges : useTranslation() inline dans les composants visibles → re-render automatique si la langue change (et timing-safe vis-à-vis HMR).
Helpers non-réactifs : engagementStatusLabel() / simulationStatusLabel() gardés pour les call sites hors-component (form options, banner content construction, etc.).
Date helpers monolocale : formatDate / formatDateTime hardcodent 'fr-FR' — pas de prop locale, pas de switcher. Si multi-langue un jour, c'est ici qu'on passera à i18n.language.
Variable shadowing évité : les loop vars t ont été renommés en tech / tac / tpl / toast partout pour ne pas masquer le t de useTranslation().
"Simulations" double label+h1 (design MINOR 2) : le tab "Simulations" et l'h1 du panel font doublon visuel. Pre-existing depuis sprint 11. Design call séparé pour le futur (probablement supprimer l'h1).
Multi-langue : infra prête pour ajouter en.json si jamais besoin, mais aucun switcher UI ni détection navigator. FR-only.
Backend i18n : zéro touché. Les error messages backend (rares pour l'utilisateur) restent en EN — à voir si nécessaire un jour.
ForbiddenState / NotFoundState : composants jamais créés (pas dans le code), pas de keys i18n.
NITs rejetés
text-caption-md → caption-md (design NIT 1) : rejeté. Le sprint 11 build-fix a établi que les fontSize tokens (registered dans theme.extend) génèrent des utilities text-<key>, jamais des bare-name utilities. Le prefix text- est correct, omettre casserait PostCSS. Documenté dans feedback_css_apply_tokens.md.
Commits (14 sur la branche, dont 12 nouveaux sprint 12)
3cdfba6 feat(frontend): merge Schedule tab into Description on EngagementDetailPage
51fb2e4 chore(frontend): install react-i18next + i18next deps
3723bd0 feat(frontend): scaffold i18n init, fr.json, status map, format helpers
fe597e9 feat(frontend): i18n common + nav + auth (Layout, LoginPage, ProtectedRoute)
ea870af feat(frontend): i18n engagement pages + fr-FR date formatting
5b93f88 feat(frontend): i18n simulation pages
284494c feat(frontend): i18n template pages
bdda02b feat(frontend): i18n MITRE components
ad0a3f5 feat(frontend): i18n C2 components
2931e4a feat(frontend): i18n shared state components + UsersAdminPage + CHANGELOG
6648d1c fix(frontend): translate status badge labels via i18n helpers
ac0bc8d fix(frontend): i18n reactive badges + fr-FR dates on detail/admin + FR footer (design-review)
2ff097b fix(frontend): i18n ExportEngagementButton + aria-labels + MitreMatrix noMatch (code-review)
(Plus 2c7fcec du sprint 11 trail tail, sera absorbé par le merge.)
border-l-4 semantic strip, bg-paper, Lucide icons at size=16.
ARIA role="alert" for error/warn, role="status" for success/info.
No shadow, no radius, no transition.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add table-compact class to all 4 list surfaces: EngagementsListPage,
TemplatesListPage, UsersAdminPage, SimulationList. Remove inline
px-xl py-md from th/td — recipe handles padding. Row border-b moved
to recipe. WCAG SC 2.5.5 tradeoff accepted (BAS operator tool).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
4 new spec files adding 21 tests (7+6+4+4). Each component spec
includes brutalism invariant assertions (no rounded-md, no transition-*,
no shadow-*).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Fix 1 (A): move row height constraint from min-height on tbody tr
(CSS no-op on table-row) to height: 32px on td (works on table-cell).
Fix 2 (A): table header text-[11px] -> text-[12px] to align with
documented caption scale.
Fix 3: add aria-controls + id to Tabs buttons; wrap active tab content
in role="tabpanel" + aria-labelledby in EngagementDetailPage.
Test: add aria-controls/id assertion to Tabs.test.tsx (234 total).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Fix 4: useHashTab navigate() uses history.replaceState instead of
window.location.hash assignment — no spurious history entries, no
anchor-jump scroll side-effect.
Fix 5: Tabs ArrowLeft/ArrowRight keyboard nav (WAI-ARIA tabs pattern).
Fix 6: TabId union type in EngagementDetailPage, cast from string for
type-safe switch on activeTab without breaking Tabs.onChange signature.
Fix 7: intentional comment on UsersAdminPage reset-password aerated row.
Tests: 236/236 (+2 arrow-key assertions in Tabs.test.tsx).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
caption-bold and caption-md are fontSize tokens — Tailwind generates
text-caption-bold / text-caption-md utilities, not bare-name utilities.
Missing text- prefix caused @apply resolution failure in vite build
(PostCSS step), while vitest passed because it does not resolve @apply.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Reduces from 3 tabs (Schedule / Description / Simulations) to 2
(Description / Simulations). Schedule content (start/end/status/created_at
+ Edit button) moved into a header block at the top of the Description
panel, separated from description text by a hairline <hr>. Default tab
changed from 'schedule' to 'description'.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
StatusBadge and SimulationStatusBadge now call engagementStatusLabel() /
simulationStatusLabel() from @/i18n/status.ts instead of hardcoded English
strings. Tests updated to expect French labels (Planifié/Actif/Clôturé,
En attente/En cours/Révision requise/Terminé).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- StatusBadge/SimulationStatusBadge: swap bare i18n.t() helper for
useTranslation() hook so badges re-render reactively on i18n init
- EngagementDetailPage: formatDate() on start_date/end_date/created_at
(was: raw ISO string, formatDateTime with noisy 00:00:00)
- UsersAdminPage: formatDate() on created_at column (was: raw ISO string)
- Layout footer: t('nav.footer') with FR string in fr.json
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Summary
fr-FR(jj/mm/aaaa). 13 commits livrés + le2c7fcecdu sprint 11 (build fixtext-caption-*).pending/active/done/etc.), traduction au rendering layer viai18n/status.ts+ hookuseTranslation()insideStatusBadge/SimulationStatusBadge(subscription réactive correcte).formatDate()/formatDateTime()danslib/format.tsutilisenttoLocaleDateString('fr-FR')/toLocaleString('fr-FR'). Appliqués à 5 sites (engagements list/detail, users admin, templates list, simulations list).Dépendance — sprint 11
Cette branche est basée sur
sprint/11-spectrum-ux(PR #13 toujours ouverte). Le diff inclura temporairement les commits sprint 11 — il se réduira automatiquement à sprint 12 seul une fois PR #13 mergée. Merger PR #13 avant celui-ci pour un diff propre.Test plan
tsc --noEmitclean,eslint --max-warnings=0clean.npm run build(=vite build) clean — PostCSS résout tous les@apply, aucune classe Tailwind non-résolue (leçon sprint 11feedback_css_apply_tokens).node_modules/.vitebustéavant la prise des screenshots de validation finale — élimine la suspicion de stale bundle (code-reviewer flag REG 1).Comment tester en local
Scénarios :
Mimic(brand),Connectez-vous pour accéder à vos engagements., fieldsNom d'utilisateur/Mot de passe, boutonSe connecter.Engagements/Templates/Utilisateurs/Se déconnecter. Footer FR :Mimic — Outillage interne Purple Team. Engagements autorisés uniquement.Engagements, subtitleMissions red team et leur statut., columnsNOM / STATUT / DÉBUT / FIN / CRÉÉ PAR / ACTIONS, badges status FR (Planifié/Actif/Clôturé), datesjj/mm/aaaa, actionsVoir/Modifier/Supprimer.Retour aux engagements, badge status FR juste sous le titre (Actif), tabsDescription(default) +Simulationsavec count pill. Dans le panel Description : header block avecDate de début/Date de fin/Statut(badge) /Créé le+ boutonMODIFIER, séparateur, puisDescriptiontext. Boutons header droite :EXPORTER+MODIFIER.Le nom est requis, etc.), boutonsEnregistrer les modifications/Annuler, toastEngagement mis à jour.success/warnen FR, status badges traduits réactivement.admin/redteam/socpeuvent rester en values), labels en FR, datesjj/mm/aaaa.Matrice MITRE ATT&CK, filterFiltrer, empty stateAucun résultat, aria-labelsReplier {{id}}/Déplier {{id}}.Configuration C2, fields en FR, checkboxVérifier le certificat TLS+ helper text MITM en FR (depuis sprint 10).Fichiers nouveaux
frontend/src/i18n/index.tsfrontend/src/i18n/fr.jsonfrontend/src/i18n/status.tsengagementStatusLabel/simulationStatusLabelnon-réactifs (call sites hors-component)frontend/tests/i18n.test.tsFichiers modifiés (sample, ~30 src files + 23 test files)
frontend/src/main.tsximport './i18n'avant<App />frontend/src/lib/format.tsformatDate/formatDateTimewrappersfr-FRfrontend/src/components/StatusBadge.tsxuseTranslation()réactiffrontend/src/components/SimulationStatusBadge.tsxuseTranslation()réactiffrontend/src/components/Layout.tsxt()frontend/src/components/ExportEngagementButton.tsxExporter+ aria + loading)frontend/src/components/AlertBanner.tsxfrontend/src/components/MitreMatrixModal.tsxAucun résultat+ ariaReplier/Déplier {{id}}frontend/src/components/C2*.tsxfrontend/src/pages/EngagementDetailPage.tsxfrontend/src/pages/LoginPage.tsxfrontend/src/pages/EngagementsListPage.tsxfrontend/src/pages/EngagementFormPage.tsxfrontend/src/pages/SimulationFormPage.tsxfrontend/src/pages/TemplatesListPage.tsxfrontend/src/pages/TemplateFormPage.tsxfrontend/src/pages/UsersAdminPage.tsxdata-testid="new-role-select"CHANGELOG.mdDécisions verrouillées (de la phase planning)
react-i18nextcomplet (deps + hook + JSON FR + interpolation + pluralization)i18n/status.tsfr-FR(jj/mm/aaaa)Engagement créé, pasEngagement enregistré avec succès)Patterns notables
main.tsximporte./i18navant<App />pour éviter les premières renders avec keys raw.useTranslation()inline dans les composants visibles → re-render automatique si la langue change (et timing-safe vis-à-vis HMR).engagementStatusLabel()/simulationStatusLabel()gardés pour les call sites hors-component (form options, banner content construction, etc.).formatDate/formatDateTimehardcodent'fr-FR'— pas de prop locale, pas de switcher. Si multi-langue un jour, c'est ici qu'on passera ài18n.language.tont été renommés entech/tac/tpl/toastpartout pour ne pas masquer letdeuseTranslation()._one/_other(c2.modal.execute.commandCount, c2.modal.import.toast.imported/partial, mitre.matrix.applyItem).Out of scope (reconnu, sprint 13+)
en.jsonsi jamais besoin, mais aucun switcher UI ni détection navigator. FR-only.ForbiddenState/NotFoundState: composants jamais créés (pas dans le code), pas de keys i18n.NITs rejetés
text-caption-md→caption-md(design NIT 1) : rejeté. Le sprint 11 build-fix a établi que les fontSize tokens (registered danstheme.extend) génèrent des utilitiestext-<key>, jamais des bare-name utilities. Le prefixtext-est correct, omettre casserait PostCSS. Documenté dansfeedback_css_apply_tokens.md.Commits (14 sur la branche, dont 12 nouveaux sprint 12)
(Plus
2c7fcecdu sprint 11 trail tail, sera absorbé par le merge.)🤖 Generated with Claude Code
Tabs: Schedule (default) / Description / Simulations, wired via useHashTab('schedule'). Count pill on Simulations tab. BackLink replaces hand-rolled Link. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>- StatusBadge/SimulationStatusBadge: swap bare i18n.t() helper for useTranslation() hook so badges re-render reactively on i18n init - EngagementDetailPage: formatDate() on start_date/end_date/created_at (was: raw ISO string, formatDateTime with noisy 00:00:00) - UsersAdminPage: formatDate() on created_at column (was: raw ISO string) - Layout footer: t('nav.footer') with FR string in fr.json Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>