Quand le catalogue seede ne couvre pas un ingredient, l'utilisateur pouvait etre bloque (creation manuelle) ou perdre silencieusement la ligne (import). Une ligne de recette accepte desormais `placeholderName` (texte libre) au lieu de `ingredientId` : l'API cree une ligne `Ingredient` `isPlaceholder` (cle `placeholder:<uuid>`, `displayName`, `createdById`) dans la transaction de la recette, et emet `ingredient.placeholder_created`. Ces lignes sont exclues de `GET /reference/ingredients` et de `ingredient-matcher`. Front : bouton "Ajouter << ... >>" dans l'etat vide de `IngredientPicker` (formulaire + import), badge "a completer" sur la ligne, helper `ingredientLabel` applique partout ou un libelle d'ingredient est rendu. Admin : `/admin/catalog/*` (+ page `apps/admin-web`) liste les placeholders regroupes par nom normalise, "marquer traite" (`reviewedAt`) et purge des orphelins. La promotion en vraie entree catalogue reste manuelle. Migration `ingredient_placeholder` ecrite a la main (Postgres indisponible). Suites Mocha DB-backed ecrites, non executees en session ; test pur `normalizePlaceholderName` + Cypress admin-web/web verts. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
148 lines
5.3 KiB
JSON
148 lines
5.3 KiB
JSON
{
|
|
"errors": {
|
|
"VALIDATION_ERROR": "Erreur de validation",
|
|
"INVALID_CREDENTIALS": "Email ou mot de passe incorrect",
|
|
"NOT_AUTHENTICATED": "Vous devez être connecté",
|
|
"NOT_FOUND": "Ressource introuvable",
|
|
"TECH_STEP_NOT_FOUND": "Cette technique n'existe pas",
|
|
"RETRAIN_ALREADY_RUNNING": "Un ré-entraînement est déjà en cours",
|
|
"INTERNAL_ERROR": "Une erreur est survenue, réessayez plus tard"
|
|
},
|
|
"admin": {
|
|
"common": {
|
|
"comingSoon": "Section à venir.",
|
|
"loading": "Chargement…",
|
|
"loadError": "Impossible de charger les données, réessayez plus tard."
|
|
},
|
|
"login": {
|
|
"title": "Administration",
|
|
"emailLabel": "Email",
|
|
"passwordLabel": "Mot de passe",
|
|
"submit": "Se connecter",
|
|
"submitting": "Connexion…"
|
|
},
|
|
"nav": {
|
|
"dashboard": "Tableau de bord",
|
|
"monitoring": "Monitoring",
|
|
"corrections": "Corrections",
|
|
"catalog": "Catalogue"
|
|
},
|
|
"layout": {
|
|
"logout": "Se déconnecter"
|
|
},
|
|
"dashboard": {
|
|
"title": "Tableau de bord",
|
|
"lead": "Métriques d'utilisation de l'application.",
|
|
"windowTotal": "{{n}} sur 30 j",
|
|
"recipesBySource": "Recettes importées par source",
|
|
"noImports": "Aucune recette importée.",
|
|
"events": "Évènements enregistrés (30 j)",
|
|
"kpi": {
|
|
"users": "Utilisateurs",
|
|
"households": "Foyers",
|
|
"activeHouseholds": "Foyers actifs",
|
|
"recipes": "Recettes",
|
|
"recipesImported": "Recettes importées",
|
|
"plannings": "Plannings",
|
|
"planningItems": "Créneaux planifiés",
|
|
"favorites": "Favoris",
|
|
"corrections": "Corrections",
|
|
"correctionsUnconsumed": "Corrections à traiter",
|
|
"trainingSuggestions": "Suggestions d'entraînement",
|
|
"admins": "Administrateurs"
|
|
},
|
|
"series": {
|
|
"signups": "Inscriptions",
|
|
"recipesCreated": "Recettes créées",
|
|
"planningItemsAdded": "Ajouts au planning",
|
|
"correctionsSubmitted": "Corrections soumises",
|
|
"trainingSuggestions": "Suggestions générées"
|
|
}
|
|
},
|
|
"monitoring": {
|
|
"title": "Monitoring",
|
|
"lead": "Santé des microservices et de la base de données.",
|
|
"lastChecked": "Dernière vérification à {{time}}",
|
|
"latency": "Latence",
|
|
"detail": "Détail",
|
|
"lastRun": "Dernier job",
|
|
"never": "jamais",
|
|
"jobFailed": "échec",
|
|
"status": {
|
|
"up": "OK",
|
|
"degraded": "Dégradé",
|
|
"down": "Hors service",
|
|
"unknown": "Inconnu"
|
|
},
|
|
"service": {
|
|
"postgres": "Base de données",
|
|
"api": "API",
|
|
"intent-service": "Service NLP (spaCy)",
|
|
"tech-step-llm-worker": "Worker LLM"
|
|
}
|
|
},
|
|
"corrections": {
|
|
"title": "Corrections",
|
|
"lead": "Tri des corrections utilisateur pour le ré-entraînement NLP.",
|
|
"caveat": "Le gate F1 + backfill n'a de sens qu'APRÈS avoir édité training_data.py à la main et redémarré le service NLP (il ne s'entraîne qu'au démarrage). Cet écran ne peut faire ni l'un ni l'autre.",
|
|
"noSuggestions": "Aucune suggestion pour ces filtres.",
|
|
"synonyms": "Synonymes proposés (un par ligne)",
|
|
"utterances": "Phrases proposées (une par ligne)",
|
|
"save": "Enregistrer",
|
|
"apply": "Appliquer",
|
|
"reject": "Rejeter",
|
|
"tab": {
|
|
"suggestions": "Suggestions",
|
|
"corrections": "Corrections brutes"
|
|
},
|
|
"filter": {
|
|
"status": "Statut",
|
|
"source": "Source",
|
|
"consumed": "Consommée",
|
|
"hasCorrected": "Technique corrigée",
|
|
"any": "Toutes",
|
|
"yes": "Oui",
|
|
"no": "Non"
|
|
},
|
|
"snippet": {
|
|
"title": "Snippet training_data.py",
|
|
"help": "Agrège les synonymes/phrases des suggestions « applied » d'une technique, au format à coller dans training_data.py.",
|
|
"keyPlaceholder": "clé de technique (ex. simmer)",
|
|
"generate": "Générer"
|
|
},
|
|
"retrain": {
|
|
"title": "Gate F1 + backfill",
|
|
"help": "Lance l'évaluation de régression F1 puis, si elle passe, recalcule les techniques de toutes les étapes.",
|
|
"run": "Lancer",
|
|
"running": "En cours…",
|
|
"passed": "OK — {{changed}}/{{total}} étape(s) recalculée(s)",
|
|
"failed": "Échec du gate — aucun backfill"
|
|
},
|
|
"col": {
|
|
"clause": "Clause",
|
|
"change": "Changement",
|
|
"created": "Créée",
|
|
"consumed": "Consommée"
|
|
}
|
|
},
|
|
"catalog": {
|
|
"title": "Ingrédients hors-catalogue",
|
|
"lead": "Ingrédients saisis en texte libre par les utilisateurs parce que le catalogue ne les couvrait pas. Regroupés par nom normalisé — à promouvoir dans reference-seed-data.ts + les locales, à la main.",
|
|
"empty": "Aucun ingrédient hors-catalogue.",
|
|
"tab": {
|
|
"pending": "À traiter",
|
|
"reviewed": "Traités"
|
|
},
|
|
"recipeCount": "{{count}} recette(s)",
|
|
"alsoWritten": "Aussi écrit : {{variants}}",
|
|
"seenIn": "Vu dans :",
|
|
"firstSeen": "Première fois le {{date}}",
|
|
"markReviewed": "Marquer comme traité",
|
|
"marking": "…",
|
|
"pruneOrphans": "Purger les orphelins",
|
|
"pruning": "Purge…",
|
|
"prunedNone": "Aucun placeholder orphelin à purger.",
|
|
"pruned": "{{count}} placeholder(s) orphelin(s) supprimé(s)."
|
|
}
|
|
}
|
|
}
|