Files
Epicure/apps/web/messages/fr.json
T
Arnaud 1abab17ca8 feat: notifications system, rate limiting, fix recipe visibility 404, follow race
Part of the social-feature backlog (follow, comments, reactions, ratings,
feed, threading) audited earlier — see conversation.

- notifications table: follow/comment/reply/reaction/rating events,
  replaces the fully-dead feed_items table (feed_item_type enum existed
  but had zero references anywhere in the codebase).
- Bell UI in the nav with unread badge, mark-all-read, 30s poll.
- Rate limiting on comment posting (20/min), follow/unfollow (30/min),
  and comment reactions (60/min) — previously unthrottled.
- /recipes/[id] queried by (id, authorId=session.user) only, so any
  recipe not owned by the viewer 404'd regardless of visibility.
  Widen the query to include public/unlisted recipes and gate the
  owner-only actions (edit, delete, version history, translate, AI
  content generation) behind an isOwner check.
- user_follows had no primary key/unique constraint, so the follow
  route's onConflictDoNothing() was a silent no-op — concurrent follow
  clicks could insert duplicate rows and inflate follower counts. Add
  a composite primary key on (follower_id, following_id).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-03 21:56:34 +02:00

775 lines
34 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.
{
"nav": {
"menu": "Menu",
"recipes": "Recettes",
"explore": "Explorer",
"search": "Rechercher",
"feed": "Fil d'actualité",
"collections": "Collections",
"mealPlan": "Planning repas",
"pantry": "Garde-manger",
"shopping": "Courses",
"settings": "Paramètres",
"admin": "Admin",
"signOut": "Se déconnecter",
"apiKeys": "Clés API",
"webhooks": "Webhooks",
"language": "Langue",
"notifications": "Notifications"
},
"notifications": {
"title": "Notifications",
"empty": "Aucune notification pour l'instant.",
"markAllRead": "Tout marquer comme lu",
"follow": "{name} vous suit désormais",
"comment": "{name} a commenté votre recette",
"reply": "{name} a répondu à votre commentaire",
"reaction": "{name} a réagi à votre commentaire",
"rating": "{name} a noté votre recette"
},
"recipe": {
"share": "Partager",
"shareLinkCopied": "Lien copié dans le presse-papiers",
"shareCopyFailed": "Échec de la copie du lien",
"shareNotPublicNotice": "Ce lien ne fonctionnera qu'une fois la recette définie sur Public — lien copié quand même.",
"print": "Imprimer",
"ingredients": "Ingrédients",
"instructions": "Instructions",
"photos": "Photos",
"servings": "{count} portions",
"prep": "{mins}m prép.",
"cook": "{mins}m cuisson",
"total": "{mins}m total",
"edit": "Modifier",
"variations": "Variations",
"save": "Enregistrer",
"cancel": "Annuler",
"delete": "Supprimer",
"noIngredientsSteps": "Pas encore d'ingrédients ni d'étapes.",
"generateContent": "Générer avec l'IA",
"editManually": "Modifier manuellement",
"deleted": "Recette supprimée",
"deleteFailed": "Échec de la suppression",
"imported": "Recette importée ! Vérifiez avant de publier.",
"adapted": "Recette adaptée enregistrée comme brouillon",
"adaptFailed": "Échec de l'adaptation de la recette",
"adaptConstraintRequired": "Sélectionnez au moins un ingrédient à exclure ou ajoutez une contrainte",
"adaptTooltip": "Adapter",
"adaptTitle": "Adapter cette recette",
"adaptDescription": "Touchez les ingrédients pour les exclure. L'IA trouvera les meilleurs substituts tout en préservant le plat.",
"excludeIngredients": "Exclure des ingrédients",
"excludedCount": "{count} exclus",
"additionalConstraints": "Contraintes supplémentaires",
"optional": "facultatif",
"adapting": "Adaptation de la recette — cela peut prendre 20 à 30 secondes…",
"adaptingButton": "Adaptation…",
"adaptButton": "Adapter la recette",
"clearExclusions": "Effacer",
"adaptConstraintPlaceholder": "ex. Rendre végétalien, réduire les calories, utiliser seulement les produits du garde-manger, sans gluten…",
"variationsDirectionsPlaceholder": "ex. rendre végétalien, utiliser uniquement les produits du garde-manger, réduire le sucre…",
"historyRestoreFailed": "Échec de la restauration de la version",
"pairingMealFailed": "Échec de la suggestion d'accompagnements",
"pairingDrinkFailed": "Échec de la suggestion de boissons",
"pairingGenerateFailed": "Échec de la génération de « {name} »",
"pairingSaveFailed": "Échec de l'enregistrement de « {name} »",
"pairingSuccess": "Recette générée — vérifiez avant de publier",
"pairingBulkSuccess": "{count} recettes générées — retrouvez-les dans votre bibliothèque",
"cookAction": "Cuisiner",
"photoImportFailed": "Échec de l'importation de la recette depuis la photo.",
"importFromPhoto": "Importer depuis une photo",
"analyzingPhoto": "Analyse de la photo…",
"pairMealTooltip": "Accorder un plat",
"historyTooltip": "Historique",
"translateTooltip": "Traduire",
"drinksTooltip": "Boissons",
"variationsTooltip": "Variations",
"viewPublicly": "Voir publiquement",
"shoppingListCreateFailed": "Échec de la création de la liste",
"shoppingListAddFailed": "Échec de l'ajout des ingrédients",
"bulkDeleted": "{count, plural, one {1 recette supprimée} other {{count} recettes supprimées}}",
"bulkVisibility": "{count, plural, one {1 recette définie sur {visibility}} other {{count} recettes définies sur {visibility}}}",
"bulkDeleteFailed": "Échec de la suppression",
"bulkUpdateFailed": "Échec de la mise à jour",
"bulkDeleteConfirm": "{count, plural, one {Supprimer 1 recette ?} other {Supprimer {count} recettes ?}} Cette action est irréversible.",
"deselectAll": "Tout désélectionner",
"selectAll": "Tout sélectionner",
"clickToSelect": "Cliquez sur les cartes pour sélectionner",
"selectedCount": "{count} sélectionnée(s)",
"select": "Sélectionner",
"visibility": "Visibilité",
"makePublic": "Rendre publique",
"makeUnlisted": "Rendre non répertoriée",
"makePrivate": "Rendre privée",
"visibility": {
"private": "Privée",
"unlisted": "Non répertoriée",
"public": "Publique"
},
"difficulty": {
"easy": "Facile",
"medium": "Moyen",
"hard": "Difficile"
},
"dietary": {
"vegan": "Végétalien",
"vegetarian": "Végétarien",
"glutenFree": "Sans gluten",
"dairyFree": "Sans lactose",
"nutFree": "Sans noix",
"halal": "Halal",
"kosher": "Casher"
}
},
"ai": {
"generate": {
"title": "Générer une recette avec l'IA",
"description": "Décrivez ce que vous voulez cuisiner et l'IA créera une recette complète.",
"descriptionFull": "Décrivez un plat ou prenez une photo — l'IA construit la recette complète.",
"tabDescribe": "Décrire",
"tabPhoto": "Depuis une photo",
"surpriseMe": "Surprends-moi",
"prompt": "Que voulez-vous cuisiner ?",
"placeholder": "ex. Un risotto crémeux aux champignons et parmesan, 4 portions, niveau facile",
"language": "Langue de la recette",
"difficulty": "Difficulté",
"anyDifficulty": "Toutes",
"button": "Générer",
"generating": "Génération en cours…",
"success": "Recette générée ! Vérifiez et modifiez avant de publier.",
"error": "Échec de la génération de la recette",
"saveError": "Échec de l'enregistrement de la recette générée",
"uploadClick": "Cliquez pour importer",
"uploadDrag": "ou glissez une photo du plat",
"uploadFormats": "JPEG, PNG ou WebP · l'IA reconstituera la recette",
"analyzing": "Analyse en cours…",
"analyzePhoto": "Analyse de la photo du plat…",
"recognizeDish": "Reconnaître le plat",
"photoSuccess": "Recette reconnue ! Vérifiez et modifiez avant de publier.",
"photoError": "Échec de l'analyse de la photo",
"contentGenerated": "Ingrédients et étapes générés !",
"contentGenerateFailed": "Échec de la génération",
"contentSaveFailed": "Échec de l'enregistrement du contenu généré",
"generatingContent": "Génération du contenu de la recette…",
"photoAnalysisNote": "L'IA analysera ce plat et déduira les ingrédients, quantités et étapes de préparation."
},
"variations": {
"title": "Variations de recette par IA",
"description": "Générez des variations créatives de cette recette — adaptations diététiques, profils de saveurs, changements de techniques.",
"generate": "Générer 3 variations",
"generating": "Génération des variations…",
"regenerate": "Régénérer",
"apply": "Appliquer",
"applying": "Application…",
"ingredientChanges": "Changements d'ingrédients",
"stepChanges": "Changements d'étapes",
"success": "Variation créée — vérifiez et modifiez avant de publier.",
"error": "Échec de la génération des variations",
"saveError": "Échec de l'enregistrement de la variation",
"wait": "Cela peut prendre 20 à 30 secondes",
"directions": "Instructions",
"optional": "facultatif",
"stepLabel": "Étape {n}"
},
"translate": {
"title": "Traduire la recette",
"description": "Traduisez le titre, la description, les ingrédients et les étapes de cette recette dans une autre langue.",
"targetLanguage": "Langue cible",
"button": "Traduire",
"translating": "Traduction en cours…",
"success": "Traduction enregistrée comme nouveau brouillon.",
"error": "Échec de la traduction de la recette",
"saveError": "Échec de l'enregistrement de la recette traduite",
"wait": "Cela peut prendre 20 à 30 secondes"
}
},
"settings": {
"title": "Paramètres",
"subtitle": "Gérez votre compte, vos préférences et vos intégrations.",
"profile": "Profil",
"language": "Langue",
"units": "Unités",
"metric": "Métrique",
"imperial": "Impérial",
"security": "Sécurité",
"aiModels": "IA et modèles",
"notifications": "Notifications",
"nutrition": "Nutrition",
"apiKeys": "Clés API",
"webhooks": "Webhooks",
"byok": {
"title": "Vos clés API (BYOK)",
"description": "Utilisez vos propres clés API au lieu du quota partagé de l'application. Les clés sont chiffrées au repos."
},
"modelPrefs": {
"title": "Préférences de modèle",
"description": "Choisissez le modèle à utiliser pour chaque tâche. Par défaut, votre premier fournisseur configuré."
},
"apiKeysPage": {
"title": "Clés API",
"description": "Gérez les clés API pour un accès programmatique à l'API Epicure."
},
"pushNotifications": {
"title": "Notifications push",
"description": "Soyez averti lorsqu'on commente vos recettes ou aime votre contenu."
},
"nutritionGoals": {
"title": "Objectifs nutritionnels quotidiens",
"description": "Définissez vos objectifs quotidiens. Ils apparaissent sous forme de barres de progression dans votre planning repas."
},
"webhooksPage": {
"title": "Webhooks",
"description": "Recevez des appels HTTP lorsque des événements se produisent sur votre compte Epicure."
}
},
"print": {
"footer": "Imprimé depuis Epicure",
"day": "Jour",
"srv": " · {count} pers.",
"days": {
"mon": "Lundi",
"tue": "Mardi",
"wed": "Mercredi",
"thu": "Jeudi",
"fri": "Vendredi",
"sat": "Samedi",
"sun": "Dimanche"
}
},
"publicRecipe": {
"publicRecipeBy": "Recette publique par",
"viewInLibrary": "Voir dans votre bibliothèque",
"signUpFree": "S'inscrire gratuitement",
"logIn": "Se connecter",
"wantToSave": "Envie de sauvegarder cette recette ?",
"wantToSaveDescription": "Créez un compte gratuit pour sauvegarder des recettes, ajuster les portions et construire votre propre bibliothèque.",
"getStartedFree": "Commencer gratuitement"
},
"substitute": {
"fetchError": "Impossible de récupérer les substituts.",
"titleFor": "Substitut pour {ingredient}",
"substitutesFor": "Substituts pour",
"finding": "Recherche de substituts…",
"noneFound": "Aucune substitution trouvée."
},
"recipeChat": {
"sorry": "Désolé, je n'ai pas pu répondre à cela.",
"error": "Une erreur s'est produite. Veuillez réessayer.",
"suggestion1": "Puis-je substituer des ingrédients ?",
"suggestion2": "Comment savoir quand c'est prêt ?",
"suggestion3": "Puis-je préparer cela à l'avance ?",
"suggestion4": "Avec quoi puis-je servir ce plat ?",
"ariaLabel": "Demander à l'IA à propos de cette recette",
"title": "Poser une question sur cette recette",
"intro": "Demandez n'importe quoi sur cette recette — ingrédients, techniques, substitutions, minutage…",
"inputPlaceholder": "Posez une question…"
},
"servingScaler": {
"servings": "Portions",
"reset": "Réinitialiser",
"aiScale": "Ajuster avec l'IA",
"scaling": "Ajustement…",
"aiScaledNote": "Quantités ajustées par l'IA affichées ci-dessous"
},
"explore": {
"title": "Explorer",
"searchPlaceholder": "Rechercher des recettes publiques…",
"maxMinutes": "Minutes max",
"aiSearchPlaceholder": "ex. dîners rapides en semaine, cuisine réconfortante italienne…",
"anyDifficulty": "Toute difficulté",
"resultsFoundSingular": "{count} recette trouvée",
"resultsFoundPlural": "{count} recettes trouvées",
"noResultsTitle": "Aucune recette trouvée pour « {query} »",
"noResultsHint": "Essayez d'autres mots-clés ou retirez des filtres",
"loadMore": "Charger plus",
"recipeIdeas": "Idées de recettes",
"recipeIdeasHint": "Dites à l'IA ce dont vous avez envie, ou laissez-la vous surprendre.",
"generating": "Génération…",
"getIdeas": "Obtenir des idées",
"surpriseMe": "Surprenez-moi",
"generateFullRecipe": "Générer la recette complète",
"trendingThisWeek": "Tendances cette semaine",
"noTrending": "Aucune recette tendance pour l'instant. Soyez le premier à en partager une !",
"recentlyAdded": "Récemment ajoutées",
"noRecent": "Aucune recette publique pour l'instant."
},
"common": {
"loading": "Chargement…",
"error": "Une erreur s'est produite",
"save": "Enregistrer",
"saved": "Enregistré",
"saveFailed": "Échec de l'enregistrement",
"print": "Imprimer",
"share": "Partager",
"deleteFailed": "Échec de la suppression",
"updateFailed": "Échec de la mise à jour",
"copyFailed": "Échec de la copie dans le presse-papiers",
"deleted": "Supprimé",
"cancel": "Annuler",
"delete": "Supprimer",
"confirm": "Confirmer",
"back": "Retour",
"difficulty": "Difficulté",
"anyDifficulty": "Toutes",
"editManually": "Modifier manuellement",
"surpriseMe": "Surprends-moi",
"generatingContent": "Génération du contenu de la recette…"
},
"auth": {
"signIn": "Se connecter",
"signInTitle": "Connectez-vous à votre bibliothèque de recettes",
"signInLoading": "Connexion en cours…",
"signUp": "S'inscrire",
"signUpTitle": "Créer un compte",
"signUpSubtitle": "Commencez à construire votre bibliothèque de recettes",
"signUpLoading": "Création du compte…",
"continueWithGoogle": "Continuer avec Google",
"continueWithGithub": "Continuer avec GitHub",
"continueWithDiscord": "Continuer avec Discord",
"continueWithAuthentik": "Continuer avec Authentik",
"email": "E-mail",
"emailPlaceholder": "vous@exemple.fr",
"password": "Mot de passe",
"name": "Nom",
"namePlaceholder": "Votre nom",
"forgotPassword": "Mot de passe oublié ?",
"forgotPasswordTitle": "Mot de passe oublié",
"forgotPasswordSent": "Vérifiez vos e-mails",
"forgotPasswordDescription": "Saisissez votre e-mail et nous vous enverrons un lien de réinitialisation.",
"forgotPasswordSentDescription": "Si un compte existe pour cet e-mail, vous recevrez un lien sous peu.",
"sendResetLink": "Envoyer le lien",
"sendingLink": "Envoi en cours…",
"resetPasswordTitle": "Réinitialiser le mot de passe",
"resetPasswordDescription": "Saisissez un nouveau mot de passe pour votre compte.",
"newPassword": "Nouveau mot de passe",
"confirmPassword": "Confirmer le mot de passe",
"updatePassword": "Mettre à jour le mot de passe",
"updatingPassword": "Mise à jour…",
"invalidToken": "Jeton de réinitialisation invalide ou manquant.",
"noAccount": "Pas de compte ? S'inscrire",
"alreadyHaveAccount": "Déjà un compte ? Se connecter",
"backToSignIn": "Retour à la connexion",
"emailNotVerified": "E-mail non vérifié",
"checkInboxVerification": "Vérifiez votre boîte de réception pour l'e-mail de vérification.",
"resendVerification": "Renvoyer l'e-mail de vérification",
"resendingSending": "Envoi en cours…",
"or": "ou"
},
"recipes": {
"title": "Recettes",
"subtitle": "Votre bibliothèque personnelle de recettes",
"new": "Nouvelle recette",
"newTitle": "Nouvelle recette",
"newSubtitle": "Créez votre recette depuis zéro",
"importUrl": "Importer une URL",
"generate": "Générer",
"search": "Rechercher des recettes…",
"noRecipes": "Aucune recette pour l'instant",
"noMatch": "Aucune recette ne correspond",
"clearSearch": "Effacer la recherche",
"createFirst": "Créer votre première recette",
"resultSingular": "{count} recette",
"resultPlural": "{count} recettes",
"for": "pour",
"filterByTag": "Filtrer par tag…",
"sortRecentlyUpdated": "Mise à jour récemment",
"sortOldestUpdated": "Mise à jour la plus ancienne",
"sortNewestFirst": "Plus récentes",
"sortOldestFirst": "Plus anciennes",
"sortTitleAZ": "Titre AZ",
"sortTitleZA": "Titre ZA",
"sort": "Trier",
"sortBy": "Trier par",
"filter": "Filtrer",
"all": "Toutes",
"anyDifficulty": "Toute difficulté",
"visibilityLabel": "Visibilité",
"difficultyLabel": "Difficulté",
"tagLabel": "Tag",
"clearFilters": "Effacer les filtres"
},
"recipeForm": {
"titleLabel": "Titre *",
"titleRequired": "Le titre est requis",
"titlePlaceholder": "Ma super recette",
"descriptionLabel": "Description",
"descriptionPlaceholder": "Ce qui rend cette recette spéciale…",
"servings": "Portions",
"prepMins": "Prép. (min)",
"cookMins": "Cuisson (min)",
"difficulty": "Difficulté",
"visibility": "Visibilité",
"dietaryTags": "Tags alimentaires",
"photos": "Photos",
"ingredients": "Ingrédients",
"amount": "Quantité",
"unit": "Unité",
"ingredient": "Ingrédient",
"note": "Note",
"addIngredient": "Ajouter un ingrédient",
"setCover": "Définir comme couverture",
"removePhoto": "Supprimer",
"expand": "Développer",
"steps": "Étapes",
"stepPlaceholder": "Étape {n}…",
"timerSeconds": "Minuteur (s)",
"addStep": "Ajouter une étape",
"saving": "Enregistrement…",
"saveChanges": "Enregistrer les modifications",
"createRecipe": "Créer la recette",
"updateSuccess": "Recette mise à jour",
"createSuccess": "Recette créée",
"saveError": "Échec de l'enregistrement de la recette"
},
"canCook": {
"title": "Que puis-je cuisiner ?",
"subtitle": "Recettes réalisables avec votre garde-manger",
"emptyPantry": "Votre garde-manger est vide",
"addPantryItems": "Ajouter des articles",
"readyToCook": "Prêt à cuisiner",
"almostThere": "Presque là (7099 %)",
"partialMatches": "Correspondances partielles",
"noMatches": "Aucune correspondance trouvée",
"missing": "Manquant",
"ingredientProgress": "{matched}/{total} ingrédients",
"useItUp": "À utiliser vite"
},
"mealPlan": {
"title": "Planning repas",
"shoppingLists": "Listes de courses",
"print": "Imprimer",
"shareTitle": "Partager le planning de cette semaine",
"shareDescription": "Invitez des membres du foyer à voir ou modifier le planning repas de cette semaine.",
"sharedWithYou": "Partagé avec vous",
"sharedPlanOf": "Planning de {name}",
"weekOf": "Semaine du {date}",
"sharedMealPlanTitle": "Planning repas de {name}",
"addTo": "Ajouter à {day} {meal}",
"searchRecipes": "Rechercher des recettes…",
"servings": "Portions",
"servingsAbbrev": "{count} pers.",
"difficulty": "Difficulté",
"noRecipes": "Aucune recette trouvée",
"addFailed": "Échec de l'ajout",
"addedToPantry": "{count, plural, one {1 article ajouté au garde-manger} other {{count} articles ajoutés au garde-manger}}",
"moveToPantryFailed": "Échec du déplacement des articles vers le garde-manger",
"aisleOther": "Autre",
"listEmptyState": "Cette liste est vide.",
"checkedCount": "{checked}/{total} cochés",
"moveToPantry": "Déplacer {count} vers le garde-manger",
"listCreated": "Liste créée",
"days": {
"mon": "Lun",
"tue": "Mar",
"wed": "Mer",
"thu": "Jeu",
"fri": "Ven",
"sat": "Sam",
"sun": "Dim"
},
"meals": {
"breakfast": "Petit-déjeuner",
"lunch": "Déjeuner",
"dinner": "Dîner",
"snack": "Collation"
},
"generateWithAi": "Générer avec l'IA",
"aiModalDescription": "L'IA va générer une semaine complète de repas et créer des recettes brouillons.",
"dietaryPrefs": "Préférences alimentaires",
"dietaryPrefsPlaceholder": "ex. végétarien, sans gluten, low-carb…",
"aiPantryNote": "Les articles de votre garde-manger seront pris en compte.",
"generating": "Génération…",
"generate": "Générer",
"cancel": "Annuler",
"aiGenerated": "Plan de repas généré !",
"includePantryItems": "Inclure les articles du garde-manger",
"pantryMode": "Mode garde-manger — maximiser ce que vous avez",
"pantryModeDescription": "Préférer les repas utilisant plusieurs ingrédients du garde-manger et limiter les achats supplémentaires.",
"removeFailed": "Échec de la suppression",
"generationFailed": "Échec de la génération",
"anyDifficulty": "Toute",
"aiPhaseAnalyzing": "Analyse de vos préférences…",
"aiPhasePlanning": "Planification du petit-déjeuner, déjeuner et dîner…",
"aiPhaseSelecting": "Sélection des recettes pour chaque jour…",
"aiPhaseBalancing": "Équilibrage nutritionnel de la semaine…",
"aiPhaseFinalizing": "Finalisation de votre planning repas…",
"newList": "Nouvelle liste",
"newListTitle": "Nouvelle liste de courses",
"listNameLabel": "Nom",
"listNamePlaceholder": "Courses de la semaine",
"generateFromWeek": "Générer depuis une semaine du planning (facultatif)",
"generateFromWeekHint": "Sélectionne le lundi de la semaine choisie",
"listCreateFailed": "Échec de la création",
"listCreating": "Création…",
"listCreate": "Créer",
"pickRecipe": "Choisir une recette",
"addEntry": "+ Ajouter"
},
"pantry": {
"title": "Garde-manger",
"subtitle": "Suivez ce que vous avez en stock",
"canCook": "Que puis-je cuisiner ?",
"itemNamePlaceholder": "Nom de l'article",
"qtyPlaceholder": "Qté",
"unitPlaceholder": "Unité",
"add": "Ajouter",
"addFailed": "Échec de l'ajout",
"removeFailed": "Échec de la suppression",
"empty": "Aucun article dans le garde-manger.",
"expired": "Expiré",
"expiresInDays": "Expire dans {days}j",
"expiresOn": "Expire le {date}",
"printItemCountSingular": "{count} article",
"printItemCountPlural": "{count} articles",
"noItems": "Aucun article dans le garde-manger.",
"colItem": "Article",
"colQuantity": "Quantité",
"colExpires": "Expire",
"expiredDaysAgo": "Expiré il y a {days}j"
},
"feed": {
"title": "Fil d'actualité",
"followEmpty": "Suivez des chefs pour voir leurs recettes ici.",
"noNew": "Aucune nouvelle recette de vos abonnements.",
"following": "Abonnements",
"trending": "Tendances",
"trendingEmpty": "Aucune recette tendance cette semaine.",
"forYou": "Pour vous",
"forYouEmpty": "Ajoutez des favoris ou notez des recettes pour des suggestions personnalisées.",
"loading": "Chargement…"
},
"shoppingLists": {
"title": "Listes de courses",
"subtitle": "Planifiez vos courses",
"empty": "Aucune liste de courses",
"listEmpty": "Vide",
"generated": "Générée",
"shareTitle": "Partager la liste de courses",
"shareDescription": "Invitez des membres du foyer à voir ou modifier cette liste.",
"sharedWithYou": "Partagées avec vous",
"sendToGroceryDelivery": "Envoyer à un service de livraison",
"copyAsText": "Copier la liste en texte",
"sendToInstacart": "Envoyer à Instacart",
"copiedToClipboard": "Liste copiée dans le presse-papiers",
"exportBuildFailed": "Impossible de générer l'export",
"instacartNotConfigured": "Instacart n'est pas encore configuré",
"items": "{checked}/{total} articles",
"itemsChecked": "{checked}/{total} articles cochés",
"fromMealPlan": " · Depuis le planning repas",
"itemCount": "{count} article",
"itemCountPlural": "{count} articles",
"addToList": "Ajouter à la liste",
"dialogTitle": "Ajouter à la liste de courses",
"dialogDescription": "Ajouter les ingrédients de <strong>{title}</strong> à une liste de courses.",
"scaledSuffix": "ajustée)",
"loadingLists": "Chargement de vos listes…",
"modeExisting": "Ajouter à une liste existante",
"modeNew": "Créer une nouvelle liste",
"listNamePlaceholder": "Nom de la liste",
"ingredientsWillBeAdded": "{count, plural, one {1 ingrédient sera ajouté.} other {{count} ingrédients seront ajoutés.}}",
"adding": "Ajout…",
"addIngredients": "Ajouter les ingrédients",
"addedCount": "{count} ingrédients ajoutés à la liste"
},
"collections": {
"title": "Collections",
"subtitle": "Organisez vos recettes favorites en collections",
"empty": "Aucune collection pour l'instant",
"public": "Publique",
"recipeCount": "{count} recette",
"recipeCountPlural": "{count} recettes",
"exportPdf": "Exporter en PDF",
"shareTitle": "Partager la collection",
"shareDescription": "Invitez des personnes à voir ou modifier cette collection.",
"new": "Nouvelle collection",
"newTitle": "Nouvelle collection",
"nameLabel": "Nom",
"namePlaceholder": "Dîners du week-end",
"descriptionLabel": "Description",
"descriptionPlaceholder": "Facultatif…",
"makePublic": "Rendre publique",
"creating": "Création…",
"create": "Créer",
"createFailed": "Échec de la création",
"forking": "Duplication…",
"forkCollection": "Dupliquer la collection",
"forkFailed": "Échec de la duplication"
},
"shareDialog": {
"invite": "Inviter",
"emailPlaceholder": "Adresse e-mail",
"owner": "Propriétaire",
"viewer": "Lecteur",
"editor": "Éditeur",
"loadingMembers": "Chargement des membres…",
"noMembers": "Aucun membre pour l'instant.",
"invitationSent": "Invitation envoyée",
"alreadyMember": "Déjà membre",
"userNotFound": "Utilisateur introuvable",
"inviteFailed": "Impossible d'inviter cet utilisateur",
"loadMembersFailed": "Impossible de charger les membres",
"memberRemoved": "Membre retiré",
"removeMemberFailed": "Impossible de retirer le membre",
"enterEmail": "Saisissez une adresse e-mail"
},
"social": {
"followed": "Abonné",
"unfollowed": "Désabonné",
"commentFailed": "Échec de la publication du commentaire",
"deleted": "Supprimé",
"ratingSaved": "Note enregistrée",
"collectionCreated": "Collection créée",
"collectionForked": "Collection dupliquée dans votre bibliothèque",
"inviteSent": "Invitation envoyée",
"memberRemoved": "Membre supprimé",
"replyPlaceholder": "Répondre…",
"commentPlaceholder": "Partagez vos impressions…"
},
"cookingMode": {
"cooking": "En cuisine",
"stepOf": "Étape {current} sur {total}",
"ingredients": "Ingrédients",
"startTimer": "Démarrer le minuteur",
"timerDone": "Terminé",
"pause": "Pause",
"reset": "Réinitialiser",
"previous": "Précédent",
"next": "Suivant",
"finish": "Terminé !",
"voiceNotSupported": "Commandes vocales non supportées dans ce navigateur",
"voiceError": "Erreur vocale — réessayez",
"enableVoice": "Activer les commandes vocales",
"stopVoice": "Désactiver les commandes vocales",
"toggleIngredients": "Afficher les ingrédients",
"pageTitle": "Cuisiner : {title}",
"pageTitleFallback": "Mode cuisine",
"shortcutsTitle": "Raccourcis et commandes",
"keyboardSection": "Clavier",
"voiceSection": "Commandes vocales",
"enableMicFirst": "Activez le micro via le bouton micro d'abord.",
"stepRepeatPrefix": "Étape {n} : ",
"shortcuts": {
"nextStep": "Étape suivante",
"prevStep": "Étape précédente",
"startTimer": "Démarrer le minuteur",
"toggleHelp": "Afficher/masquer ce panneau",
"exitMode": "Quitter le mode cuisine"
},
"voiceLabels": {
"nextCmd": "suivant / continuer",
"prevCmd": "précédent / retour",
"timerCmd": "minuteur / démarrer",
"stopCmd": "arrêter / pause",
"resetCmd": "réinitialiser",
"repeatCmd": "répéter / encore",
"nextLabel": "Étape suivante",
"prevLabel": "Étape précédente",
"timerLabel": "Démarrer le minuteur",
"stopLabel": "Mettre en pause",
"resetLabel": "Réinitialiser le minuteur",
"repeatLabel": "Relire l'étape"
}
},
"settingsForm": {
"profile": "Profil",
"displayName": "Nom d'affichage",
"email": "E-mail",
"emailReadOnly": "L'e-mail ne peut pas être modifié ici.",
"saving": "Enregistrement…",
"title": "Paramètres",
"subtitle": "Gérez votre compte et vos préférences",
"language": "Langue",
"languageDescription": "Choisissez la langue de l'application et du contenu généré par l'IA.",
"changeEmail": "Changer l'e-mail",
"newEmail": "Nouvelle adresse e-mail",
"changeEmailDescription": "Un lien de vérification sera envoyé à votre nouvelle adresse avant que la modification soit prise en compte.",
"sendVerification": "Envoyer la vérification",
"sendingVerification": "Envoi…",
"emailChangeSent": "E-mail de vérification envoyé. Vérifiez votre boîte de réception.",
"emailChangeFailed": "Échec de l'envoi de l'e-mail de vérification.",
"changePassword": "Changer le mot de passe",
"currentPassword": "Mot de passe actuel",
"newPassword": "Nouveau mot de passe",
"confirmPassword": "Confirmer le nouveau mot de passe",
"passwordMismatch": "Les mots de passe ne correspondent pas.",
"changingPassword": "Modification…",
"changePasswordButton": "Changer le mot de passe",
"passwordChanged": "Mot de passe modifié avec succès.",
"passwordChangeFailed": "Échec de la modification du mot de passe.",
"webhookCreateFailed": "Échec de la création du webhook",
"webhookDeleteFailed": "Échec de la suppression du webhook",
"webhookUpdateFailed": "Échec de la mise à jour du webhook",
"webhookUrlPlaceholder": "https://exemple.fr/webhook",
"webhookAdding": "Ajout…",
"webhookAddButton": "Ajouter un webhook",
"webhookDocsLink": "Voir la doc webhook et l'intégration Zapier →",
"endpointUrlLabel": "URL de destination",
"eventsLabel": "Événements",
"eventsHint": "Sélectionnez les événements qui déclenchent ce webhook. Laissez tout décoché pour recevoir tous les événements.",
"secretRevealNotice": "Enregistrez ce secret de signature — il ne sera plus affiché.",
"secretUsageHint": "Utilisez-le pour vérifier l'en-tête X-Epicure-Signature des requêtes entrantes.",
"noWebhooks": "Aucun webhook pour l'instant.",
"addedOn": "Ajouté le {date}",
"active": "Actif",
"inactive": "Inactif",
"allEvents": "Tous les événements",
"deliveries": "Livraisons",
"disable": "Désactiver",
"enable": "Activer",
"noDeliveriesYet": "Aucune livraison pour l'instant.",
"redeliverySuccess": "Renvoi mis en file d'attente",
"redeliveryFailed": "Échec du renvoi",
"apiKeyCreateFailed": "Échec de la création de la clé API",
"apiKeyRevokeFailed": "Échec de la révocation de la clé API",
"apiKeyNamePlaceholder": "ex. Mon application",
"apiKeyNameLabel": "Nom de la clé",
"apiKeyCreating": "Création…",
"apiKeyCreate": "Créer",
"apiKeyRevealNotice": "Enregistrez cette clé — elle ne sera plus affichée.",
"copy": "Copier",
"copied": "Copié !",
"dismiss": "Ignorer",
"noApiKeys": "Aucune clé API pour l'instant.",
"createdOn": "Créée le {date}",
"lastUsedOn": "Utilisée le {date}",
"neverUsed": "Jamais utilisée",
"revoke": "Révoquer",
"copyFailed": "Échec de la copie dans le presse-papiers",
"byokSaved": "Clé {provider} enregistrée",
"byokSaveFailed": "Échec de l'enregistrement de la clé",
"byokRemoved": "Clé {provider} supprimée",
"byokRemoveFailed": "Échec de la suppression de la clé",
"byokConfigured": "Configurée",
"byokOllamaHint": "Définie via la variable d'environnement OLLAMA_BASE_URL",
"byokSaving": "Enregistrement…",
"byokSave": "Enregistrer",
"byokFooter": "Vos clés sont chiffrées au repos. Une fois définies, elles remplacent les clés par défaut de l'application pour votre compte.",
"modelSaved": "Préférences de modèle enregistrées",
"modelSaveFailed": "Échec de l'enregistrement",
"useCaseText": "Génération de texte",
"useCaseTextDesc": "Génération de recettes, variations, traductions, adaptation",
"useCaseVision": "Vision / import photo",
"useCaseVisionDesc": "Reconnaissance de plats, import de recette depuis une photo",
"useCaseMealPlan": "Planning repas",
"useCaseMealPlanDesc": "Génération du planning repas hebdomadaire",
"providerLabel": "Fournisseur",
"modelLabel": "Modèle",
"defaultAuto": "Par défaut (auto)",
"modelDefault": "Par défaut",
"modelSaving": "Enregistrement…",
"saveModelPrefs": "Enregistrer les préférences de modèle",
"modelDefaultPlaceholder": "Par défaut",
"modelNamePlaceholder": "ex. llama3.2",
"userUpdated": "Utilisateur mis à jour avec succès",
"userUpdateFailed": "Échec de l'enregistrement",
"adminSettingsSaved": "Paramètres enregistrés",
"adminSettingsFailed": "Échec de l'enregistrement des paramètres",
"nutritionGoalsSaved": "Objectifs nutritionnels enregistrés",
"bio": "Bio",
"publicBio": "Bio publique",
"publicBioDescription": "Affichée sur votre profil public.",
"publicBioPlaceholder": "Parlez de vous aux autres cuisiniers…",
"privateBio": "Contexte IA (privé)",
"privateBioDescription": "Jamais visible publiquement. Injecté dans les prompts IA pour personnaliser les suggestions — ajoutez vos préférences alimentaires, équipements, niveau de cuisine, allergies, etc.",
"privateBioPlaceholder": "ex. Je suis végétarien, j'ai un robot pâtissier et une friteuse à air, niveau intermédiaire, allergique aux fruits à coque, je préfère les saveurs méditerranéennes…"
}
}