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:
+225
-9
@@ -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 A–Z",
|
||||
"sortTitleZA": "Titre Z–A",
|
||||
"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",
|
||||
|
||||
Reference in New Issue
Block a user