feat(frontend): scaffold i18n init, fr.json (~140 keys), status map, format helpers
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
478
frontend/src/i18n/fr.json
Normal file
478
frontend/src/i18n/fr.json
Normal file
@@ -0,0 +1,478 @@
|
|||||||
|
{
|
||||||
|
"common": {
|
||||||
|
"save": "Enregistrer",
|
||||||
|
"saving": "Enregistrement…",
|
||||||
|
"cancel": "Annuler",
|
||||||
|
"delete": "Supprimer",
|
||||||
|
"edit": "Modifier",
|
||||||
|
"new": "Nouveau",
|
||||||
|
"loading": "Chargement…",
|
||||||
|
"retry": "Réessayer",
|
||||||
|
"back": "Retour",
|
||||||
|
"dismiss": "Fermer",
|
||||||
|
"view": "Voir",
|
||||||
|
"create": "Créer",
|
||||||
|
"creating": "Création…",
|
||||||
|
"close": "Fermer",
|
||||||
|
"confirm": "Confirmer"
|
||||||
|
},
|
||||||
|
"nav": {
|
||||||
|
"engagements": "Engagements",
|
||||||
|
"templates": "Templates",
|
||||||
|
"users": "Utilisateurs",
|
||||||
|
"signOut": "Se déconnecter",
|
||||||
|
"brand": "Mimic"
|
||||||
|
},
|
||||||
|
"auth": {
|
||||||
|
"login": {
|
||||||
|
"title": "Mimic",
|
||||||
|
"subtitle": "Connectez-vous pour accéder à vos engagements.",
|
||||||
|
"username": "Nom d'utilisateur",
|
||||||
|
"password": "Mot de passe",
|
||||||
|
"signIn": "Se connecter",
|
||||||
|
"signingIn": "Connexion…",
|
||||||
|
"invalid": "Identifiants invalides"
|
||||||
|
},
|
||||||
|
"forbidden": "Accès refusé",
|
||||||
|
"loadingSession": "Chargement de la session…"
|
||||||
|
},
|
||||||
|
"engagement": {
|
||||||
|
"list": {
|
||||||
|
"title": "Engagements",
|
||||||
|
"subtitle": "Missions red team et leur statut.",
|
||||||
|
"new": "Nouveau",
|
||||||
|
"empty": {
|
||||||
|
"title": "Aucun engagement",
|
||||||
|
"desc": "Créez votre premier engagement pour commencer à suivre les missions red team."
|
||||||
|
},
|
||||||
|
"col": {
|
||||||
|
"name": "Nom",
|
||||||
|
"status": "Statut",
|
||||||
|
"start": "Début",
|
||||||
|
"end": "Fin",
|
||||||
|
"createdBy": "Créé par",
|
||||||
|
"actions": "Actions"
|
||||||
|
},
|
||||||
|
"view": "Voir",
|
||||||
|
"edit": "Modifier",
|
||||||
|
"delete": "Supprimer",
|
||||||
|
"deleteConfirm": "Supprimer l'engagement « {{name}} » ? Cette action est irréversible.",
|
||||||
|
"toast": {
|
||||||
|
"deleted": "Engagement supprimé"
|
||||||
|
},
|
||||||
|
"error": {
|
||||||
|
"load": "Impossible de charger les engagements",
|
||||||
|
"delete": "Impossible de supprimer l'engagement"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"detail": {
|
||||||
|
"tabs": {
|
||||||
|
"description": "Description",
|
||||||
|
"simulations": "Simulations"
|
||||||
|
},
|
||||||
|
"schedule": {
|
||||||
|
"startDate": "Date de début",
|
||||||
|
"endDate": "Date de fin",
|
||||||
|
"status": "Statut",
|
||||||
|
"createdAt": "Créé le"
|
||||||
|
},
|
||||||
|
"edit": "Modifier",
|
||||||
|
"createdBy": "Créé par",
|
||||||
|
"noDescription": "Aucune description fournie.",
|
||||||
|
"loading": "Chargement de l'engagement…",
|
||||||
|
"errorLoad": "Impossible de charger l'engagement",
|
||||||
|
"notFound": "Engagement introuvable",
|
||||||
|
"backTo": "Retour aux engagements"
|
||||||
|
},
|
||||||
|
"form": {
|
||||||
|
"title": {
|
||||||
|
"new": "Nouvel engagement",
|
||||||
|
"edit": "Modifier l'engagement"
|
||||||
|
},
|
||||||
|
"subtitle": {
|
||||||
|
"new": "Définir le périmètre et la planification.",
|
||||||
|
"edit": "Mettre à jour les détails."
|
||||||
|
},
|
||||||
|
"field": {
|
||||||
|
"name": "Nom",
|
||||||
|
"description": "Description",
|
||||||
|
"startDate": "Date de début",
|
||||||
|
"endDate": "Date de fin",
|
||||||
|
"endDateHint": "Optionnel",
|
||||||
|
"status": "Statut"
|
||||||
|
},
|
||||||
|
"validation": {
|
||||||
|
"nameRequired": "Le nom est obligatoire",
|
||||||
|
"startDateRequired": "La date de début est obligatoire",
|
||||||
|
"endDateAfterStart": "La date de fin doit être égale ou postérieure à la date de début"
|
||||||
|
},
|
||||||
|
"btn": {
|
||||||
|
"create": "Créer",
|
||||||
|
"save": "Enregistrer",
|
||||||
|
"saving": "Enregistrement…",
|
||||||
|
"cancel": "Annuler"
|
||||||
|
},
|
||||||
|
"toast": {
|
||||||
|
"created": "Engagement créé",
|
||||||
|
"updated": "Engagement mis à jour"
|
||||||
|
},
|
||||||
|
"error": {
|
||||||
|
"save": "Impossible d'enregistrer l'engagement",
|
||||||
|
"load": "Impossible de charger l'engagement"
|
||||||
|
},
|
||||||
|
"loading": "Chargement…",
|
||||||
|
"c2Config": "Configuration C2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"simulation": {
|
||||||
|
"form": {
|
||||||
|
"title": {
|
||||||
|
"new": "Nouvelle simulation"
|
||||||
|
},
|
||||||
|
"field": {
|
||||||
|
"name": "Nom",
|
||||||
|
"mitre": "Techniques et tactiques MITRE",
|
||||||
|
"description": "Description",
|
||||||
|
"commands": "Commandes",
|
||||||
|
"commandsHint": "Une commande par ligne",
|
||||||
|
"prerequisites": "Prérequis",
|
||||||
|
"executedAt": "Exécuté le",
|
||||||
|
"executionResult": "Résultat d'exécution",
|
||||||
|
"logSource": "Source de log",
|
||||||
|
"logs": "Logs",
|
||||||
|
"socComment": "Commentaire SOC",
|
||||||
|
"incidentNumber": "Numéro d'incident"
|
||||||
|
},
|
||||||
|
"btn": {
|
||||||
|
"create": "Créer la simulation",
|
||||||
|
"creating": "Création…",
|
||||||
|
"save": "Enregistrer",
|
||||||
|
"saving": "Enregistrement…",
|
||||||
|
"saveSoc": "Enregistrer SOC",
|
||||||
|
"markReview": "Marquer pour révision",
|
||||||
|
"close": "Clore",
|
||||||
|
"reopen": "Rouvrir",
|
||||||
|
"delete": "Supprimer",
|
||||||
|
"executeC2": "Exécuter via C2",
|
||||||
|
"importC2History": "Importer l'historique C2",
|
||||||
|
"cancel": "Annuler"
|
||||||
|
},
|
||||||
|
"deleteConfirm": {
|
||||||
|
"title": "Supprimer la simulation",
|
||||||
|
"desc": "Cette action est permanente. La simulation sera définitivement supprimée.",
|
||||||
|
"confirm": "Supprimer",
|
||||||
|
"cancel": "Annuler"
|
||||||
|
},
|
||||||
|
"banner": {
|
||||||
|
"done": "Cette simulation est terminée et en lecture seule. Utilisez Rouvrir pour effectuer des modifications.",
|
||||||
|
"socNotReady": "La simulation n'est pas encore prête pour la révision — l'équipe red team doit la marquer comme « Révision requise » avant que vous puissiez remplir la section SOC."
|
||||||
|
},
|
||||||
|
"header": {
|
||||||
|
"redTeam": "Red Team",
|
||||||
|
"soc": "SOC"
|
||||||
|
},
|
||||||
|
"validation": {
|
||||||
|
"nameRequired": "Le nom est obligatoire"
|
||||||
|
},
|
||||||
|
"toast": {
|
||||||
|
"created": "Simulation créée",
|
||||||
|
"updated": "Simulation mise à jour",
|
||||||
|
"deleted": "Simulation supprimée",
|
||||||
|
"markedReview": "Simulation marquée pour révision",
|
||||||
|
"closed": "Simulation close",
|
||||||
|
"reopened": "Simulation rouverte",
|
||||||
|
"socUpdated": "Rapport SOC mis à jour"
|
||||||
|
},
|
||||||
|
"error": {
|
||||||
|
"create": "Impossible de créer la simulation",
|
||||||
|
"update": "Impossible de mettre à jour la simulation",
|
||||||
|
"delete": "Impossible de supprimer la simulation",
|
||||||
|
"soc": "Impossible de mettre à jour les champs SOC",
|
||||||
|
"transition": "Transition échouée",
|
||||||
|
"load": "Impossible de charger la simulation"
|
||||||
|
},
|
||||||
|
"loading": "Chargement de la simulation…"
|
||||||
|
},
|
||||||
|
"list": {
|
||||||
|
"col": {
|
||||||
|
"name": "Nom",
|
||||||
|
"mitre": "MITRE",
|
||||||
|
"status": "Statut",
|
||||||
|
"executedAt": "Exécuté le"
|
||||||
|
},
|
||||||
|
"empty": {
|
||||||
|
"title": "Aucune simulation",
|
||||||
|
"desc": "Créez votre première simulation pour cet engagement."
|
||||||
|
},
|
||||||
|
"new": "Nouvelle simulation",
|
||||||
|
"error": "Impossible de charger les simulations"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"template": {
|
||||||
|
"list": {
|
||||||
|
"title": "Templates",
|
||||||
|
"subtitle": "Modèles de simulation réutilisables.",
|
||||||
|
"new": "Nouveau",
|
||||||
|
"empty": {
|
||||||
|
"title": "Aucun template",
|
||||||
|
"desc": "Créez votre premier template de simulation."
|
||||||
|
},
|
||||||
|
"col": {
|
||||||
|
"name": "Nom",
|
||||||
|
"mitre": "MITRE",
|
||||||
|
"createdBy": "Créé par",
|
||||||
|
"updated": "Modifié"
|
||||||
|
},
|
||||||
|
"edit": "Modifier",
|
||||||
|
"delete": "Supprimer"
|
||||||
|
},
|
||||||
|
"form": {
|
||||||
|
"title": {
|
||||||
|
"new": "Nouveau template",
|
||||||
|
"edit": "Modifier le template"
|
||||||
|
},
|
||||||
|
"field": {
|
||||||
|
"name": "Nom",
|
||||||
|
"description": "Description",
|
||||||
|
"descriptionPlaceholder": "Description du template…",
|
||||||
|
"commands": "Commandes",
|
||||||
|
"commandsHint": "Une commande par ligne",
|
||||||
|
"commandsPlaceholder": "whoami\nnet user",
|
||||||
|
"prerequisites": "Prérequis",
|
||||||
|
"prerequisitesPlaceholder": "Prérequis…",
|
||||||
|
"mitre": "Techniques MITRE",
|
||||||
|
"mitreSearch": "Rechercher une technique…",
|
||||||
|
"mitreOpenMatrix": "Ouvrir la matrice",
|
||||||
|
"mitreEmpty": "Aucune technique associée."
|
||||||
|
},
|
||||||
|
"btn": {
|
||||||
|
"save": "Enregistrer",
|
||||||
|
"saving": "Enregistrement…",
|
||||||
|
"delete": "Supprimer",
|
||||||
|
"cancel": "Annuler"
|
||||||
|
},
|
||||||
|
"validation": {
|
||||||
|
"nameRequired": "Le nom est obligatoire"
|
||||||
|
},
|
||||||
|
"toast": {
|
||||||
|
"created": "Template créé",
|
||||||
|
"saved": "Template enregistré",
|
||||||
|
"deleted": "Template supprimé"
|
||||||
|
},
|
||||||
|
"deleteConfirm": {
|
||||||
|
"title": "Supprimer le template",
|
||||||
|
"desc": "Cette action est permanente.",
|
||||||
|
"confirm": "Supprimer",
|
||||||
|
"cancel": "Annuler"
|
||||||
|
},
|
||||||
|
"loading": "Chargement du template…",
|
||||||
|
"errorLoad": "Impossible de charger le template",
|
||||||
|
"errorSave": "Impossible d'enregistrer le template",
|
||||||
|
"errorDelete": "Impossible de supprimer le template"
|
||||||
|
},
|
||||||
|
"picker": {
|
||||||
|
"title": "Choisir un template",
|
||||||
|
"empty": "Aucun template disponible.",
|
||||||
|
"loading": "Chargement…"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"user": {
|
||||||
|
"admin": {
|
||||||
|
"title": "Utilisateurs",
|
||||||
|
"subtitle": "Gestion des comptes.",
|
||||||
|
"new": "Nouveau",
|
||||||
|
"col": {
|
||||||
|
"username": "Nom d'utilisateur",
|
||||||
|
"role": "Rôle",
|
||||||
|
"createdAt": "Créé le",
|
||||||
|
"actions": "Actions"
|
||||||
|
},
|
||||||
|
"role": {
|
||||||
|
"admin": "Admin",
|
||||||
|
"redteam": "Red Team",
|
||||||
|
"soc": "SOC"
|
||||||
|
},
|
||||||
|
"field": {
|
||||||
|
"username": "Nom d'utilisateur",
|
||||||
|
"password": "Mot de passe"
|
||||||
|
},
|
||||||
|
"btn": {
|
||||||
|
"create": "Créer",
|
||||||
|
"creating": "Création…",
|
||||||
|
"resetPassword": "Réinitialiser le mot de passe",
|
||||||
|
"setPassword": "Définir le mot de passe",
|
||||||
|
"delete": "Supprimer",
|
||||||
|
"cancel": "Annuler"
|
||||||
|
},
|
||||||
|
"you": "(vous)",
|
||||||
|
"toast": {
|
||||||
|
"created": "Utilisateur créé",
|
||||||
|
"deleted": "Utilisateur supprimé",
|
||||||
|
"passwordReset": "Mot de passe réinitialisé"
|
||||||
|
},
|
||||||
|
"error": {
|
||||||
|
"create": "Impossible de créer l'utilisateur",
|
||||||
|
"delete": "Impossible de supprimer l'utilisateur",
|
||||||
|
"passwordReset": "Impossible de réinitialiser le mot de passe"
|
||||||
|
},
|
||||||
|
"deleteConfirm": "Supprimer l'utilisateur « {{username}} » ?",
|
||||||
|
"newPassword": "Nouveau mot de passe",
|
||||||
|
"empty": {
|
||||||
|
"title": "Aucun utilisateur",
|
||||||
|
"desc": "Créez le premier compte via le formulaire ci-dessus."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"c2": {
|
||||||
|
"config": {
|
||||||
|
"title": "Configuration C2",
|
||||||
|
"disabled": "Les fonctionnalités C2 sont désactivées (aucune clé de chiffrement configurée sur le serveur).",
|
||||||
|
"field": {
|
||||||
|
"url": "URL",
|
||||||
|
"urlHint": "HTTPS requis (ex. https://mythic.lab:7443)",
|
||||||
|
"token": "Token API",
|
||||||
|
"tokenHint": "Token API",
|
||||||
|
"verifyTls": "Vérifier le certificat TLS",
|
||||||
|
"verifyTlsHint": "Décochez uniquement pour un Mythic de lab avec des certificats auto-signés. Désactiver la vérification expose le token API aux attaques MITM."
|
||||||
|
},
|
||||||
|
"btn": {
|
||||||
|
"save": "Enregistrer",
|
||||||
|
"saving": "Enregistrement…",
|
||||||
|
"test": "Tester la connexion",
|
||||||
|
"testing": "Test…",
|
||||||
|
"delete": "Supprimer la configuration",
|
||||||
|
"replaceToken": "Remplacer le token"
|
||||||
|
},
|
||||||
|
"toast": {
|
||||||
|
"saved": "Configuration C2 enregistrée",
|
||||||
|
"deleted": "Configuration C2 supprimée"
|
||||||
|
},
|
||||||
|
"error": {
|
||||||
|
"save": "Impossible d'enregistrer la configuration C2",
|
||||||
|
"delete": "Impossible de supprimer la configuration C2"
|
||||||
|
},
|
||||||
|
"connected": "Connecté",
|
||||||
|
"connectionFailed": "Connexion échouée",
|
||||||
|
"deleteConfirm": {
|
||||||
|
"title": "Supprimer la configuration C2",
|
||||||
|
"desc": "La configuration C2 de cet engagement sera supprimée. Le token API sera définitivement effacé.",
|
||||||
|
"confirm": "Supprimer",
|
||||||
|
"cancel": "Annuler"
|
||||||
|
},
|
||||||
|
"loading": "Chargement…"
|
||||||
|
},
|
||||||
|
"tasks": {
|
||||||
|
"title": "Tâches C2",
|
||||||
|
"empty": "Aucune tâche C2.",
|
||||||
|
"col": {
|
||||||
|
"task": "Tâche",
|
||||||
|
"command": "Commande",
|
||||||
|
"source": "Source",
|
||||||
|
"status": "Statut",
|
||||||
|
"completedAt": "Terminé le"
|
||||||
|
},
|
||||||
|
"refreshing": "Actualisation…"
|
||||||
|
},
|
||||||
|
"modal": {
|
||||||
|
"execute": {
|
||||||
|
"title": "Exécuter via C2",
|
||||||
|
"callback": "Agent cible",
|
||||||
|
"btn": {
|
||||||
|
"launch": "Lancer",
|
||||||
|
"launching": "Lancement…",
|
||||||
|
"cancel": "Annuler"
|
||||||
|
},
|
||||||
|
"commandCount_one": "{{count}} commande",
|
||||||
|
"commandCount_other": "{{count}} commandes",
|
||||||
|
"validation": {
|
||||||
|
"callbackRequired": "Sélectionnez un agent",
|
||||||
|
"commandsRequired": "Aucune commande à exécuter"
|
||||||
|
},
|
||||||
|
"toast": {
|
||||||
|
"launched": "Tâche C2 lancée"
|
||||||
|
},
|
||||||
|
"error": {
|
||||||
|
"launch": "Impossible de lancer la tâche C2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"import": {
|
||||||
|
"title": "Importer l'historique C2",
|
||||||
|
"total": "Total",
|
||||||
|
"page": "Page",
|
||||||
|
"of": "sur",
|
||||||
|
"prev": "Précédent",
|
||||||
|
"next": "Suivant",
|
||||||
|
"btn": {
|
||||||
|
"import": "Importer",
|
||||||
|
"importing": "Importation…",
|
||||||
|
"cancel": "Annuler"
|
||||||
|
},
|
||||||
|
"selected": "sélectionné(s)",
|
||||||
|
"empty": "Aucun historique C2 disponible.",
|
||||||
|
"toast": {
|
||||||
|
"imported": "Historique C2 importé",
|
||||||
|
"partial": "Import partiel"
|
||||||
|
},
|
||||||
|
"error": {
|
||||||
|
"import": "Impossible d'importer l'historique C2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"picker": {
|
||||||
|
"title": "Sélectionner un agent",
|
||||||
|
"empty": "Aucun agent disponible.",
|
||||||
|
"hostnameColon": "Hôte :",
|
||||||
|
"lastCheckinColon": "Dernière connexion :"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"mitre": {
|
||||||
|
"matrix": {
|
||||||
|
"title": "Matrice MITRE ATT&CK",
|
||||||
|
"filter": "Filtrer…",
|
||||||
|
"loading": "Chargement de la matrice…",
|
||||||
|
"error": "Impossible de charger la matrice MITRE",
|
||||||
|
"applyItem_one": "Appliquer ({{count}} élément)",
|
||||||
|
"applyItem_other": "Appliquer ({{count}} éléments)",
|
||||||
|
"clearAll": "Tout effacer",
|
||||||
|
"close": "Fermer",
|
||||||
|
"retry": "Réessayer"
|
||||||
|
},
|
||||||
|
"field": {
|
||||||
|
"empty": "Aucune technique associée.",
|
||||||
|
"search": "Rechercher une technique…",
|
||||||
|
"openMatrix": "Ouvrir la matrice",
|
||||||
|
"savedToast": "Technique ajoutée",
|
||||||
|
"errorToast": "Impossible d'ajouter la technique"
|
||||||
|
},
|
||||||
|
"tag": {
|
||||||
|
"remove": "Retirer"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"state": {
|
||||||
|
"loading": "Chargement…",
|
||||||
|
"error": {
|
||||||
|
"title": "Erreur",
|
||||||
|
"desc": "Une erreur est survenue."
|
||||||
|
},
|
||||||
|
"empty": {
|
||||||
|
"default": "Aucun résultat."
|
||||||
|
},
|
||||||
|
"retry": "Réessayer"
|
||||||
|
},
|
||||||
|
"toast": {
|
||||||
|
"dismiss": "Fermer"
|
||||||
|
},
|
||||||
|
"status": {
|
||||||
|
"engagement": {
|
||||||
|
"planned": "Planifié",
|
||||||
|
"active": "Actif",
|
||||||
|
"closed": "Clôturé"
|
||||||
|
},
|
||||||
|
"simulation": {
|
||||||
|
"pending": "En attente",
|
||||||
|
"in_progress": "En cours",
|
||||||
|
"review_required": "Révision requise",
|
||||||
|
"done": "Terminé"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
13
frontend/src/i18n/index.ts
Normal file
13
frontend/src/i18n/index.ts
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import i18n from 'i18next';
|
||||||
|
import { initReactI18next } from 'react-i18next';
|
||||||
|
import fr from './fr.json';
|
||||||
|
|
||||||
|
i18n.use(initReactI18next).init({
|
||||||
|
resources: { fr: { translation: fr } },
|
||||||
|
lng: 'fr',
|
||||||
|
fallbackLng: 'fr',
|
||||||
|
interpolation: { escapeValue: false },
|
||||||
|
returnEmptyString: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default i18n;
|
||||||
8
frontend/src/i18n/status.ts
Normal file
8
frontend/src/i18n/status.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import i18n from './index';
|
||||||
|
import type { EngagementStatus, SimulationStatus } from '@/api/types';
|
||||||
|
|
||||||
|
export const engagementStatusLabel = (s: EngagementStatus): string =>
|
||||||
|
i18n.t(`status.engagement.${s}`);
|
||||||
|
|
||||||
|
export const simulationStatusLabel = (s: SimulationStatus): string =>
|
||||||
|
i18n.t(`status.simulation.${s}`);
|
||||||
7
frontend/src/lib/format.ts
Normal file
7
frontend/src/lib/format.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
export const formatDate = (iso: string | null | undefined): string => {
|
||||||
|
if (!iso) return '—';
|
||||||
|
return new Date(iso).toLocaleDateString('fr-FR');
|
||||||
|
};
|
||||||
|
|
||||||
|
export const formatDateTime = (iso: string): string =>
|
||||||
|
new Date(iso).toLocaleString('fr-FR');
|
||||||
@@ -5,6 +5,7 @@ import { BrowserRouter } from 'react-router-dom';
|
|||||||
import { App } from './App';
|
import { App } from './App';
|
||||||
import { AuthProvider } from './hooks/useAuth';
|
import { AuthProvider } from './hooks/useAuth';
|
||||||
import { ToastProvider } from './hooks/useToast';
|
import { ToastProvider } from './hooks/useToast';
|
||||||
|
import './i18n';
|
||||||
import './styles/index.css';
|
import './styles/index.css';
|
||||||
|
|
||||||
const queryClient = new QueryClient({
|
const queryClient = new QueryClient({
|
||||||
|
|||||||
46
frontend/tests/i18n.test.ts
Normal file
46
frontend/tests/i18n.test.ts
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
import { describe, expect, it } from 'vitest';
|
||||||
|
import i18n from '@/i18n/index';
|
||||||
|
import { engagementStatusLabel, simulationStatusLabel } from '@/i18n/status';
|
||||||
|
import { formatDate, formatDateTime } from '@/lib/format';
|
||||||
|
|
||||||
|
describe('i18n smoke tests', () => {
|
||||||
|
it('resolves common.save to French', () => {
|
||||||
|
expect(i18n.t('common.save')).toBe('Enregistrer');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('missing key returns the key path', () => {
|
||||||
|
expect(i18n.t('this.key.does.not.exist')).toBe('this.key.does.not.exist');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('simulationStatusLabel done → Terminé', () => {
|
||||||
|
expect(simulationStatusLabel('done')).toBe('Terminé');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('engagementStatusLabel active → Actif', () => {
|
||||||
|
expect(engagementStatusLabel('active')).toBe('Actif');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('interpolation inserts the value', () => {
|
||||||
|
expect(i18n.t('engagement.list.deleteConfirm', { name: 'Test' })).toContain('Test');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('format helpers', () => {
|
||||||
|
it('formatDate returns — for null', () => {
|
||||||
|
expect(formatDate(null)).toBe('—');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('formatDate returns — for undefined', () => {
|
||||||
|
expect(formatDate(undefined)).toBe('—');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('formatDate formats ISO date in fr-FR', () => {
|
||||||
|
const result = formatDate('2026-06-01');
|
||||||
|
expect(result).toMatch(/\d{2}\/\d{2}\/\d{4}/);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('formatDateTime returns a non-empty string for valid ISO', () => {
|
||||||
|
const result = formatDateTime('2026-06-01T08:00:00');
|
||||||
|
expect(result.length).toBeGreaterThan(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user