Files
benevoles/frontend/src/i18n/locales/fr.json
T
AdamThomasandClaude Sonnet 5 e47c29d35b Front : suggestions du solveur en tête de liste dans la grille
- Surface API `solver.run(editionId)` (HTTP + mock) et hooks
  `useRunSolver` (mutation, alimente le cache) / `useSolverSuggestions`
  (lecture seule du cache, ne déclenche jamais de calcul).
- Grille des besoins : bouton « Suggestions (solveur) », affiche le
  nombre de suggestions calculées.
- Panneau d'affectation d'un créneau : les bénévoles suggérés par le
  solveur passent en tête de la liste des candidats (avant le tri par
  disponibilité / charge), avec un badge « Suggéré ». Ne pré-affecte
  jamais personne — la coordination choisit et confirme comme avant.
- Mock : portage glouton (par créneau, du plus urgent au moins urgent)
  du scoring du moteur back-end, pour une démonstration cohérente sans
  CP-SAT côté navigateur.

typecheck / lint / build / smoke au vert.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017uUsRg7XzopkCPWimwg9ZM
2026-09-11 08:16:29 +02:00

302 lines
18 KiB
JSON
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{
"filter.all": "TOUS",
"legend.title": "LÉGENDE",
"action.close": "Fermer",
"action.remove": "Retirer",
"action.add": "Ajouter",
"app.loading": "Chargement…",
"app.error.generic": "Une erreur est survenue",
"error.generic": "Erreur inattendue",
"error.not_found": "Ressource introuvable",
"error.forbidden": "Accès refusé",
"error.unauthorized": "Authentification requise",
"error.validation_failed": "La validation a échoué (règle bloquante)",
"assignment.duplicate": "Ce bénévole est déjà affecté à ce créneau",
"assignment.wrong_edition": "Bénévole ou créneau hors de cette édition",
"volunteer.duplicate_email": "Un bénévole avec cet email existe déjà",
"shift.ends_not_after_starts": "La fin doit être après le début",
"shift.position_wrong_edition": "Le poste n'appartient pas à cette édition",
"auth.invalid_credentials": "Email ou mot de passe incorrect",
"auth.invalid_token": "Ce lien de connexion est invalide ou expiré",
"role.coordinator": "Coordination",
"role.lead": "Référent",
"role.volunteer": "Bénévole",
"auth.logout": "Déconnexion",
"auth.email": "Email",
"auth.password": "Mot de passe",
"auth.signIn": "Se connecter",
"auth.signingIn": "Connexion…",
"auth.admin.title": "Espace coordination",
"auth.demoCredentials": "Démo : {email} · mot de passe « {password} »",
"auth.notAdmin": "Bénévole ou référent ?",
"auth.magicLinkLink": "Recevoir un lien de connexion",
"auth.isAdmin": "Vous êtes de la coordination ?",
"auth.adminLink": "Connexion par mot de passe",
"auth.magic.title": "Connexion bénévole / référent",
"auth.magic.lead": "Entrez votre email : vous recevrez un lien de connexion, sans mot de passe.",
"auth.magic.send": "Envoyer le lien",
"auth.magic.sending": "Envoi…",
"auth.magic.sent": "Si un compte correspond à cet email, un lien de connexion vient d'être envoyé.",
"auth.magic.devHint": "Démo (email stubé) — cliquez ce lien :",
"auth.magic.another": "Utiliser un autre email",
"auth.magic.verifyTitle": "Connexion en cours",
"auth.magic.verifying": "Vérification du lien…",
"auth.magic.retry": "Demander un nouveau lien",
"header.title": "GESTION DES BÉNÉVOLES",
"header.meta": "BÉNÉVOLAT · {count} INSCRITS",
"header.mockMode": "DONNÉES DE DÉMO",
"tab.grille": "Grille des besoins",
"tab.volunteers": "Vue par bénévole",
"tab.mobileVolunteer": "Bénévole · mobile",
"tab.mobileLead": "Référent · mobile",
"tab.config": "Configuration",
"position.category.bar": "Bar",
"position.category.ticketing": "Billetterie",
"position.category.welcome": "Accueil",
"position.category.parking": "Parking",
"position.category.cleaning": "Propreté",
"position.category.catering": "Catering",
"position.category.setup": "Montage",
"position.category.stage": "Régie",
"position.category.other": "Autre",
"validation.SHIFT_OVERLAP": "Se chevauche avec un autre créneau",
"validation.SHIFT_OVERLAP.at": "Déjà affecté sur {position} au même moment",
"validation.SHIFT_OVERLAP.rule": "Deux créneaux d'un même bénévole se chevauchent.",
"validation.OUTSIDE_AVAILABILITY": "Hors des disponibilités déclarées ({gap} non couvertes).",
"validation.OUTSIDE_AVAILABILITY.rule": "Le créneau n'est pas entièrement couvert par les disponibilités déclarées.",
"validation.DAILY_HOURS_EXCEEDED": "Plus de 8 h sur la journée ({hours})",
"validation.DAILY_HOURS_EXCEEDED.rule": "Plus de 8 h 00 d'affectations cumulées sur une même journée.",
"validation.INSUFFICIENT_REST": "Moins de 8 h de repos entre deux créneaux ({rest})",
"validation.INSUFFICIENT_REST.rule": "Moins de 8 h 00 entre la fin d'un créneau et le début du suivant.",
"grille.filter.day": "JOUR",
"grille.filter.position": "POSTE",
"grille.legend.critical": "SOUS-EFFECTIF CRITIQUE",
"grille.legend.incomplete": "INCOMPLET",
"grille.legend.complete": "COMPLET",
"grille.legend.overstaffed": "SUREFFECTIF",
"grille.legend.undefined": "À DÉFINIR",
"grille.positionsCount": "{count} POSTES",
"grille.assignedRequired": "AFFECTÉS / IDÉAL",
"grille.footnote": "Chaque cellule est un créneau. Nombre = bénévoles affectés, / = effectif idéal. Cliquer ouvre le panneau d'affectation à droite.",
"grille.empty": "Aucun créneau pour le moment. Créez des postes dans Configuration → « Postes & créneaux » — un créneau est généré par jour et par créneau type.",
"grille.panel.empty.title": "Cliquez une cellule de la grille pour affecter des bénévoles.",
"grille.panel.empty.body": "Le panneau s'ouvre ici, sans quitter la grille : candidats triés par charge, alertes bloquantes et avertissements du moteur de validation.",
"grille.panel.slotDuration": "CRÉNEAU DE {hours} H",
"grille.panel.assigned": "AFFECTÉS",
"grille.panel.min": "MINIMUM {count}",
"grille.panel.ideal": "IDÉAL {count}",
"grille.panel.assignmentsTitle": "AFFECTATIONS ({count})",
"grille.panel.noneAssigned": "Personne n'est encore affecté sur ce créneau.",
"grille.panel.hoursTotal": "{hours} H AU TOTAL",
"grille.panel.candidates": "CANDIDATS",
"grille.panel.candidatesHint": "LES MOINS CHARGÉS D'ABORD",
"grille.panel.hoursAssigned": "{hours} H · {slots} CRÉNEAUX",
"grille.panel.assign": "AFFECTER",
"grille.panel.blocked": "BLOQUÉ",
"grille.panel.blockingPrefix": "BLOQUANT — {reason}",
"grille.panel.warningsTitle": "⚠ AVERTISSEMENTS — AFFECTATION POSSIBLE",
"volunteers.filter.show": "AFFICHER",
"volunteers.filter.all": "TOUS",
"volunteers.filter.overloaded": "SURCHARGÉS",
"volunteers.filter.unassigned": "SANS AFFECTATION",
"volunteers.legend.assigned": "AFFECTATION",
"volunteers.count": "{count} BÉNÉVOLES",
"volunteers.totalHours": "TOTAL H",
"volunteers.name": "NOM",
"volunteers.cumulative": "CUMUL",
"volunteers.badge.overloaded": "▲ SURCHARGE",
"volunteers.badge.unassigned": "○ OUBLIÉ",
"volunteers.empty": "Aucun bénévole ne correspond à ce filtre — bonne nouvelle. Repassez sur « TOUS » pour revoir l'ensemble de l'équipe.",
"volunteers.noData": "Aucun bénévole pour le moment. Ajoutez-les dans Configuration → « Bénévoles & import ».",
"volunteers.footnote": "Surcharge = plus de 8 h sur une même journée ou moins de 8 h entre deux créneaux. Oublié = aucune affectation.",
"design.noteTitle": "CHOIX DE CONCEPTION",
"mobileVol.statusTime": "9:41",
"mobileVol.kicker": "MON PLANNING BÉNÉVOLE",
"mobileVol.greeting": "SALUT {name}",
"mobileVol.slots": "{count} CRÉNEAUX",
"mobileVol.hours": "{count} HEURES",
"mobileVol.location": "📍 {location}",
"mobileVol.slotAssigned": "✓ TU ES ATTENDU SUR CE CRÉNEAU",
"mobileVol.noSlots": "Aucun créneau pour l'instant. La coordination t'affectera bientôt.",
"mobileVol.noAccount": "Ce compte n'est rattaché à aucun bénévole. Contactez la coordination.",
"mobileVol.detailsUnavailable": "Le détail des créneaux (poste, horaires) n'est pas encore exposé aux bénévoles par l'API.",
"mobileVol.designNote": "Lisibilité plein soleil : fonds opaques, texte noir sur jaune ou blanc, aucun gris clair. Cibles tactiles à 52–62 px de haut. Une seule action par créneau. Les données proviennent des affectations réelles de l'API.",
"mobileLead.statusTime": "18:07",
"mobileLead.kicker": "RÉFÉRENT DE POSTE",
"mobileLead.inProgress": "CRÉNEAU · {hours}",
"mobileLead.present": "{present}/{total} POINTÉS PRÉSENTS",
"mobileLead.absenceAlert": "⚠ {count} ABSENCE(S) SUR LE CRÉNEAU",
"mobileLead.requestBackup": "DEMANDER UN RENFORT À LA COORDINATION",
"mobileLead.teamTitle": "POINTAGE DE L'ÉQUIPE",
"mobileLead.badgePresent": "✓ PRÉSENT",
"mobileLead.badgeAbsent": "✕ ABSENT",
"mobileLead.markPresent": "PRÉSENT",
"mobileLead.markAbsent": "ABSENT",
"mobileLead.emptyTeam": "Aucun bénévole affecté à ce créneau.",
"mobileLead.designNote": "Le référent ne voit que son poste et un créneau. Le pointage est un geste unique par personne. Il est local à cette maquette : le pointage sur site est hors périmètre (CLAUDE.md §4).",
"config.admin": "ADMINISTRATION",
"config.reserved": "RÉSERVÉ À LA COORDINATION",
"config.tab.grid": "POSTES & CRÉNEAUX",
"config.tab.volunteers": "BÉNÉVOLES & IMPORT",
"config.tab.mail": "MESSAGES",
"config.tab.rules": "RÈGLES & ÉDITION",
"config.grid.positionsHeader": "POSTES · {count} CRÉNEAUX",
"config.grid.slotsCount": "{count} créneaux",
"config.grid.addPosition": "+ AJOUTER UN POSTE",
"config.grid.newPositionName": "Nouveau poste",
"config.grid.minTotal": "BESOIN MINIMUM · TOTAL",
"config.grid.idealTotal": "BESOIN IDÉAL · TOTAL",
"config.grid.min": "MIN",
"config.grid.ideal": "IDÉAL",
"config.grid.emptyDay": "Aucun créneau ce jour.",
"config.grid.deletePosition": "SUPPRIMER LE POSTE",
"config.grid.deletePositionConfirm": "Supprimer ce poste et tous ses créneaux ?",
"config.grid.generateShifts": "GÉNÉRER LES CRÉNEAUX",
"config.grid.generated": "{count} créneau(x) ajouté(s).",
"config.grid.note": "Effectifs, créneaux, nom et catégorie du poste sont enregistrés immédiatement via l'API. Pour ajouter ou retirer des jours, modifiez les dates de l'édition dans l'onglet « Règles & édition ».",
"config.vol.importTitle": "IMPORTER DES BÉNÉVOLES",
"config.vol.importHint": "Trois façons d'ajouter des bénévoles : saisie manuelle, import d'un fichier CSV / Excel, ou (à venir) le formulaire d'inscription en ligne.",
"config.vol.profiles": "PROFILS ENREGISTRÉS",
"config.vol.colName": "NOM",
"config.vol.colPhone": "TÉLÉPHONE",
"config.vol.colEmail": "EMAIL",
"config.vol.ficheKicker": "FICHE BÉNÉVOLE",
"config.vol.firstName": "PRÉNOM",
"config.vol.lastName": "NOM",
"config.vol.phone": "TÉLÉPHONE",
"config.vol.email": "EMAIL (LIEN MAGIQUE)",
"config.vol.delete": "SUPPRIMER (RGPD — EFFACEMENT COMPLET)",
"config.vol.deleteConfirm": "Supprimer définitivement ce bénévole et toutes ses données (disponibilités, affectations) ?",
"config.vol.note": "Modifications enregistrées immédiatement via l'API. La suppression est un effacement complet en cascade (RGPD).",
"config.mail.smtpBanner": "Envoie un récapitulatif de planning à chaque bénévole. L'email part si le serveur SMTP est configuré (EMAIL_ENABLED) ; sinon il est seulement journalisé.",
"config.mail.templateTitle": "MODÈLE DE MESSAGE",
"config.mail.senderNote": "Expéditeur défini côté serveur (SMTP_FROM).",
"config.mail.subject": "OBJET",
"config.mail.body": "CORPS DU MESSAGE",
"config.mail.variables": "VARIABLES DISPONIBLES",
"config.mail.variablesHint": "Cliquer une variable l'ajoute au corps. {lien_connexion} insère un lien de connexion personnel.",
"config.mail.send": "ENVOYER LE RÉCAPITULATIF",
"config.mail.sendWithLink": "ENVOYER AVEC LIEN DE CONNEXION",
"config.mail.sending": "ENVOI EN COURS…",
"config.mail.sent": "Envoi lancé : {queued} email(s), {skipped} bénévole(s) sans adresse.",
"config.mail.loginLinkLine": "Accède à ton planning : {lien_connexion}",
"config.mail.previewTitle": "APERÇU",
"config.mail.previewSender": "DE : expéditeur configuré côté serveur",
"config.rules.editionTitle": "ÉDITION",
"config.rules.editionName": "Nom",
"config.rules.editionStart": "Premier jour",
"config.rules.editionEnd": "Dernier jour",
"config.rules.editionDays": "{count} jour(s)",
"config.rules.editionSave": "ENREGISTRER L'ÉDITION",
"config.rules.editionSaved": "Édition enregistrée.",
"config.rules.apiMode": "Mode API",
"config.rules.volunteersCount": "Bénévoles",
"config.rules.positionsCount": "Postes",
"config.rules.shiftsCount": "Créneaux",
"edition.dates_inconsistent": "La date de fin doit être postérieure ou égale à la date de début.",
"day_part.code_required": "Le libellé du créneau type est requis.",
"day_part.duplicate_code": "Un créneau type porte déjà ce libellé.",
"config.rules.slotsTitle": "CRÉNEAUX TYPES",
"config.rules.slotLabel": "Libellé",
"config.rules.slotAdd": "AJOUTER",
"config.rules.slotsNote": "Ces créneaux sont ajoutés à besoin 0/0 sur chaque jour à la création d'un poste. Après un ajout ici (ou une extension des dates), utilisez « Générer les créneaux » sur le poste.",
"config.rules.thresholdsTitle": "MOTEUR DE VALIDATION",
"config.rules.maxDaily": "Maximum d'heures par journée",
"config.rules.minRest": "Repos minimum entre deux créneaux",
"config.rules.thresholdsNote": "Seuils figés côté back-end (CLAUDE.md §3) : non paramétrables. Le double emploi au même créneau est toujours bloquant.",
"config.rules.rulesTitle": "RÈGLES APPLIQUÉES",
"config.rules.rolesTitle2": "RÔLES ET ACCÈS",
"config.rules.roleCoordinator": "Coordination",
"config.rules.roleCoordinatorDesc": "Accès complet, construit et publie les plannings",
"config.rules.roleLead": "Référent de poste",
"config.rules.roleLeadDesc": "Lecture sur l'édition, écriture sur ses postes",
"config.rules.roleVolunteer": "Bénévole",
"config.rules.roleVolunteerDesc": "Ses seules données, lien magique par email",
"config.vol.addButton": "AJOUTER MANUELLEMENT",
"config.vol.importButton2": "IMPORTER UN FICHIER",
"config.vol.search": "Rechercher un nom, un email…",
"config.vol.emptyList": "Aucun bénévole pour l'instant. Ajoutez-en un ou importez un fichier.",
"config.vol.noMatch": "Aucun bénévole ne correspond à la recherche.",
"config.vol.count": "{count} bénévole(s)",
"config.vol.cancel": "ANNULER",
"config.vol.field.firstName": "Prénom",
"config.vol.field.lastName": "Nom",
"config.vol.field.email": "Email (lien magique)",
"config.vol.field.phone": "Téléphone",
"config.vol.field.comment": "Commentaire",
"config.vol.addTitle": "NOUVEAU BÉNÉVOLE",
"config.vol.addHint": "Le bénévole reçoit un accès par lien magique uniquement si un email est renseigné.",
"config.vol.create": "CRÉER LE BÉNÉVOLE",
"config.vol.requiredNames": "Le prénom et le nom sont obligatoires.",
"config.vol.wiz.title": "IMPORTER UN FICHIER",
"config.vol.wiz.step1": "1 · FICHIER",
"config.vol.wiz.step2": "2 · COLONNES",
"config.vol.wiz.step3": "3 · APERÇU & IMPORT",
"config.vol.wiz.pickFile": "CHOISIR UN FICHIER CSV OU EXCEL",
"config.vol.wiz.fileHint": "Une ligne par bénévole. La première ligne contient les intitulés de colonnes. En mode démonstration, seuls les fichiers CSV sont pris en charge.",
"config.vol.wiz.analyzing": "Analyse du fichier…",
"config.vol.wiz.detected": "{rows} ligne(s) · {cols} colonne(s) détectée(s)",
"config.vol.wiz.preview": "APERÇU DES PREMIÈRES LIGNES",
"config.vol.wiz.mapTitle": "ASSOCIER LES COLONNES",
"config.vol.wiz.mapHint": "« Prénom » et « Nom » sont obligatoires. Laissez « — » pour ignorer une colonne.",
"config.vol.wiz.ignore": "—",
"config.vol.wiz.mapMissing": "Associez au moins les colonnes « Prénom » et « Nom ».",
"config.vol.wiz.simulate": "SIMULER",
"config.vol.wiz.runImport": "IMPORTER POUR DE VRAI",
"config.vol.wiz.back": "RETOUR",
"config.vol.wiz.next": "SUIVANT",
"config.vol.wiz.dryRunTitle": "SIMULATION — RIEN N'A ÉTÉ ENREGISTRÉ",
"config.vol.wiz.doneTitle": "IMPORT TERMINÉ",
"config.vol.wiz.created": "{count} créé(s)",
"config.vol.wiz.updated": "{count} mis à jour",
"config.vol.wiz.skipped": "{count} ignoré(s)",
"config.vol.wiz.rowsWithIssues": "LIGNES AVEC REMARQUES",
"config.vol.wiz.line": "Ligne {line}",
"config.vol.wiz.noIssues": "Aucune remarque : tout est prêt à être importé.",
"config.vol.wiz.close": "FERMER",
"import.error.empty_file": "Le fichier est vide.",
"import.error.encoding": "L'encodage du fichier n'est pas reconnu.",
"import.error.unreadable_file": "Fichier illisible ou format non pris en charge.",
"import.error.file_too_large": "Le fichier est trop volumineux (5 Mo maximum).",
"import.error.xlsx_demo_only": "En mode démonstration, l'import Excel n'est pas disponible : exportez votre fichier en CSV.",
"import.mapping.invalid": "Le paramétrage des colonnes est invalide.",
"import.mapping.required_field_missing": "Le champ « {field} » doit être associé à une colonne.",
"import.mapping.column_not_found": "La colonne « {column} » est absente du fichier.",
"import.row.missing_required_field": "Champ obligatoire manquant : {field}.",
"import.row.invalid_email": "Adresse email invalide : {value}.",
"import.row.duplicate_email_in_file": "Email déjà présent à la ligne {first_line} du fichier.",
"import.row.duplicate_name_in_file": "Nom et prénom déjà présents à la ligne {first_line} du fichier.",
"import.row.updated_existing": "Bénévole existant mis à jour.",
"import.row.possible_duplicate_name": "Un bénévole portant le même nom existe déjà dans l’édition.",
"config.tab.artists": "PROGRAMMATION",
"config.artists.title": "PROGRAMMATION",
"config.artists.hint": "La liste des artistes de l'édition, jour par jour. Les bénévoles y choisissent ceux qu'ils veulent voir depuis leur fiche.",
"config.artists.count": "ARTISTES",
"config.artists.empty": "Aucun artiste ce jour.",
"config.artists.outside": "hors dates",
"config.artists.namePlaceholder": "Nom de l'artiste",
"config.vol.field.artists": "Artistes à voir",
"config.vol.field.positions": "Postes souhaités",
"config.vol.noArtists": "Aucun artiste choisi",
"config.vol.noPositions": "Aucun poste choisi",
"config.vol.pickArtist": "Ajouter un artiste…",
"config.vol.pickPosition": "Ajouter un poste…",
"config.vol.colWishes": "VŒUX",
"config.vol.wishesCount": "{count} vœu(x)",
"config.vol.addAvailabilityHint": "Les disponibilités se renseignent sur la fiche du bénévole, une fois celui-ci créé.",
"config.vol.avail.title": "Disponibilités",
"config.vol.avail.hint": "Plages horaires où le bénévole peut venir. Une heure de fin ≤ début signifie que la plage se termine le lendemain.",
"config.vol.avail.none": "Aucune disponibilité déclarée",
"config.vol.avail.add": "Ajouter une plage",
"config.vol.avail.save": "Enregistrer les disponibilités",
"grille.panel.coverage.full": "Disponible",
"grille.panel.coverage.partial": "Partiellement dispo",
"grille.panel.coverage.none": "Dispo non déclarée",
"availability.date_outside_edition": "Cette date est hors des dates de l'édition.",
"availability.empty_window": "Une plage doit avoir un début et une fin différents.",
"grille.solver.run": "SUGGESTIONS (SOLVEUR)",
"grille.solver.running": "CALCUL EN COURS…",
"grille.solver.result": "{count} suggestion(s)",
"grille.panel.suggested": "Suggéré"
}