Files
Epicure/apps/web/messages/en.json
T
Arnaud e5d1080fb9 feat: implement remaining TODO.md feature ideas + fix mobile headers
Implements the six previously-unscoped feature ideas plus a mobile
layout fix reported via screenshot:

- Mobile: Recipes/Collections/Pantry/Meal Plan/Shopping Lists headers
  now stack and wrap instead of clipping buttons on narrow viewports.
- Recipe diff/compare view: word/list diff against any past version,
  next to Restore in version history.
- Shared meal plans & shopping lists: new shoppingListMembers/
  mealPlanMembers tables (viewer/editor roles, mirrors
  collectionMembers), share dialogs, membership-checked routes.
- PDF cookbook export: /print/collection/[id] renders a whole
  collection with page breaks, using the existing print-CSS pattern
  instead of adding a PDF rendering dependency.
- Grocery delivery handoff: shopping lists can copy-as-text (works
  today) or send to Instacart once INSTACART_API_KEY is configured
  (stub adapter — real API needs a partner agreement).
- Personalized "For You" feed tab: ranks public recipes by tag/
  dietary overlap with the user's favorited/highly-rated history.
- PWA: added manifest.json + icons on top of the existing service
  worker so the app is installable; cook-mode pages were already
  cached for offline use.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-02 12:13:00 +02:00

