Files
Epicure/apps/web/messages/en.json
T
Arnaud 002f14ced0 feat: batch-cook shopping list (already worked) + leftover expiry reminders
Shopping list add already worked generically for batch-cook recipes —
no code needed there.

New: mark a specific batch-cook dish as "cooked today", track its
fridge expiry (cookingHistory.batchDishId), surface a "Leftovers
expiring soon" widget on the pantry page, and send a daily push+email
reminder via a new /api/internal/cron/leftover-reminders endpoint
(mirrors the weekly-digest cron pattern; doesn't use the social
notifications table, which requires a non-null actor and isn't built
for self-reminders).

Also fixes, from user-reported bugs:
- Recipe cards showed no batch-cook badge/dish-count/prep-time in some
  views — added dishCount + prepMins/cookMins (now generated by the AI
  and persisted) to the card component and /recipes query.
- Batch-cook descriptions occasionally contained raw markdown
  (**bold**) — added explicit "plain prose only" prompt instructions
  and a stripMarkdown() defensive fallback at render time.
- Truncated/cut-off descriptions — the generateObject call had no
  maxOutputTokens set, so long structured responses could get cut off
  mid-field; now capped explicitly at 8000.
- Generate dialogs (batch-cook + the main AI dialog) could show
  buttons unreachable once the progress bar appeared mid-generation —
  restructured so the action row is pinned outside the scrollable
  content area, not affected by content height changes.
