i18n(hemskärm): översätt rekommendationstitlar + ingrediensnamn + hårdkodade strängar

- what-to-eat översätter nu recepttitlar (resolveRecipeTitles, batch) + /saknas-
  ingrediensnamn (via allIngredients-id-karta + resolveIngredientNames)
- index.tsx använder rec.title/item.name; hårdkodat -> t(): sökfält, Rekommenderat,
  Toppval, Menade du, Baka (kategori), tomma-lägen
- 7 nya nycklar (wte.searchPlaceholder/recommended/topPick/didYouMean/emptySearch/
  emptyHoliday, cat.baking) i alla 12 språk
- KVAR (separat): whySv-prosan (kräver strukturerade skäl), säsongsval (region), meal-box-titlar

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Claude
2026-08-21 00:36:35 +00:00
parent 4e86b1b724
commit 28c4d0db71
15 changed files with 154 additions and 10 deletions
+11 -10
View File
@@ -29,10 +29,11 @@ import { colors, spacing } from "@/lib/theme";
interface Recommendation {
recipeId: string;
titleSv: string;
title?: string;
score: number;
whySv: string;
missingIngredients: string[];
usesExpiring: Array<{ nameSv: string; daysLeft: number | null }>;
usesExpiring: Array<{ nameSv: string; name?: string; daysLeft: number | null }>;
coveragePercent: number;
ratingAverage: number | null;
ratingCount: number;
@@ -73,7 +74,7 @@ const CATS: ReadonlyArray<{
{ key: "dinner", labelKey: "myday.mealType.dinner", mealType: "dinner", glyph: "🍽️" },
{ key: "snack", labelKey: "myday.mealType.snack", mealType: "snack", glyph: "🍎" },
{ key: "dessert", labelKey: "myday.mealType.dessert", mealType: "dessert", glyph: "🍰" },
{ key: "baking", labelKey: null, label: "Baka", tag: "baking", ctxMeal: "dessert", glyph: "🥐" },
{ key: "baking", labelKey: "cat.baking", label: "Baka", tag: "baking", ctxMeal: "dessert", glyph: "🥐" },
];
const errText = (e: unknown): string | undefined =>
@@ -161,7 +162,7 @@ export default function WhatToEatScreen() {
{/* Sök bland recept (fungerar i alla flikar, söker på namn). */}
<Input
placeholder="Sök recept, t.ex. pannkaka…"
placeholder={t("wte.searchPlaceholder")}
value={searchText}
onChangeText={setSearchText}
onSubmitEditing={() => submitSearch(searchText)}
@@ -187,7 +188,7 @@ export default function WhatToEatScreen() {
{/* Kategori-flikar med enhetliga rutor (2 kolumner). Filtrerar in-place. */}
<Row style={{ flexWrap: "wrap", justifyContent: "space-between" }}>
<Button
label="🍽️ Rekommenderat"
label={`🍽️ ${t("wte.recommended")}`}
variant={cat === null ? "secondary" : "ghost"}
onPress={() => selectCat(null)}
style={{ width: "48%", marginBottom: spacing.xs }}
@@ -237,9 +238,9 @@ export default function WhatToEatScreen() {
<EmptyState
text={
submittedSearch
? "Inga recept matchar din sökning."
? t("wte.emptySearch")
: holiday
? "Inga recept för den här högtiden än."
? t("wte.emptyHoliday")
: t("wte.empty")
}
/>
@@ -248,16 +249,16 @@ export default function WhatToEatScreen() {
{/* "Menade du …?" stavningsförslag när sök gav noll träffar. */}
{allRecs.length === 0 && submittedSearch && query.data.context.searchSuggestion && (
<Pressable onPress={() => submitSearch(query.data!.context.searchSuggestion!)}>
<Tag label={`Menade du ${query.data.context.searchSuggestion}?`} tone="accent" />
<Tag label={t("wte.didYouMean", { suggestion: query.data.context.searchSuggestion })} tone="accent" />
</Pressable>
)}
{shownRecs.map((rec, index) => (
<Card key={rec.recipeId} onPress={() => router.push(`/recipe/${rec.recipeId}`)}>
<Row style={{ justifyContent: "space-between", alignItems: "center" }}>
<Heading>{rec.titleSv}</Heading>
<Heading>{rec.title ?? rec.titleSv}</Heading>
{page === 0 && index === 0 && !submittedSearch && !holiday && (
<Tag label="Toppval" tone="accent" />
<Tag label={t("wte.topPick")} tone="accent" />
)}
</Row>
<Row>
@@ -272,7 +273,7 @@ export default function WhatToEatScreen() {
tone={rec.coveragePercent >= 80 ? "success" : "neutral"}
/>
{rec.usesExpiring.slice(0, 2).map((item) => (
<Tag key={item.nameSv} label={`${item.nameSv}`} tone="warning" />
<Tag key={item.nameSv} label={`${item.name ?? item.nameSv}`} tone="warning" />
))}
</Row>
<View