From e7657d0153965b1e6034e26cc9ed83f00e75ad96 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 21 Aug 2026 12:59:56 +0000 Subject: [PATCH] =?UTF-8?q?feat:=20d=C3=B6lj=20SEK-receptkostnad=20f=C3=B6?= =?UTF-8?q?r=20icke-SEK-anv=C3=A4ndare=20+=20i18n:remove-language?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Recept-kostnaden ligger i SEK (basvaluta, D-028). För en spansk användare (currency != SEK) nollas estimatedCostMinorPerPortion i API:t (list + detalj) -> kostnads-taggen försvinner helt i stället för att visa en svensk SEK-siffra. (Beslut: hellre ingen uppskattning än fel valuta, tills riktpriser via kvitton/M8.) - i18n:remove-language – motsvarighet till add-language, tar bort ett språk rent. Verifierat: add el -> remove el -> byte-rent, vakt + typecheck gröna. Co-Authored-By: Claude --- apps/api/src/routes/recipes.ts | 18 +++++-- apps/mobile/package.json | 3 +- apps/mobile/scripts/i18n-remove-language.mjs | 55 ++++++++++++++++++++ 3 files changed, 72 insertions(+), 4 deletions(-) create mode 100644 apps/mobile/scripts/i18n-remove-language.mjs diff --git a/apps/api/src/routes/recipes.ts b/apps/api/src/routes/recipes.ts index 9ae07fb..30945ab 100644 --- a/apps/api/src/routes/recipes.ts +++ b/apps/api/src/routes/recipes.ts @@ -125,8 +125,12 @@ export async function recipeRoutes(app: FastifyInstance) { .offset(q.offset); // Titlar på användarens språk där publicerad översättning finns (i18n M3). - const languageTag = await userLanguageTag(app.db, req.userId); + const listPrefs = await loadLocalePreferences(app.db, req.userId); + const languageTag = listPrefs.languageTag; const candidates = languageCandidates(languageTag); + // Recept-kostnaden ligger i SEK (basvaluta, D-028). Visa den bara för SEK- + // användare – annars ingen siffra alls (hellre inget än fel valuta). + const listShowCost = listPrefs.currencyCode === "SEK"; let titleMap = new Map(); if (!candidates.includes("sv") && rows.length > 0) { const translations = await app.db @@ -153,7 +157,11 @@ export async function recipeRoutes(app: FastifyInstance) { } return { - recipes: rows.map((r) => ({ ...r, title: titleMap.get(r.id) ?? r.titleSv })), + recipes: rows.map((r) => ({ + ...r, + title: titleMap.get(r.id) ?? r.titleSv, + estimatedCostMinorPerPortion: listShowCost ? r.estimatedCostMinorPerPortion : null, + })), language: candidates.includes("sv") ? "sv" : languageTag, }; }); @@ -237,7 +245,10 @@ export async function recipeRoutes(app: FastifyInstance) { // Täckning mot hushållets lager: vad har du hemma, vad saknas (→ inköpslista). const coverage = await recipeCoverage(app, householdId, recipe.ingredients); - const languageTag = await userLanguageTag(app.db, req.userId); + const detailPrefs = await loadLocalePreferences(app.db, req.userId); + const languageTag = detailPrefs.languageTag; + // Recept-kostnaden är i SEK (D-028) – dölj den för icke-SEK-användare. + const detailShowCost = detailPrefs.currencyCode === "SEK"; const translation = await resolveRecipeTranslation(app.db, id, languageTag); const ingredientNames = await resolveIngredientNames( app.db, @@ -247,6 +258,7 @@ export async function recipeRoutes(app: FastifyInstance) { return { ...recipe, + estimatedCostMinorPerPortion: detailShowCost ? recipe.estimatedCostMinorPerPortion : null, language: translation?.language ?? "sv", title: translation?.title ?? recipe.titleSv, description: translation ? translation.description : recipe.descriptionSv, diff --git a/apps/mobile/package.json b/apps/mobile/package.json index f3fc01d..c515399 100644 --- a/apps/mobile/package.json +++ b/apps/mobile/package.json @@ -10,7 +10,8 @@ "typecheck": "tsc --noEmit", "test": "vitest run --passWithNoTests", "i18n:check": "node scripts/i18n-check.mjs", - "i18n:add-language": "node scripts/i18n-add-language.mjs" + "i18n:add-language": "node scripts/i18n-add-language.mjs", + "i18n:remove-language": "node scripts/i18n-remove-language.mjs" }, "dependencies": { "@app/analytics": "workspace:*", diff --git a/apps/mobile/scripts/i18n-remove-language.mjs b/apps/mobile/scripts/i18n-remove-language.mjs new file mode 100644 index 0000000..5d090c8 --- /dev/null +++ b/apps/mobile/scripts/i18n-remove-language.mjs @@ -0,0 +1,55 @@ +#!/usr/bin/env node +/** + * i18n:remove-language – ta bort ett språk rent (motsatsen till add-language). + * + * node scripts/i18n-remove-language.mjs + * ex: node scripts/i18n-remove-language.mjs el + * + * Tar bort: locale-mappen, profile.language.-raden i alla filer, och de tre + * raderna i i18n.ts (import + SUPPORTED_LANGUAGES + resources). Endast radborttag + * → minimala diffar. Kör i18n:check + typecheck efteråt för att bekräfta. + */ +import { rmSync, readFileSync, writeFileSync, existsSync, readdirSync, statSync } from "node:fs"; +import { join } from "node:path"; + +const ROOT = process.cwd(); // apps/mobile +const LOC = join(ROOT, "src/locales"); +const I18N = join(ROOT, "src/lib/i18n.ts"); + +const code = process.argv[2]; +if (!code || !/^[a-z]{2,3}$/.test(code)) { + console.error("Användning: node scripts/i18n-remove-language.mjs (t.ex. el)"); + process.exit(1); +} +if (code === "sv") { + console.error("Kan inte ta bort sv – svenska är källspråket."); + process.exit(1); +} +if (!existsSync(join(LOC, code))) { + console.error(`src/locales/${code} finns inte – inget att ta bort.`); + process.exit(1); +} + +// 1. locale-mappen +rmSync(join(LOC, code), { recursive: true, force: true }); + +// 2. profile.language.-raden ur alla kvarvarande locale-filer +const labelNeedle = `"profile.language.${code}":`; +for (const lang of readdirSync(LOC).filter((d) => statSync(join(LOC, d)).isDirectory())) { + const p = join(LOC, lang, "common.json"); + const lines = readFileSync(p, "utf8").split("\n"); + const kept = lines.filter((l) => !l.includes(labelNeedle)); + if (kept.length !== lines.length) writeFileSync(p, kept.join("\n")); +} + +// 3. de tre raderna i i18n.ts +const importNeedle = `import ${code} from "../locales/${code}/common.json";`; +const supportedNeedle = `code: "${code}",`; +const resourceRe = new RegExp(`^\\s+${code}: \\{ common: ${code} \\},$`); +const kept = readFileSync(I18N, "utf8") + .split("\n") + .filter((l) => l.trim() !== importNeedle && !l.includes(supportedNeedle) && !resourceRe.test(l)); +writeFileSync(I18N, kept.join("\n")); + +console.log(`✓ Tog bort språket "${code}" (locale-mapp, etikett i alla filer, i18n.ts-rader).`); +console.log(`Kör: pnpm --filter @app/mobile i18n:check && pnpm --filter @app/mobile typecheck`);