542 lines
20 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": {
"recipes": "Recipes",
"explore": "Explore",
"search": "Search",
"feed": "Feed",
"collections": "Collections",
"mealPlan": "Meal Plan",
"pantry": "Pantry",
"shopping": "Shopping",
"settings": "Settings",
"admin": "Admin",
"signOut": "Sign out",
"apiKeys": "API keys",
"webhooks": "Webhooks",
"language": "Language"
},
"recipe": {
"ingredients": "Ingredients",
"instructions": "Instructions",
"photos": "Photos",
"servings": "{count} servings",
"prep": "{mins}m prep",
"cook": "{mins}m cook",
"total": "{mins}m total",
"edit": "Edit",
"variations": "Variations",
"save": "Save",
"cancel": "Cancel",
"delete": "Delete",
"noIngredientsSteps": "No ingredients or steps yet.",
"generateContent": "Generate with AI",
"editManually": "Edit manually",
"deleted": "Recipe deleted",
"deleteFailed": "Delete failed",
"imported": "Recipe imported! Review before publishing.",
"adapted": "Adapted recipe saved as draft",
"adaptFailed": "Failed to adapt recipe",
"adaptConstraintRequired": "Select at least one ingredient to exclude or add a constraint",
"variationsDirectionsPlaceholder": "e.g. make it vegan, use only pantry staples, reduce sugar…",
"historyRestoreFailed": "Failed to restore version",
"pairingMealFailed": "Failed to suggest pairings",
"pairingDrinkFailed": "Failed to suggest drinks",
"pairingGenerateFailed": "Failed to generate \"{name}\"",
"pairingSaveFailed": "Failed to save \"{name}\"",
"pairingSuccess": "Recipe generated — review before publishing",
"pairingBulkSuccess": "{count} recipes generated — find them in your library",
"cookAction": "Cook",
"adaptConstraintPlaceholder": "e.g. Make it vegan, lower the calories, use pantry staples only, gluten-free…",
"photoImportFailed": "Failed to import recipe from photo.",
"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",
"visibility": {
"private": "Private",
"unlisted": "Unlisted",
"public": "Public"
},
"difficulty": {
"easy": "Easy",
"medium": "Medium",
"hard": "Hard"
},
"dietary": {
"vegan": "Vegan",
"vegetarian": "Vegetarian",
"glutenFree": "Gluten-free",
"dairyFree": "Dairy-free",
"nutFree": "Nut-free",
"halal": "Halal",
"kosher": "Kosher"
}
},
"ai": {
"generate": {
"title": "Generate recipe with AI",
"description": "Describe what you want to cook and AI will create a complete recipe.",
"descriptionFull": "Describe a dish in words or snap a photo — AI builds the full recipe.",
"tabDescribe": "Describe",
"tabPhoto": "From photo",
"surpriseMe": "Surprise me",
"prompt": "What do you want to cook?",
"placeholder": "e.g. A creamy mushroom risotto with parmesan, serves 4, easy difficulty",
"language": "Recipe language",
"difficulty": "Difficulty",
"anyDifficulty": "Any",
"button": "Generate",
"generating": "Generating…",
"success": "Recipe generated! Review and edit before publishing.",
"error": "Failed to generate recipe",
"saveError": "Failed to save generated recipe",
"uploadClick": "Click to upload",
"uploadDrag": "or drag a food photo",
"uploadFormats": "JPEG, PNG or WebP · AI will reverse-engineer the recipe",
"analyzing": "Analyzing…",
"analyzePhoto": "Analyzing dish photo…",
"recognizeDish": "Recognize dish",
"photoSuccess": "Recipe recognized! Review and edit before publishing.",
"photoError": "Failed to analyze photo",
"contentGenerated": "Ingredients and steps generated!",
"contentGenerateFailed": "Generation failed",
"contentSaveFailed": "Failed to save generated content",
"generatingContent": "Generating recipe content…"
},
"variations": {
"title": "AI Recipe Variations",
"description": "Generate creative variations of this recipe — dietary adaptations, flavor twists, technique changes.",
"generate": "Generate 3 variations",
"generating": "Generating variations…",
"regenerate": "Regenerate",
"apply": "Apply",
"applying": "Applying…",
"ingredientChanges": "Ingredient changes",
"stepChanges": "Step changes",
"success": "Variation created — review and edit before publishing.",
"error": "Failed to generate variations",
"saveError": "Failed to save variation",
"wait": "This may take 2030 seconds"
},
"translate": {
"title": "Translate Recipe",
"description": "Translate this recipe's title, description, ingredients, and steps into another language.",
"targetLanguage": "Target language",
"button": "Translate",
"translating": "Translating…",
"success": "Translation saved as new draft recipe.",
"error": "Failed to translate recipe",
"saveError": "Failed to save translated recipe",
"wait": "This may take 2030 seconds"
}
},
"settings": {
"title": "Settings",
"profile": "Profile",
"language": "Language",
"units": "Units",
"metric": "Metric",
"imperial": "Imperial"
},
"print": {
"footer": "Printed from Epicure",
"day": "Day",
"srv": " · {count} srv",
"days": {
"mon": "Monday",
"tue": "Tuesday",
"wed": "Wednesday",
"thu": "Thursday",
"fri": "Friday",
"sat": "Saturday",
"sun": "Sunday"
}
},
"publicRecipe": {
"publicRecipeBy": "Public recipe by",
"viewInLibrary": "View in your library",
"signUpFree": "Sign up free",
"logIn": "Log in",
"wantToSave": "Want to save this recipe?",
"wantToSaveDescription": "Create a free account to save recipes, scale servings, and build your own library.",
"getStartedFree": "Get started free"
},
"substitute": {
"fetchError": "Couldn't fetch substitutes.",
"titleFor": "Substitute for {ingredient}",
"substitutesFor": "Substitutes for",
"finding": "Finding substitutes…",
"noneFound": "No substitutions found."
},
"recipeChat": {
"sorry": "Sorry, I couldn't answer that.",
"error": "Something went wrong. Please try again.",
"suggestion1": "Can I substitute any ingredients?",
"suggestion2": "How do I know when it's done?",
"suggestion3": "Can I make this ahead of time?",
"suggestion4": "What can I serve with this?",
"ariaLabel": "Ask AI about this recipe",
"title": "Ask about this recipe",
"intro": "Ask anything about this recipe — ingredients, techniques, substitutions, timing…",
"inputPlaceholder": "Ask a question…"
},
"servingScaler": {
"servings": "Servings",
"reset": "Reset",
"aiScale": "AI Scale",
"scaling": "Scaling…",
"aiScaledNote": "AI-scaled quantities shown below"
},
"explore": {
"searchPlaceholder": "Search public recipes…",
"maxMinutes": "Max minutes",
"aiSearchPlaceholder": "e.g. quick weeknight dinners, Italian comfort food…"
},
"common": {
"loading": "Loading…",
"error": "Something went wrong",
"save": "Save",
"saved": "Saved",
"saveFailed": "Failed to save",
"deleteFailed": "Delete failed",
"updateFailed": "Update failed",
"copyFailed": "Failed to copy to clipboard",
"deleted": "Deleted",
"cancel": "Cancel",
"delete": "Delete",
"confirm": "Confirm",
"back": "Back",
"difficulty": "Difficulty",
"anyDifficulty": "Any",
"editManually": "Edit manually",
"surpriseMe": "Surprise me",
"generatingContent": "Generating recipe content…"
},
"auth": {
"signIn": "Sign in",
"signInTitle": "Sign in to your recipe library",
"signInLoading": "Signing in…",
"signInFailed": "Sign in failed",
"signUp": "Sign up",
"signUpTitle": "Create account",
"signUpSubtitle": "Start building your recipe library",
"signUpLoading": "Creating account…",
"signUpFailed": "Sign up failed",
"signUpSuccess": "Account created — check your email to verify",
"continueWithGoogle": "Continue with Google",
"continueWithGithub": "Continue with GitHub",
"continueWithDiscord": "Continue with Discord",
"continueWithAuthentik": "Continue with Authentik",
"email": "Email",
"emailPlaceholder": "you@example.com",
"password": "Password",
"name": "Name",
"namePlaceholder": "Your name",
"forgotPassword": "Forgot password?",
"forgotPasswordTitle": "Forgot password",
"forgotPasswordSent": "Check your inbox",
"forgotPasswordDescription": "Enter your email and we'll send you a reset link.",
"forgotPasswordSentDescription": "If an account exists for that email, you'll receive a reset link shortly.",
"forgotPasswordFailed": "Failed to send reset email",
"sendResetLink": "Send reset link",
"sendingLink": "Sending…",
"resetPasswordTitle": "Reset password",
"resetPasswordDescription": "Enter a new password for your account.",
"newPassword": "New password",
"confirmPassword": "Confirm password",
"updatePassword": "Update password",
"updatingPassword": "Updating…",
"invalidToken": "Invalid or missing reset token.",
"resetPasswordMismatch": "Passwords don't match",
"resetPasswordFailed": "Reset failed — link may have expired",
"resetPasswordSuccess": "Password updated",
"noAccount": "No account? Sign up",
"alreadyHaveAccount": "Already have an account? Sign in",
"backToSignIn": "Back to sign in",
"emailNotVerified": "Email not verified",
"checkInboxVerification": "Check your inbox for a verification email.",
"resendVerification": "Resend verification email",
"resendingSending": "Sending…",
"resendFailed": "Failed to resend",
"resendSuccess": "Verification email sent — check your inbox",
"or": "or"
},
"recipes": {
"title": "Recipes",
"subtitle": "Your personal recipe library",
"new": "New recipe",
"newTitle": "New recipe",
"newSubtitle": "Build your recipe from scratch",
"importUrl": "Import URL",
"generate": "Generate",
"search": "Search recipes…",
"noRecipes": "No recipes yet",
"noMatch": "No recipes match",
"clearSearch": "Clear search",
"createFirst": "Create your first recipe",
"resultSingular": "result",
"resultPlural": "results",
"for": "for",
"filterByTag": "Filter by tag…"
},
"recipeForm": {
"titleLabel": "Title *",
"titleRequired": "Title is required",
"titlePlaceholder": "My amazing recipe",
"descriptionLabel": "Description",
"descriptionPlaceholder": "What makes this recipe special…",
"servings": "Servings",
"prepMins": "Prep (min)",
"cookMins": "Cook (min)",
"difficulty": "Difficulty",
"visibility": "Visibility",
"tags": "Tags",
"tagsHelp": "Press Enter to add · Backspace to remove last · max 20 tags",
"tagsPlaceholder": "Add tags… (press Enter)",
"dietaryTags": "Dietary tags",
"photos": "Photos",
"ingredients": "Ingredients",
"amount": "Amount",
"unit": "Unit",
"ingredient": "Ingredient",
"note": "Note",
"addIngredient": "Add ingredient",
"setCover": "Set as cover",
"removePhoto": "Remove",
"expand": "Expand",
"steps": "Steps",
"stepPlaceholder": "Step {n}…",
"timerSeconds": "Timer (s)",
"addStep": "Add step",
"saving": "Saving…",
"saveChanges": "Save changes",
"createRecipe": "Create recipe",
"updateSuccess": "Recipe updated",
"createSuccess": "Recipe created",
"saveError": "Failed to save recipe"
},
"canCook": {
"title": "What can I cook?",
"subtitle": "Recipes you can make with your pantry",
"emptyPantry": "Your pantry is empty",
"addPantryItems": "Add pantry items",
"readyToCook": "Ready to cook",
"almostThere": "Almost there (7099%)",
"partialMatches": "Partial matches",
"noMatches": "No strong matches found",
"missing": "Missing",
"ingredientProgress": "{matched}/{total} ingredients",
"useItUp": "Use it up"
},
"mealPlan": {
"title": "Meal Plan",
"shoppingLists": "Shopping lists",
"addTo": "Add to {day} {meal}",
"searchRecipes": "Search recipes…",
"servings": "Servings",
"servingsAbbrev": "{count} srv",
"difficulty": "Difficulty",
"noRecipes": "No recipes found",
"addFailed": "Failed to add",
"addedToPantry": "{count, plural, one {1 item added to pantry} other {{count} items added to pantry}}",
"moveToPantryFailed": "Failed to move items to pantry",
"aisleOther": "Other",
"listEmptyState": "This list is empty.",
"checkedCount": "{checked}/{total} checked",
"moveToPantry": "Move {count} to pantry",
"listCreated": "List created",
"days": {
"mon": "Mon",
"tue": "Tue",
"wed": "Wed",
"thu": "Thu",
"fri": "Fri",
"sat": "Sat",
"sun": "Sun"
},
"meals": {
"breakfast": "Breakfast",
"lunch": "Lunch",
"dinner": "Dinner",
"snack": "Snack"
},
"generateWithAi": "Generate with AI",
"aiModalDescription": "AI will generate a full week of meals and create draft recipes for you.",
"dietaryPrefs": "Dietary preferences",
"dietaryPrefsPlaceholder": "e.g. vegetarian, gluten-free, low-carb…",
"aiPantryNote": "Your pantry items will be considered when available.",
"generating": "Generating…",
"generate": "Generate",
"cancel": "Cancel",
"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."
},
"pantry": {
"title": "Pantry",
"subtitle": "Track what you have on hand",
"canCook": "What can I cook?"
},
"feed": {
"title": "Feed",
"followEmpty": "Follow chefs to see their recipes here.",
"noNew": "No new recipes from people you follow.",
"following": "Following",
"trending": "Trending",
"trendingEmpty": "No trending recipes this week.",
"forYou": "For You",
"forYouEmpty": "Favorite or rate some recipes to get personalized picks here.",
"loading": "Loading…"
},
"shoppingLists": {
"title": "Shopping Lists",
"subtitle": "Plan your grocery runs",
"empty": "No shopping lists yet",
"listEmpty": "Empty",
"generated": "Generated",
"items": "{checked}/{total} items",
"itemsChecked": "{checked}/{total} items checked",
"fromMealPlan": " · From meal plan",
"addToList": "Add to list",
"dialogTitle": "Add to shopping list",
"dialogDescription": "Add the ingredients of <strong>{title}</strong> to a shopping list.",
"scaledSuffix": "scaled)",
"loadingLists": "Loading your lists…",
"modeExisting": "Add to existing list",
"modeNew": "Create new list",
"listNamePlaceholder": "List name",
"ingredientsWillBeAdded": "{count, plural, one {1 ingredient will be added.} other {{count} ingredients will be added.}}",
"adding": "Adding…",
"addIngredients": "Add ingredients",
"addedCount": "{count} ingredients added to list"
},
"collections": {
"title": "Collections",
"subtitle": "Curate your favorite recipes into collections",
"empty": "No collections yet",
"public": "Public",
"recipeCount": "{count} recipe",
"recipeCountPlural": "{count} recipes"
},
"social": {
"followed": "Following",
"unfollowed": "Unfollowed",
"commentFailed": "Failed to post comment",
"deleted": "Deleted",
"ratingSaved": "Rating saved",
"collectionCreated": "Collection created",
"collectionForked": "Collection forked to your library",
"inviteSent": "Invitation sent",
"memberRemoved": "Member removed",
"replyPlaceholder": "Reply…",
"commentPlaceholder": "Share your thoughts…"
},
"cookingMode": {
"cooking": "Cooking",
"stepOf": "Step {current} of {total}",
"ingredients": "Ingredients",
"startTimer": "Start timer",
"timerDone": "Done",
"pause": "Pause",
"reset": "Reset",
"previous": "Previous",
"next": "Next",
"finish": "Done!",
"voiceNotSupported": "Voice not supported in this browser",
"voiceError": "Voice error — try again",
"enableVoice": "Enable voice control",
"stopVoice": "Stop voice",
"toggleIngredients": "Toggle ingredients",
"pageTitle": "Cooking: {title}",
"pageTitleFallback": "Cooking Mode",
"shortcutsTitle": "Shortcuts & commands",
"keyboardSection": "Keyboard",
"voiceSection": "Voice commands",
"enableMicFirst": "Enable voice via the mic button first.",
"stepRepeatPrefix": "Step {n}: ",
"shortcuts": {
"nextStep": "Next step",
"prevStep": "Previous step",
"startTimer": "Start timer",
"toggleHelp": "Toggle this panel",
"exitMode": "Exit cook mode"
},
"voiceLabels": {
"nextCmd": "next / continue",
"prevCmd": "previous / back",
"timerCmd": "timer / start",
"stopCmd": "stop / pause",
"resetCmd": "reset",
"repeatCmd": "repeat / again",
"nextLabel": "Next step",
"prevLabel": "Previous step",
"timerLabel": "Start timer",
"stopLabel": "Pause timer",
"resetLabel": "Reset timer",
"repeatLabel": "Re-read step"
}
},
"settingsForm": {
"profile": "Profile",
"displayName": "Display name",
"email": "Email",
"emailReadOnly": "Email cannot be changed here.",
"saving": "Saving…",
"title": "Settings",
"subtitle": "Manage your account and preferences",
"language": "Language",
"languageDescription": "Choose your preferred language for the app and AI-generated content.",
"changeEmail": "Change email",
"newEmail": "New email address",
"changeEmailDescription": "A verification link will be sent to your new address before it takes effect.",
"sendVerification": "Send verification",
"sendingVerification": "Sending…",
"emailChangeSent": "Verification email sent. Check your inbox.",
"emailChangeFailed": "Failed to send verification email.",
"changePassword": "Change password",
"currentPassword": "Current password",
"newPassword": "New password",
"confirmPassword": "Confirm new password",
"passwordMismatch": "Passwords don't match.",
"changingPassword": "Changing…",
"changePasswordButton": "Change password",
"passwordChanged": "Password changed successfully.",
"passwordChangeFailed": "Failed to change password.",
"webhookCreateFailed": "Failed to create webhook",
"webhookDeleteFailed": "Failed to delete webhook",
"webhookUpdateFailed": "Failed to update webhook",
"redeliverySuccess": "Redelivery queued",
"redeliveryFailed": "Failed to redeliver",
"apiKeyCreateFailed": "Failed to create API key",
"apiKeyRevokeFailed": "Failed to revoke API key",
"apiKeyNamePlaceholder": "e.g. My app",
"webhookUrlPlaceholder": "https://example.com/webhook",
"webhookAdding": "Adding…",
"webhookAddButton": "Add webhook",
"modelDefaultPlaceholder": "Default",
"modelNamePlaceholder": "e.g. llama3.2",
"copyFailed": "Failed to copy to clipboard",
"byokSaved": "{provider} key saved",
"byokSaveFailed": "Failed to save key",
"byokRemoved": "{provider} key removed",
"byokRemoveFailed": "Failed to remove key",
"modelSaved": "Model preferences saved",
"modelSaveFailed": "Failed to save",
"userUpdated": "User updated successfully",
"userUpdateFailed": "Failed to save",
"adminSettingsSaved": "Settings saved",
"adminSettingsFailed": "Failed to save settings",
"nutritionGoalsSaved": "Nutrition goals saved",
"bio": "Bio",
"publicBio": "Public bio",
"publicBioDescription": "Shown on your public profile.",
"publicBioPlaceholder": "Tell other cooks about yourself…",
"privateBio": "AI context (private)",
"privateBioDescription": "Never shown publicly. Injected into AI prompts to personalise suggestions — add your dietary preferences, kitchen equipment, cooking skill level, allergies, etc.",
"privateBioPlaceholder": "e.g. I'm vegetarian, have a stand mixer and an air fryer, intermediate cook, allergic to tree nuts, prefer Mediterranean flavours…"
}
}