fix: mobile layout fixes, i18n coverage, and recipe share link

Mobile:
- Recipes search bar full-width on mobile instead of capped narrow
- Cook mode ingredients panel stacks above the step instead of
  squeezing it into a narrow column
- Version history Compare/Restore buttons wrap onto their own row
- Recipe edit ingredient fields wrap instead of forcing horizontal
  scroll on narrow viewports

i18n: translates remaining hardcoded strings across recipes
filter/sort, adapt-recipe and AI variations dialogs, the full
settings section (sidebar + 6 sub-pages + BYOK/model-prefs/
API-keys/webhooks managers), explore tab, collections (new/fork/
share dialogs), meal planning (planner, AI generation phases, new
shopping list, shared-plan view), photo import, recipe bulk-select
toolbar, and recipe action-button tooltips. Also fixes the recipes
page subtitle, which wasn't just unworded but missing its {count}
interpolation entirely — it always rendered as the bare word
"results" regardless of how many recipes existed.

Feature: adds a ShareRecipeButton that copies the public /r/{id}
link to the clipboard, with a notice when the recipe isn't Public
yet.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Arnaud
2026-07-02 15:13:51 +02:00
parent b07bada291
commit eb424d8c04
44 changed files with 932 additions and 376 deletions
+225 -9
View File
@@ -16,6 +16,11 @@
"language": "Language"
},
"recipe": {
"share": "Share",
"shareLinkCopied": "Link copied to clipboard",
"shareCopyFailed": "Failed to copy link",
"shareNotPublicNotice": "This link will only work once the recipe is set to Public — link copied anyway.",
"print": "Print",
"ingredients": "Ingredients",
"instructions": "Instructions",
"photos": "Photos",
@@ -37,6 +42,17 @@
"adapted": "Adapted recipe saved as draft",
"adaptFailed": "Failed to adapt recipe",
"adaptConstraintRequired": "Select at least one ingredient to exclude or add a constraint",
"adaptTooltip": "Adapt",
"adaptTitle": "Adapt this recipe",
"adaptDescription": "Tap ingredients to exclude them. AI will find the best substitutes while preserving the dish.",
"excludeIngredients": "Exclude ingredients",
"excludedCount": "{count} excluded",
"additionalConstraints": "Additional constraints",
"optional": "optional",
"adapting": "Adapting recipe — this may take 2030 seconds…",
"adaptingButton": "Adapting…",
"adaptButton": "Adapt recipe",
"clearExclusions": "Clear",
"variationsDirectionsPlaceholder": "e.g. make it vegan, use only pantry staples, reduce sugar…",
"historyRestoreFailed": "Failed to restore version",
"pairingMealFailed": "Failed to suggest pairings",
@@ -48,12 +64,30 @@
"cookAction": "Cook",
"adaptConstraintPlaceholder": "e.g. Make it vegan, lower the calories, use pantry staples only, gluten-free…",
"photoImportFailed": "Failed to import recipe from photo.",
"importFromPhoto": "Import from Photo",
"analyzingPhoto": "Analyzing photo…",
"pairMealTooltip": "Pair meal",
"historyTooltip": "History",
"translateTooltip": "Translate",
"drinksTooltip": "Drinks",
"variationsTooltip": "Variations",
"viewPublicly": "View publicly",
"shoppingListCreateFailed": "Failed to create list",
"shoppingListAddFailed": "Failed to add ingredients",
"bulkDeleted": "{count, plural, one {1 recipe deleted} other {{count} recipes deleted}}",
"bulkVisibility": "{count, plural, one {1 recipe set to {visibility}} other {{count} recipes set to {visibility}}}",
"bulkDeleteFailed": "Delete failed",
"bulkUpdateFailed": "Update failed",
"bulkDeleteConfirm": "{count, plural, one {Delete 1 recipe?} other {Delete {count} recipes?}} This cannot be undone.",
"deselectAll": "Deselect all",
"selectAll": "Select all",
"clickToSelect": "Click cards to select",
"selectedCount": "{count} selected",
"select": "Select",
"visibility": "Visibility",
"makePublic": "Make public",
"makeUnlisted": "Make unlisted",
"makePrivate": "Make private",
"visibility": {
"private": "Private",
"unlisted": "Unlisted",
@@ -103,7 +137,8 @@
"contentGenerated": "Ingredients and steps generated!",
"contentGenerateFailed": "Generation failed",
"contentSaveFailed": "Failed to save generated content",
"generatingContent": "Generating recipe content…"
"generatingContent": "Generating recipe content…",
"photoAnalysisNote": "AI will analyze this dish and infer ingredients, quantities, and cooking steps."
},
"variations": {
"title": "AI Recipe Variations",
@@ -118,7 +153,10 @@
"success": "Variation created — review and edit before publishing.",
"error": "Failed to generate variations",
"saveError": "Failed to save variation",
"wait": "This may take 2030 seconds"
"wait": "This may take 2030 seconds",
"directions": "Directions",
"optional": "optional",
"stepLabel": "Step {n}"
},
"translate": {
"title": "Translate Recipe",
@@ -138,7 +176,37 @@
"language": "Language",
"units": "Units",
"metric": "Metric",
"imperial": "Imperial"
"imperial": "Imperial",
"security": "Security",
"aiModels": "AI & Models",
"notifications": "Notifications",
"nutrition": "Nutrition",
"apiKeys": "API Keys",
"webhooks": "Webhooks",
"byok": {
"title": "Your API Keys (BYOK)",
"description": "Use your own API keys instead of the app's shared quota. Keys are encrypted at rest."
},
"modelPrefs": {
"title": "Model Preferences",
"description": "Choose which model to use for each task. Defaults to your first configured provider."
},
"apiKeysPage": {
"title": "API Keys",
"description": "Manage API keys for programmatic access to the Epicure API."
},
"pushNotifications": {
"title": "Push Notifications",
"description": "Get notified when someone comments on your recipes or likes your content."
},
"nutritionGoals": {
"title": "Daily Nutrition Goals",
"description": "Set your daily targets. These are shown as progress bars on your meal plan."
},
"webhooksPage": {
"title": "Webhooks",
"description": "Receive HTTP callbacks when events happen in your Epicure account."
}
},
"print": {
"footer": "Printed from Epicure",
@@ -190,9 +258,26 @@
"aiScaledNote": "AI-scaled quantities shown below"
},
"explore": {
"title": "Explore",
"searchPlaceholder": "Search public recipes…",
"maxMinutes": "Max minutes",
"aiSearchPlaceholder": "e.g. quick weeknight dinners, Italian comfort food…"
"aiSearchPlaceholder": "e.g. quick weeknight dinners, Italian comfort food…",
"anyDifficulty": "Any difficulty",
"resultsFoundSingular": "{count} recipe found",
"resultsFoundPlural": "{count} recipes found",
"noResultsTitle": "No recipes found for “{query}”",
"noResultsHint": "Try different keywords or remove filters",
"loadMore": "Load more",
"recipeIdeas": "Recipe ideas",
"recipeIdeasHint": "Tell the AI what you're in the mood for, or let it surprise you.",
"generating": "Generating…",
"getIdeas": "Get ideas",
"surpriseMe": "Surprise me",
"generateFullRecipe": "Generate full recipe",
"trendingThisWeek": "Trending this week",
"noTrending": "No trending recipes yet. Be the first to share one!",
"recentlyAdded": "Recently added",
"noRecent": "No public recipes yet."
},
"common": {
"loading": "Loading…",
@@ -200,6 +285,8 @@
"save": "Save",
"saved": "Saved",
"saveFailed": "Failed to save",
"print": "Print",
"share": "Share",
"deleteFailed": "Delete failed",
"updateFailed": "Update failed",
"copyFailed": "Failed to copy to clipboard",
@@ -276,10 +363,25 @@
"noMatch": "No recipes match",
"clearSearch": "Clear search",
"createFirst": "Create your first recipe",
"resultSingular": "result",
"resultPlural": "results",
"resultSingular": "{count} recipe",
"resultPlural": "{count} recipes",
"for": "for",
"filterByTag": "Filter by tag…"
"filterByTag": "Filter by tag…",
"sortRecentlyUpdated": "Recently updated",
"sortOldestUpdated": "Oldest updated",
"sortNewestFirst": "Newest first",
"sortOldestFirst": "Oldest first",
"sortTitleAZ": "Title AZ",
"sortTitleZA": "Title ZA",
"sort": "Sort",
"sortBy": "Sort by",
"filter": "Filter",
"all": "All",
"anyDifficulty": "Any difficulty",
"visibilityLabel": "Visibility",
"difficultyLabel": "Difficulty",
"tagLabel": "Tag",
"clearFilters": "Clear filters"
},
"recipeForm": {
"titleLabel": "Title *",
@@ -333,6 +435,13 @@
"mealPlan": {
"title": "Meal Plan",
"shoppingLists": "Shopping lists",
"print": "Print",
"shareTitle": "Share this week's plan",
"shareDescription": "Invite household members to view or edit this week's meal plan.",
"sharedWithYou": "Shared with you",
"sharedPlanOf": "{name}'s plan",
"weekOf": "Week of {date}",
"sharedMealPlanTitle": "{name}'s Meal Plan",
"addTo": "Add to {day} {meal}",
"searchRecipes": "Search recipes…",
"servings": "Servings",
@@ -373,7 +482,26 @@
"aiGenerated": "Meal plan generated!",
"includePantryItems": "Include pantry items",
"pantryMode": "Pantry mode — maximize use of what you have",
"pantryModeDescription": "Prefer meals that use multiple pantry ingredients and minimize extra shopping."
"pantryModeDescription": "Prefer meals that use multiple pantry ingredients and minimize extra shopping.",
"removeFailed": "Failed to remove",
"generationFailed": "Generation failed",
"anyDifficulty": "Any",
"aiPhaseAnalyzing": "Analyzing your preferences…",
"aiPhasePlanning": "Planning breakfast, lunch & dinner…",
"aiPhaseSelecting": "Selecting recipes for each day…",
"aiPhaseBalancing": "Balancing nutrition across the week…",
"aiPhaseFinalizing": "Finalizing your meal plan…",
"newList": "New list",
"newListTitle": "New shopping list",
"listNameLabel": "Name",
"listNamePlaceholder": "Weekly groceries",
"generateFromWeek": "Generate from meal plan week (optional)",
"generateFromWeekHint": "Picks Monday of the selected week",
"listCreateFailed": "Failed to create",
"listCreating": "Creating…",
"listCreate": "Create",
"pickRecipe": "Pick recipe",
"addEntry": "+ Add"
},
"pantry": {
"title": "Pantry",
@@ -397,9 +525,20 @@
"empty": "No shopping lists yet",
"listEmpty": "Empty",
"generated": "Generated",
"shareTitle": "Share shopping list",
"shareDescription": "Invite household members to view or edit this list.",
"sharedWithYou": "Shared with you",
"sendToGroceryDelivery": "Send to grocery delivery",
"copyAsText": "Copy list as text",
"sendToInstacart": "Send to Instacart",
"copiedToClipboard": "List copied to clipboard",
"exportBuildFailed": "Could not build export",
"instacartNotConfigured": "Instacart isn't configured yet",
"items": "{checked}/{total} items",
"itemsChecked": "{checked}/{total} items checked",
"fromMealPlan": " · From meal plan",
"itemCount": "{count} item",
"itemCountPlural": "{count} items",
"addToList": "Add to list",
"dialogTitle": "Add to shopping list",
"dialogDescription": "Add the ingredients of <strong>{title}</strong> to a shopping list.",
@@ -419,7 +558,40 @@
"empty": "No collections yet",
"public": "Public",
"recipeCount": "{count} recipe",
"recipeCountPlural": "{count} recipes"
"recipeCountPlural": "{count} recipes",
"exportPdf": "Export as PDF",
"shareTitle": "Share collection",
"shareDescription": "Invite people to view or edit this collection.",
"new": "New collection",
"newTitle": "New collection",
"nameLabel": "Name",
"namePlaceholder": "Weekend dinners",
"descriptionLabel": "Description",
"descriptionPlaceholder": "Optional…",
"makePublic": "Make public",
"creating": "Creating…",
"create": "Create",
"createFailed": "Failed to create",
"forking": "Forking…",
"forkCollection": "Fork collection",
"forkFailed": "Fork failed"
},
"shareDialog": {
"invite": "Invite",
"emailPlaceholder": "Email address",
"owner": "Owner",
"viewer": "Viewer",
"editor": "Editor",
"loadingMembers": "Loading members…",
"noMembers": "No members yet.",
"invitationSent": "Invitation sent",
"alreadyMember": "Already a member",
"userNotFound": "User not found",
"inviteFailed": "Could not invite user",
"loadMembersFailed": "Could not load members",
"memberRemoved": "Member removed",
"removeMemberFailed": "Could not remove member",
"enterEmail": "Enter an email address"
},
"social": {
"followed": "Following",
@@ -513,9 +685,36 @@
"apiKeyCreateFailed": "Failed to create API key",
"apiKeyRevokeFailed": "Failed to revoke API key",
"apiKeyNamePlaceholder": "e.g. My app",
"apiKeyNameLabel": "Key name",
"apiKeyCreating": "Creating…",
"apiKeyCreate": "Create",
"apiKeyRevealNotice": "Save this key — it will not be shown again.",
"copy": "Copy",
"copied": "Copied!",
"dismiss": "Dismiss",
"noApiKeys": "No API keys yet.",
"createdOn": "Created {date}",
"lastUsedOn": "Last used {date}",
"neverUsed": "Never used",
"revoke": "Revoke",
"webhookUrlPlaceholder": "https://example.com/webhook",
"webhookAdding": "Adding…",
"webhookAddButton": "Add webhook",
"webhookDocsLink": "View webhook docs & Zapier integration →",
"endpointUrlLabel": "Endpoint URL",
"eventsLabel": "Events",
"eventsHint": "Select which events trigger this webhook. Leave all unchecked to receive all events.",
"secretRevealNotice": "Save this signing secret — it will not be shown again.",
"secretUsageHint": "Use it to verify the X-Epicure-Signature header on incoming requests.",
"noWebhooks": "No webhooks yet.",
"addedOn": "Added {date}",
"active": "Active",
"inactive": "Inactive",
"allEvents": "All events",
"deliveries": "Deliveries",
"disable": "Disable",
"enable": "Enable",
"noDeliveriesYet": "No deliveries yet.",
"modelDefaultPlaceholder": "Default",
"modelNamePlaceholder": "e.g. llama3.2",
"copyFailed": "Failed to copy to clipboard",
@@ -523,8 +722,25 @@
"byokSaveFailed": "Failed to save key",
"byokRemoved": "{provider} key removed",
"byokRemoveFailed": "Failed to remove key",
"byokConfigured": "Configured",
"byokOllamaHint": "Set via OLLAMA_BASE_URL env var",
"byokSaving": "Saving…",
"byokSave": "Save",
"byokFooter": "Your keys are encrypted at rest. When set, they override the app's default keys for your account.",
"modelSaved": "Model preferences saved",
"modelSaveFailed": "Failed to save",
"useCaseText": "Text generation",
"useCaseTextDesc": "Recipe generation, variations, translations, adapt",
"useCaseVision": "Vision / photo import",
"useCaseVisionDesc": "Dish recognition, recipe import from photo",
"useCaseMealPlan": "Meal planning",
"useCaseMealPlanDesc": "Weekly meal plan generation",
"providerLabel": "Provider",
"modelLabel": "Model",
"defaultAuto": "Default (auto)",
"modelDefault": "Default",
"modelSaving": "Saving…",
"saveModelPrefs": "Save model preferences",
"userUpdated": "User updated successfully",
"userUpdateFailed": "Failed to save",
"adminSettingsSaved": "Settings saved",
+225 -9
View File
@@ -16,6 +16,11 @@
"language": "Langue"
},
"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",
@@ -37,6 +42,17 @@
"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",
@@ -48,12 +64,30 @@
"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",
@@ -103,7 +137,8 @@
"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…"
"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",
@@ -118,7 +153,10 @@
"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"
"wait": "Cela peut prendre 20 à 30 secondes",
"directions": "Instructions",
"optional": "facultatif",
"stepLabel": "Étape {n}"
},
"translate": {
"title": "Traduire la recette",
@@ -138,7 +176,37 @@
"language": "Langue",
"units": "Unités",
"metric": "Métrique",
"imperial": "Impérial"
"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",
@@ -190,9 +258,26 @@
"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…"
"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…",
@@ -200,6 +285,8 @@
"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",
@@ -267,10 +354,25 @@
"noMatch": "Aucune recette ne correspond",
"clearSearch": "Effacer la recherche",
"createFirst": "Créer votre première recette",
"resultSingular": "résultat",
"resultPlural": "résultats",
"resultSingular": "{count} recette",
"resultPlural": "{count} recettes",
"for": "pour",
"filterByTag": "Filtrer par tag…"
"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 *",
@@ -321,6 +423,13 @@
"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",
@@ -361,7 +470,26 @@
"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."
"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",
@@ -385,9 +513,20 @@
"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.",
@@ -407,7 +546,40 @@
"empty": "Aucune collection pour l'instant",
"public": "Publique",
"recipeCount": "{count} recette",
"recipeCountPlural": "{count} recettes"
"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é",
@@ -499,18 +671,62 @@
"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",