import type { ProvenanceEntry } from "./types.js"; /** * Mall-baserade provenienssträngar för personalisering (S1). * Ingen fri AI-text till användaren — alla strängar byggs från grundade fakta. * R7/stödjande ton: positiv framing, aldrig restriktiv eller skam. */ export type LanguageTag = "sv" | "en" | "es" | "it" | "de" | "fr" | "da" | "nb" | "fi" | "nl" | "pl" | "pt"; const TEMPLATES: Record> = { favoriteCuisine: { sv: "För att du har berättat att du gillar {{cuisine}} mat.", en: "Because you said you like {{cuisine}} food.", es: "Porque dijiste que te gusta la comida {{cuisine}}.", it: "Perché hai detto che ti piace la cucina {{cuisine}}.", de: "Weil du gesagt hast, dass du {{cuisine}} Essen magst.", fr: "Parce que vous avez dit que vous aimiez la cuisine {{cuisine}}.", da: "Fordi du har sagt, at du kan lide {{cuisine}} mad.", nb: "Fordi du har sagt at du liker {{cuisine}} mat.", fi: "Koska kerroit pitäväsi {{cuisine}} ruoasta.", nl: "Omdat je hebt gezegd dat je {{cuisine}} eten lekker vindt.", pl: "Ponieważ powiedziałeś, że lubisz kuchnię {{cuisine}}.", pt: "Porque disseste que gostas de comida {{cuisine}}.", }, cookedOften: { sv: "För att du lagat {{recipe}} {{count}} gånger den senaste månaden.", en: "Because you've cooked {{recipe}} {{count}} times in the past month.", es: "Porque has cocinado {{recipe}} {{count}} veces el último mes.", it: "Perché hai cucinato {{recipe}} {{count}} volte nell'ultimo mese.", de: "Weil du {{recipe}} {{count}} Mal im letzten Monat gekocht hast.", fr: "Parce que vous avez cuisiné {{recipe}} {{count}} fois le mois dernier.", da: "Fordi du har lavet {{recipe}} {{count}} gange den seneste måned.", nb: "Fordi du har laget {{recipe}} {{count}} ganger den siste måneden.", fi: "Koska olet kokannut {{recipe}} {{count}} kertaa viimeisen kuukauden aikana.", nl: "Omdat je {{recipe}} {{count}} keer hebt gekookt afgelopen maand.", pl: "Ponieważ gotowałeś {{recipe}} {{count}} razy w ostatnim miesiącu.", pt: "Porque cozinhaste {{recipe}} {{count}} vezes no último mês.", }, expiringIngredient: { sv: "För att ni har {{count}} {{ingredient}} som bör användas inom {{days}} dagar.", en: "Because you have {{count}} {{ingredient}} to use within {{days}} days.", es: "Porque tenéis {{count}} {{ingredient}} para usar en {{days}} días.", it: "Perché avete {{count}} {{ingredient}} da usare entro {{days}} giorni.", de: "Weil ihr {{count}} {{ingredient}} habt, die binnen {{days}} Tagen verwendet werden sollten.", fr: "Parce que vous avez {{count}} {{ingredient}} à utiliser dans {{days}} jours.", da: "Fordi I har {{count}} {{ingredient}}, der bør bruges inden for {{days}} dage.", nb: "Fordi dere har {{count}} {{ingredient}} som bør brukes innen {{days}} dager.", fi: "Koska sinulla on {{count}} {{ingredient}}, jotka tulisi käyttää {{days}} päivän kuluessa.", nl: "Omdat jullie {{count}} {{ingredient}} hebben die binnen {{days}} dagen gebruikt moeten worden.", pl: "Ponieważ macie {{count}} {{ingredient}}, które należy zużyć w ciągu {{days}} dni.", pt: "Porque tens {{count}} {{ingredient}} para usar em {{days}} dias.", }, fitsProteinGoal: { sv: "För att det passar ditt proteinmål i dag.", en: "Because it fits your protein goal for today.", es: "Porque encaja con tu objetivo de proteína de hoy.", it: "Perché si adatta al tuo obiettivo proteico di oggi.", de: "Weil es zu deinem Protein-Ziel für heute passt.", fr: "Parce que cela correspond à votre objectif de protéines pour aujourd'hui.", da: "Fordi det passer til dit proteinmål i dag.", nb: "Fordi det passer proteinmålet ditt i dag.", fi: "Koska se sopii päivän proteiinitavoitteesi.", nl: "Omdat het past bij je eiwitdoel voor vandaag.", pl: "Ponieważ pasuje do dzisiejszego celu białkowego.", pt: "Porque se adequa à tua meta de proteína para hoje.", }, fitsVegetableGoal: { sv: "För att det bidrar till dina grönsaker i dag.", en: "Because it contributes to your vegetables for today.", es: "Porque contribuye a tus verduras de hoy.", it: "Perché contribuisce alle tue verdure di oggi.", de: "Weil es zu deinem Gemüse für heute beiträgt.", fr: "Parce que cela contribue à vos légumes d'aujourd'hui.", da: "Fordi det bidrager til dine grøntsager i dag.", nb: "Fordi det bidrar til grønnsakene dine i dag.", fi: "Koska se lisää päivän kasvissi saantia.", nl: "Omdat het bijdraagt aan je groenten voor vandaag.", pl: "Ponieważ przyczynia się do dzisiejszego spożycia warzyw.", pt: "Porque contribui para os teus vegetais de hoje.", }, tastePreference: { sv: "För att det matchar din smakprofil ({{axis}}).", en: "Because it matches your taste profile ({{axis}}).", es: "Porque coincide con tu perfil de sabor ({{axis}}).", it: "Perché corrisponde al tuo profilo di gusto ({{axis}}).", de: "Weil es zu deinem Geschmacksprofil passt ({{axis}}).", fr: "Parce que cela correspond à votre profil de goût ({{axis}}).", da: "Fordi det matcher din smagsprofil ({{axis}}).", nb: "Fordi det matcher smaksprofilen din ({{axis}}).", fi: "Koska se sopii makuprofiilisi ({{axis}}).", nl: "Omdat het bij je smaakprofiel past ({{axis}}).", pl: "Ponieważ pasuje do Twojego profilu smakowego ({{axis}}).", pt: "Porque corresponde ao teu perfil de sabor ({{axis}}).", }, usesStapleYouFinish: { sv: "För att ni brukar äta upp {{ingredient}} när ni lagar det.", en: "Because you usually finish {{ingredient}} when you cook it.", es: "Porque soléis acabar {{ingredient}} cuando lo cocináis.", it: "Perché di solito finite {{ingredient}} quando lo cucinate.", de: "Weil ihr {{ingredient}} normalerweise aufesset, wenn ihr es kocht.", fr: "Parce que vous finissez habituellement {{ingredient}} quand vous le cuisinez.", da: "Fordi I plejer at spise {{ingredient}} op, når I laver det.", nb: "Fordi dere pleier å spise opp {{ingredient}} når dere lager det.", fi: "Koska yleensä syöt {{ingredient}} loppuun, kun kokkaat sitä.", nl: "Omdat jullie {{ingredient}} meestal opeten als jullie het koken.", pl: "Ponieważ zwykle zjadacie {{ingredient}}, gdy to gotujecie.", pt: "Porque normalmente acabas {{ingredient}} quando cozinhas.", }, }; export function renderProvenance(entries: ProvenanceEntry[], languageTag: string): string { const lang = (languageTag.split("-")[0] ?? "sv") as LanguageTag; const parts: string[] = []; for (const entry of entries) { const tmpl = TEMPLATES[entry.key]; if (!tmpl) continue; let s = tmpl[lang] ?? tmpl.sv; for (const [k, v] of Object.entries(entry.args)) { s = s.replaceAll(`{{${k}}}`, String(v)); } parts.push(s); } return parts.join(" "); } export function renderProvenanceList(entries: ProvenanceEntry[], languageTag: string): string[] { const lang = (languageTag.split("-")[0] ?? "sv") as LanguageTag; return entries .map((entry) => { const tmpl = TEMPLATES[entry.key]; if (!tmpl) return null; let s = tmpl[lang] ?? tmpl.sv; for (const [k, v] of Object.entries(entry.args)) { s = s.replaceAll(`{{${k}}}`, String(v)); } return s; }) .filter((s): s is string => s != null); } /** Språk som varje proveniensmall är översatt till. */ export function provenanceTemplateLanguages(): Record { const result: Record = {}; for (const [key, t] of Object.entries(TEMPLATES)) { result[key] = Object.keys(t) as LanguageTag[]; } return result; } /** Förbjudna ord/fraser i proveniens/copy — R3 (ingen skam) + R7 (välmående). */ export const FORBIDDEN_COPY_PATTERNS = [ /överskrid/i, /bara .* kvar/i, /begränsa/i, /undvik/i, /skärp/i, /skäms/i, /dålig/i, /för mycket/i, /för lite/i, /borde inte/i, /får inte/i, /måste sluta/i, /överdriv/i, /kalori/i, // näringsmål ska ramas positivt, aldrig kcal-centrerat ]; /** Kontrollera att en sträng inte innehåller förbjuden copy. */ export function containsForbiddenCopy(text: string): boolean { return FORBIDDEN_COPY_PATTERNS.some((re) => re.test(text)); }