fix(i18n): translate remaining recipe card/detail strings, localize AI responses

Recipe cards, the recipe detail page, and meal planner still rendered raw
"{n} servings"/"{n}m cook"/"{n} srv" text and untranslated difficulty labels
outside the earlier i18n pass. Also wires the user's locale into AI features
that previously always answered in English regardless of app language:
recipe chat, ingredient substitution, recipe ideas, and generate-from-idea.
The recipe-language picker in the AI generate dialog now defaults to the
user's locale instead of always defaulting to English.
This commit is contained in:
Arnaud
2026-07-02 08:25:51 +02:00
parent 01fdbb880b
commit 2154512e54
15 changed files with 64 additions and 24 deletions
@@ -43,8 +43,12 @@ type RecipeIdea = {
};
function RecipeCard({ recipe }: { recipe: ExploreRecipeResult | SearchRecipeResult }) {
const t = useTranslations("recipe");
const totalMins = (recipe.prepMins ?? 0) + (recipe.cookMins ?? 0);
const description = "description" in recipe ? recipe.description : null;
const difficultyLabel = recipe.difficulty === "easy" || recipe.difficulty === "medium" || recipe.difficulty === "hard"
? t(`difficulty.${recipe.difficulty}`)
: recipe.difficulty;
return (
<Link
href={`/recipes/${recipe.id}`}
@@ -59,7 +63,7 @@ function RecipeCard({ recipe }: { recipe: ExploreRecipeResult | SearchRecipeResu
variant="secondary"
className={`shrink-0 capitalize text-xs ${DIFFICULTY_COLORS[recipe.difficulty] ?? ""}`}
>
{recipe.difficulty}
{difficultyLabel}
</Badge>
)}
</div>
@@ -70,7 +74,7 @@ function RecipeCard({ recipe }: { recipe: ExploreRecipeResult | SearchRecipeResu
{totalMins > 0 && (
<span className="flex items-center gap-1">
<Clock className="h-3.5 w-3.5" />
{totalMins}m
{t("total", { mins: totalMins })}
</span>
)}
{recipe.authorName && (
@@ -103,6 +107,7 @@ export function ExplorePageContent({ trending, recent, initialQuery }: Props) {
const searchParams = useSearchParams();
const t = useTranslations("explore");
const tCommon = useTranslations("common");
const tRecipe = useTranslations("recipe");
const [inputValue, setInputValue] = useState(initialQuery);
const [query, setQuery] = useState(initialQuery);
@@ -388,7 +393,7 @@ export function ExplorePageContent({ trending, recent, initialQuery }: Props) {
variant="secondary"
className={`shrink-0 capitalize text-xs ${DIFFICULTY_COLORS[idea.difficulty] ?? ""}`}
>
{idea.difficulty}
{tRecipe(`difficulty.${idea.difficulty}`)}
</Badge>
</div>
<p className="text-xs text-muted-foreground line-clamp-2 flex-1">{idea.description}</p>
@@ -398,7 +403,7 @@ export function ExplorePageContent({ trending, recent, initialQuery }: Props) {
))}
{idea.totalMins && (
<Badge variant="outline" className="text-xs flex items-center gap-1">
<Clock className="h-3 w-3" />{idea.totalMins}m
<Clock className="h-3 w-3" />{tRecipe("total", { mins: idea.totalMins })}
</Badge>
)}
</div>
@@ -2,6 +2,7 @@
import { useState, useEffect, useRef, useCallback } from "react";
import { useRouter, useSearchParams } from "next/navigation";
import { useTranslations } from "next-intl";
import Link from "next/link";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
@@ -39,7 +40,11 @@ const DIFFICULTY_COLORS: Record<string, string> = {
};
function RecipeCard({ recipe }: { recipe: RecipeResult }) {
const t = useTranslations("recipe");
const totalMins = (recipe.prepMins ?? 0) + (recipe.cookMins ?? 0);
const difficultyLabel = recipe.difficulty === "easy" || recipe.difficulty === "medium" || recipe.difficulty === "hard"
? t(`difficulty.${recipe.difficulty}`)
: recipe.difficulty;
return (
<Link
href={`/recipes/${recipe.id}`}
@@ -54,7 +59,7 @@ function RecipeCard({ recipe }: { recipe: RecipeResult }) {
variant="secondary"
className={`shrink-0 capitalize text-xs ${DIFFICULTY_COLORS[recipe.difficulty] ?? ""}`}
>
{recipe.difficulty}
{difficultyLabel}
</Badge>
)}
</div>
@@ -65,7 +70,7 @@ function RecipeCard({ recipe }: { recipe: RecipeResult }) {
{totalMins > 0 && (
<span className="flex items-center gap-1">
<Clock className="h-3.5 w-3.5" />
{totalMins}m
{t("total", { mins: totalMins })}
</span>
)}
{recipe.authorName && (