- /api/internal/* routes were being redirected to /login by middleware
  before their own CRON_SECRET check ever ran (pre-existing bug,
  affected the weekly-digest cron too) — added to PUBLIC_PATHS.

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

1165 lines
47 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": "Recipes",
"explore": "Explore",
"search": "Search",
"feed": "Feed",
"collections": "Collections",
"mealPlan": "Meal Plan",
"nutrition": "Nutrition",
"pantry": "Pantry",
"shopping": "Shopping",
"settings": "Settings",
"admin": "Admin",
"signOut": "Sign out",
"apiKeys": "API keys",
"webhooks": "Webhooks",
"language": "Language",
"notifications": "Notifications",
"viewProfile": "View profile",
"lightMode": "Light mode",
"darkMode": "Dark mode",
"systemMode": "System"
},
"notifications": {
"title": "Notifications",
"empty": "No notifications yet.",
"markAllRead": "Mark all as read",
"viewAll": "View all",
"loading": "Loading…",
"loadMore": "Load more",
"loadFailed": "Failed to load notifications. Check your connection and try again.",
"retry": "Retry",
"unread": "Unread",
"follow": "{name} followed you",
"comment": "{name} commented on your recipe",
"reply": "{name} replied to your comment",
"reaction": "{name} reacted to your comment",
"rating": "{name} rated your recipe",
"mention": "{name} mentioned you in a comment",
"detail": {
"comment": "{name} commented on your recipe \"{title}\"",
"rating": "{name} rated your recipe \"{title}\" {stars} stars",
"leftoverExpiring": "Your \"{dish}\" from \"{title}\" is about to expire — eat it soon!"
},
"pushTitle": {
"follow": "New follower",
"comment": "New comment",
"reply": "New reply",
"reaction": "New reaction",
"rating": "New rating",
"mention": "You were mentioned",
"leftoverExpiring": "Leftovers expiring soon"
}
},
"recipe": {
"byAuthor": "by {name}",
"source": "Source",
"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",
"batchCookBadge": "Batch cooking",
"batchCookPrep": "Prep",
"batchCookDishesTitle": "Dishes & storage",
"batchCookFridgeDays": "Fridge: {days}d",
"batchCookFreezerFriendly": "Freezer-friendly",
"batchCookDayOf": "On the day",
"batchCookMarkCooked": "Mark as cooked today",
"batchCookMarkSuccess": "Marked as cooked",
"batchCookMarkFailed": "Failed to mark as cooked",
"batchCookExpiresOn": "Cooked — good until {date}",
"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",
"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",
"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",
"pairingDialogTitle": "Complete the meal",
"pairingDialogDescription": "AI-suggested dishes that pair well with this recipe. Generate any of them as a new recipe.",
"forkTooltip": "Fork this recipe",
"forked": "Recipe forked to your library",
"forkFailed": "Failed to fork recipe",
"forkLimitReached": "Recipe limit reached for your tier",
"forkedFrom": "Forked from {title}",
"pairingFindingLabel": "Finding perfect pairings…",
"pairingSuggestButton": "Suggest pairings",
"pairingRegenerate": "Regenerate",
"pairingGenerateButton": "Generate",
"pairingGeneratingButton": "Generating {current}/{total}…",
"pairingGeneratingLabel": "Generating recipe {current} of {total}…",
"pairingRoleStarter": "Starter",
"pairingRoleSide": "Side",
"pairingRoleSalad": "Salad",
"pairingRoleBread": "Bread",
"pairingRoleDrink": "Drink",
"pairingRoleDessert": "Dessert",
"pairingRoleSauce": "Sauce",
"drinksDialogTitle": "Drink pairings",
"drinksDialogDescription": "AI-suggested drinks that complement this recipe.",
"drinksSuggestButton": "Suggest drinks",
"drinksExamplesLabel": "e.g.",
"drinksTypeWine": "Wine",
"drinksTypeBeer": "Beer",
"drinksTypeCocktail": "Cocktail",
"drinksTypeSpirit": "Spirit",
"drinksTypeNonAlcoholic": "Non-alcoholic",
"drinksTypeHot": "Hot",
"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",
"urlImportTitle": "Import recipe from URL",
"urlImportDescription": "Paste a recipe URL and AI will extract the ingredients and instructions.",
"urlImportLabel": "Recipe URL",
"urlImportButton": "Import",
"urlImportingButton": "Importing…",
"urlImportFetchFailed": "Failed to import recipe",
"urlImportSaveFailed": "Failed to save imported recipe",
"urlImportSuccess": "Recipe imported! Review before publishing.",
"diffTitleLabel": "Title",
"diffDescriptionLabel": "Description",
"diffIngredientsLabel": "Ingredients",
"diffStepsLabel": "Steps",
"viewGrid": "Grid view",
"viewList": "List view",
"viewCompact": "Compact view",
"updatedLabel": "Updated {date}",
"addToCollection": "Add to collection",
"addToCollectionTitle": "Add {count, plural, one {1 recipe} other {{count} recipes}} to a collection",
"newCollectionOption": "New collection…",
"newCollectionNamePlaceholder": "Collection name",
"addToCollectionSuccess": "{count, plural, one {1 recipe} other {{count} recipes}} added to \"{name}\"",
"addToCollectionFailed": "Failed to add to collection",
"createCollectionFailed": "Failed to create collection",
"noCollectionsYet": "You don't have any collections yet.",
"versionHistoryTitle": "Version History",
"versionsLoading": "Loading versions...",
"versionsEmpty": "No versions yet. Versions are saved automatically when you edit this recipe.",
"versionCompare": "Compare",
"versionCompareWith": "Compare v{version} with current",
"versionRestore": "Restore",
"versionRestored": "Recipe restored to version {version}",
"versionDetailLoading": "Loading...",
"versionDetailSummary": "{ingredients, plural, one {1 ingredient} other {{ingredients} ingredients}}, {steps, plural, one {1 step} other {{steps} steps}}",
"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",
"bulkDeleteConfirmTitle": "{count, plural, one {Delete 1 recipe?} other {Delete {count} recipes?}}",
"bulkDeleteConfirmDescription": "This action cannot be undone.",
"deselectAll": "Deselect all",
"selectAll": "Select all",
"clickToSelect": "Click cards to select",
"selectedCount": "{count} selected",
"select": "Select",
"visibilityMenuLabel": "Visibility",
"makePublic": "Make public",
"makeUnlisted": "Make unlisted",
"makePrivate": "Make private",
"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…",
"photoAnalysisNote": "AI will analyze this dish and infer ingredients, quantities, and cooking steps."
},
"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",
"directions": "Directions",
"optional": "optional",
"stepLabel": "Step {n}"
},
"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",
"subtitle": "Manage your account, preferences, and integrations.",
"profile": "Profile",
"language": "Language",
"units": "Units",
"metric": "Metric",
"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.",
"docsLink": "View API documentation"
},
"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.",
"viewDiaryCta": "View nutrition diary",
"caloriesLabel": "Calories (kcal/day)",
"proteinLabel": "Protein (g/day)",
"carbsLabel": "Carbs (g/day)",
"fatLabel": "Fat (g/day)",
"caloriesPlaceholder": "e.g. 2000",
"proteinPlaceholder": "e.g. 50",
"carbsPlaceholder": "e.g. 250",
"fatPlaceholder": "e.g. 70",
"saveButton": "Save goals",
"saving": "Saving...",
"saveSuccess": "Nutrition goals saved",
"saveError": "Failed to save nutrition goals"
},
"webhooksPage": {
"title": "Webhooks",
"description": "Receive HTTP callbacks when events happen in your Epicure account."
}
},
"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…",
"send": "Send message"
},
"servingScaler": {
"servings": "Servings",
"reset": "Reset",
"aiScale": "AI Scale",
"scaling": "Scaling…",
"aiScaledNote": "AI-scaled quantities shown below",
"dismissAiScaling": "Dismiss AI scaling"
},
"people": {
"title": "Find People",
"subtitle": "Search for cooks to follow by name or username.",
"searchPlaceholder": "Search people by name or username…",
"searching": "Searching…",
"noneFound": "No one found."
},
"messages": {
"loading": "Loading…",
"noMessagesYet": "No messages yet. Say hi!",
"sendFailed": "Failed to send",
"placeholder": "Type a message…",
"noConversationsYet": "No conversations yet. Visit a profile to say hi.",
"unknownUser": "Unknown",
"noMessagesPreview": "No messages yet",
"selectConversation": "Select a conversation",
"messageButton": "Message",
"startConversationFailed": "Failed to start conversation",
"title": "Messages",
"loadOlder": "Load older messages",
"loadingOlder": "Loading…",
"send": "Send message"
},
"nutritionPanel": {
"estimateButton": "Estimate nutrition",
"reEstimateButton": "Re-estimate",
"estimating": "Estimating…",
"estimatingNutrition": "Estimating nutrition…",
"title": "Nutrition per serving",
"estimateFailed": "Failed to estimate nutrition",
"error": "Something went wrong",
"protein": "Protein",
"carbs": "Carbs",
"fat": "Fat",
"fiber": "Fiber",
"sodium": "Sodium"
},
"nutritionDiary": {
"title": "Nutrition Diary",
"subtitle": "Your daily nutrition intake, computed from what you've cooked.",
"dateLabel": "Date",
"today": "Today",
"loading": "Loading…",
"loadError": "Couldn't load your nutrition diary. Please try again.",
"goalNotSet": "You haven't set daily nutrition goals yet.",
"setGoalsCta": "Set goals",
"calories": "Calories",
"protein": "Protein",
"carbs": "Carbs",
"fat": "Fat",
"fiber": "Fiber",
"sodium": "Sodium",
"entriesTitle": "Recipes cooked",
"noEntries": "Nothing logged for this day. Mark a recipe as \"cooked\" to see it here.",
"servingsLabel": "{count, plural, one {1 serving} other {{count} servings}}",
"nutritionUnknownBadge": "Nutrition unknown",
"unknownNote": "{count, plural, one {1 entry has no nutrition data and isn't included in the totals above.} other {{count} entries have no nutrition data and aren't included in the totals above.}}"
},
"explore": {
"title": "Explore",
"tabRecipes": "Recipes",
"tabPeople": "People",
"searchPlaceholder": "Search public recipes…",
"maxMinutes": "Max minutes",
"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…",
"error": "Something went wrong",
"save": "Save",
"saved": "Saved",
"saveFailed": "Failed to save",
"exportMarkdown": "Export as Markdown",
"copyMarkdown": "Copy as Markdown",
"downloadMarkdown": "Download as Markdown",
"copiedToClipboard": "Copied to clipboard",
"copyFailed": "Failed to copy to clipboard",
"print": "Print",
"share": "Share",
"deleteFailed": "Delete failed",
"updateFailed": "Update failed",
"deleted": "Deleted",
"cancel": "Cancel",
"delete": "Delete",
"confirm": "Confirm",
"back": "Back",
"difficulty": "Difficulty",
"anyDifficulty": "Any",
"editManually": "Edit manually",
"surpriseMe": "Surprise me",
"generatingContent": "Generating recipe content…",
"decrease": "Decrease",
"increase": "Increase"
},
"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"
},
"batchCooking": {
"title": "Batch cooking",
"subtitle": "Cook once, eat well all week",
"empty": "No batch-cooking sessions yet",
"newSession": "New batch-cooking session",
"dishCount": "{count, plural, one {# dish} other {# dishes}}",
"wizardTitle": "Plan a batch-cooking session",
"wizardDescription": "Choose how many meals to cover — we'll design a shared shopping list and one prep session for all of them.",
"dinners": "Dinners",
"lunches": "Lunches",
"servingsPerMeal": "Servings per meal",
"dietaryPrefs": "Dietary preferences",
"dietaryPrefsPlaceholder": "e.g. vegetarian, no shellfish",
"anyDifficulty": "Any",
"generate": "Generate",
"generating": "Generating… this can take a minute",
"generateSuccess": "Batch-cooking session created",
"generateFailed": "Failed to generate"
},
"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",
"batchCooking": "Batch cooking",
"search": "Search recipes…",
"noRecipes": "No recipes yet",
"noMatch": "No recipes match",
"clearSearch": "Clear search",
"createFirst": "Create your first recipe",
"resultSingular": "{count} recipe",
"resultPlural": "{count} recipes",
"for": "for",
"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",
"batchCookLabel": "Batch cooking",
"batchCookOnly": "Batch cooking only",
"batchCookExclude": "Hide batch cooking",
"clearFilters": "Clear filters",
"tabMine": "My Recipes",
"tabFavorites": "Favorites",
"favoritesCount": "{count} favorited recipe",
"favoritesCountPlural": "{count} favorited recipes",
"favoritesEmpty": "No favorited recipes yet — tap the heart on any recipe to save it here.",
"favoritesEmptyTitle": "No favorites yet",
"exploreRecipes": "Explore recipes",
"emptyStateDescription": "Create your own, import from a URL, or let AI generate one to get started."
},
"recipeNotes": {
"title": "Your private notes",
"visibilityHint": "Only visible to you — never shared with the recipe's author or anyone else.",
"placeholder": "e.g. I used less salt than the recipe calls for, great with rice instead of pasta…",
"save": "Save",
"saving": "Saving…",
"saved": "Note saved",
"saveFailed": "Failed to save note"
},
"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",
"ingredientsRequired": "Add at least one ingredient",
"stepsRequired": "Add at least one step",
"discardChangesTitle": "Discard changes?",
"discardChangesDescription": "You have unsaved changes. If you leave now, they will be lost.",
"keepEditing": "Keep editing",
"discardChanges": "Discard changes"
},
"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",
"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",
"batchCookBadge": "Batch cooking",
"batchCooking": "Batch cooking",
"pickDishTitle": "Which dish from \"{recipe}\"?",
"pickDishDescription": "This is a batch-cooking session — pick the specific dish for this slot.",
"back": "Back",
"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",
"searchItemsPlaceholder": "Search items…",
"sortByCategory": "By category",
"sortByAlpha": "Alphabetical",
"sortByUnchecked": "Unchecked first",
"noSearchResults": "No items match your search.",
"autoCategorize": "Auto-categorize",
"autoCategorizeSuccess": "{count, plural, one {1 item categorized} other {{count} items categorized}}",
"autoCategorizeNoneFound": "Couldn't guess a category for the remaining items",
"removeItemConfirmTitle": "Remove item?",
"removeItemConfirmDescription": "Remove \"{name}\" from this list?",
"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.",
"targetNutritionGoals": "Target my nutrition goals",
"targetNutritionGoalsDescription": "Nudge the AI to aim for your daily calorie and macro targets across the day's meals.",
"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",
"generateFromThisWeek": "Add to shopping list",
"generateFromThisWeekHint": "Merges every recipe's ingredients from this week, summing matching ones and flagging what's already in your pantry.",
"shoppingListWeekName": "Groceries for {week}",
"listCreateFailed": "Failed to create",
"listCreating": "Creating…",
"listCreate": "Create",
"pickRecipe": "Pick recipe",
"addEntry": "+ Add",
"removeEntry": "Remove meal",
"markCooked": "Mark as cooked",
"markCookedSuccess": "Marked as cooked",
"markCookedFailed": "Failed to mark as cooked"
},
"pantry": {
"title": "Pantry",
"subtitle": "Track what you have on hand",
"canCook": "What can I cook?",
"expiringSoonSuggestionsTitle": "Use it up soon",
"seeAll": "See all",
"expiringLeftoversTitle": "Leftovers expiring soon",
"expiringToday": "Eat today",
"expiringInDays": "{days}d left",
"itemNamePlaceholder": "Item name",
"qtyPlaceholder": "Qty",
"unitPlaceholder": "Unit",
"add": "Add",
"addFailed": "Failed to add",
"removeFailed": "Failed to remove",
"removeConfirmTitle": "Remove item?",
"removeConfirmDescription": "Remove \"{name}\" from your pantry?",
"empty": "No pantry items yet.",
"emptyDescription": "Add items by hand, or scan a barcode / photo to fill it in fast.",
"expired": "Expired",
"expiresInDays": "Expires in {days}d",
"expiresOn": "Expires {date}",
"printItemCountSingular": "{count} item",
"printItemCountPlural": "{count} items",
"noItems": "No items in pantry.",
"colItem": "Item",
"colQuantity": "Quantity",
"colExpires": "Expires",
"expiredDaysAgo": "Expired {days}d ago",
"scan": "Scan",
"scanTitle": "Add by scan",
"scanDescription": "Scan a barcode or take a photo to quickly add pantry items.",
"scanBarcodeOption": "Scan barcode",
"scanPhotoOption": "Take/upload photo",
"scanBarcodeTitle": "Scan barcode",
"scanBarcodeDescription": "Point your camera at a product barcode.",
"scanConfirmTitle": "Confirm item",
"scanConfirmDescription": "Review and edit before adding to your pantry.",
"scanAnalyzingTitle": "Analyzing photo",
"scanAnalyzingDescription": "Identifying items in your photo…",
"scanBack": "Back",
"scanAddToPantry": "Add to pantry",
"scanAdded": "Added to pantry",
"scanLookupFailed": "Barcode lookup failed",
"scanNotFound": "Product not found. Try a photo scan instead.",
"scanPhotoFailed": "Photo scan failed",
"scanNoItemsFound": "No items recognized in this photo",
"cameraError": "Couldn't access the camera"
},
"feed": {
"title": "Feed",
"followEmpty": "Follow chefs to see their recipes here.",
"followEmptyDescription": "Your following feed fills up as soon as you follow a few people.",
"findPeople": "Find people to follow",
"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…",
"loadMore": "Load more",
"loadFailed": "Failed to load recipes. Check your connection and try again.",
"retry": "Retry"
},
"shoppingLists": {
"title": "Shopping Lists",
"subtitle": "Plan your grocery runs",
"empty": "No shopping lists yet",
"emptyDescription": "Generate one from a meal plan, or start one from scratch.",
"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",
"alreadyInPantry": "Already in pantry",
"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.",
"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",
"actionsMenuLabel": "List actions",
"rename": "Rename",
"renameListTitle": "Rename list",
"renameListLabel": "List name",
"renaming": "Saving…",
"listRenamed": "List renamed",
"listRenameFailed": "Failed to rename list",
"deleteListConfirmTitle": "Delete list?",
"deleteListConfirmDescription": "This action cannot be undone. The list and all its items will be permanently deleted.",
"listDeleted": "List deleted",
"listDeleteFailed": "Failed to delete list",
"changeCategory": "Change category",
"deleteItem": "Delete item",
"aisleOther": "Other",
"newCategoryPlaceholder": "New category…",
"addCategory": "Add",
"categories": {
"produce": "Produce",
"dairyEggs": "Dairy & Eggs",
"meatSeafood": "Meat & Seafood",
"bakery": "Bakery",
"frozen": "Frozen",
"pantry": "Pantry",
"spicesCondiments": "Spices & Condiments",
"beverages": "Beverages"
}
},
"collections": {
"title": "Collections",
"subtitle": "Curate your favorite recipes into collections",
"empty": "No collections yet",
"emptyDescription": "Group your recipes by theme, occasion, or however makes sense to you.",
"emptyCollection": "No recipes in this collection yet.",
"public": "Public",
"recipeCount": "{count} recipe",
"recipeCountPlural": "{count} recipes",
"discoverTitle": "Discover",
"discoverSubtitle": "Trending and recently shared public collections",
"myCollectionsLink": "My collections",
"trendingThisWeek": "Trending this week",
"recentlyAdded": "Recently added",
"noTrending": "No trending collections yet",
"noRecent": "No public collections yet",
"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",
"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…",
"commentsTitle": "Comments",
"noCommentsYet": "No comments yet. Be the first!",
"deleteCommentTitle": "Delete comment?",
"deleteCommentDescription": "This action cannot be undone.",
"blockConfirmTitle": "Block @{username}?",
"blockConfirmDescription": "They won't be able to follow you or comment on your recipes.",
"blockConfirmAction": "Block",
"loadMoreComments": "Load more comments",
"loadingMoreComments": "Loading…",
"postButton": "Post",
"postingButton": "Posting…",
"replyButton": "Reply",
"reportButton": "Report",
"ratingFailed": "Failed to rate",
"cookedItPrompt": "Cooked it? Rate it and share a photo",
"reviewTextPlaceholder": "How did it turn out?",
"reviewPhotoAdd": "Add photo",
"reviewPhotoUploading": "Uploading…",
"reviewPhotoFailed": "Failed to upload photo",
"reviewSubmit": "Post review",
"reviewSubmitting": "Posting…",
"reviewsTitle": "{count, plural, one {# review} other {# reviews}}",
"justNow": "just now",
"minutesAgo": "{mins}m ago",
"hoursAgo": "{hours}h ago",
"daysAgo": "{days}d ago",
"signInToReact": "Sign in to react to comments",
"reactionFailed": "Failed to update reaction",
"addReaction": "Add {type} reaction",
"removeReaction": "Remove {type} reaction",
"favoriteAdd": "Save recipe",
"favoriteRemove": "Unsave recipe"
},
"cookingMode": {
"cooking": "Cooking",
"stepOf": "Step {current} of {total}",
"batchPrepLabel": "Prep",
"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.",
"downloadData": "Download my data",
"downloadDataDescription": "Get a JSON export of all the personal data Epicure holds about you — your profile, recipes, activity, and settings.",
"downloadDataButton": "Download my data",
"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",
"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",
"byokSaved": "{provider} key saved",
"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",
"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…",
"privateAccount": "Private account",
"privateAccountDescription": "When on, your profile and recipes won't appear in search or discovery (Explore, Trending, For You). People who already follow you keep seeing your recipes."
},
"profilePage": {
"tabRecipes": "Recipes",
"tabHistory": "Cooking History",
"tabPhotos": "Photos",
"statTotalCooked": "Total times cooked",
"statStreak": "Current streak",
"statLastCooked": "Last cooked",
"streakDays": "{count, plural, one {# day} other {# days}}",
"noneYet": "None yet",
"recentActivity": "Recent activity",
"noHistoryYet": "No cooking history yet.",
"noPhotosYet": "No photos yet.",
"previous": "Previous",
"next": "Next",
"pageOf": "Page {page} of {total}"
}
}