feat(i18n): translate hardcoded strings across print, public recipe, and recipe management UI

Wires dozens of components and server pages to next-intl instead of hardcoded
English text, and adds a lightweight getMessages/formatMessage helper for
server components (print pages, public recipe page, cook metadata) since
next-intl/server isn't wired into this app's routing. Backfills missing
en/fr message keys that existing code already referenced but fr.json lacked.
This commit is contained in:
Arnaud
2026-07-02 07:58:18 +02:00
parent afff6cf9eb
commit 01fdbb880b
32 changed files with 515 additions and 187 deletions
+96 -3
View File
@@ -44,6 +44,10 @@
"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}}",
@@ -136,6 +140,60 @@
"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",
@@ -220,7 +278,8 @@
"createFirst": "Create your first recipe",
"resultSingular": "result",
"resultPlural": "results",
"for": "for"
"for": "for",
"filterByTag": "Filter by tag…"
},
"recipeForm": {
"titleLabel": "Title *",
@@ -244,6 +303,9 @@
"ingredient": "Ingredient",
"note": "Note",
"addIngredient": "Add ingredient",
"setCover": "Set as cover",
"removePhoto": "Remove",
"expand": "Expand",
"steps": "Steps",
"stepPlaceholder": "Step {n}…",
"timerSeconds": "Timer (s)",
@@ -277,6 +339,11 @@
"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",
@@ -326,7 +393,21 @@
"empty": "No shopping lists yet",
"listEmpty": "Empty",
"generated": "Generated",
"items": "{checked}/{total} items"
"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",
@@ -345,7 +426,9 @@
"collectionCreated": "Collection created",
"collectionForked": "Collection forked to your library",
"inviteSent": "Invitation sent",
"memberRemoved": "Member removed"
"memberRemoved": "Member removed",
"replyPlaceholder": "Reply…",
"commentPlaceholder": "Share your thoughts…"
},
"cookingMode": {
"cooking": "Cooking",
@@ -363,6 +446,8 @@
"enableVoice": "Enable voice control",
"stopVoice": "Stop voice",
"toggleIngredients": "Toggle ingredients",
"pageTitle": "Cooking: {title}",
"pageTitleFallback": "Cooking Mode",
"shortcutsTitle": "Shortcuts & commands",
"keyboardSection": "Keyboard",
"voiceSection": "Voice commands",
@@ -423,8 +508,16 @@
"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",