FAS 1b: Progressive onboarding (3-layer flow)
- Add PROGRESSIVE_ONBOARDING feature flag to KNOWN_FLAGS - Add onboarding_step column to users table (a/b/c) - New backend endpoints: - GET /v1/onboarding/status – check current step + feature flag - POST /v1/onboarding/quick-start – complete Step A (goal + precision) - POST /v1/onboarding/complete-b – complete Step B (diet, allergens, household) - POST /v1/onboarding/complete-c – complete Step C (health profile) - POST /v1/onboarding/skip – GDPR-friendly skip - Refactor mobile onboarding screen into 3 progressive layers - Update auth store with onboardingStep state - Update tab layout to only block on Step A - Update registration to set onboardingStep='a' - Add i18n keys for Step B/C titles across all 12 locales - Add Zod validation schemas (quickStartInputSchema, onboardingStatusSchema) - Add tests for validation and feature flag - Preserve existing /v1/me/onboarding for backward compatibility - Migration: 0004_progressive_onboarding.sql
This commit is contained in:
@@ -1,339 +1,343 @@
|
||||
{
|
||||
"common.loading": "Laddar …",
|
||||
"auth.backToLogin": "Tillbaka till inloggning",
|
||||
"auth.displayName": "Vad ska vi kalla dig?",
|
||||
"auth.email": "E-post",
|
||||
"auth.forgotBody": "Ange din e-postadress så skickar vi en återställningslänk om kontot finns.",
|
||||
"auth.forgotLink": "Glömt lösenordet?",
|
||||
"auth.forgotSentBody": "Om adressen har ett konto har vi skickat en länk som gäller i 30 minuter. Öppna den på den här enheten.",
|
||||
"auth.forgotSentTitle": "Kolla din inkorg",
|
||||
"auth.forgotSubmit": "Skicka länk",
|
||||
"auth.forgotTitle": "Återställ lösenord",
|
||||
"auth.hasAccount": "Har du redan ett konto? Logga in",
|
||||
"auth.login": "Logga in",
|
||||
"auth.newPassword": "Nytt lösenord (minst 10 tecken)",
|
||||
"auth.noAccount": "Ny här? Skapa konto",
|
||||
"auth.password": "Lösenord",
|
||||
"auth.register": "Skapa konto",
|
||||
"auth.resetBody": "Länken gäller i 30 minuter och kan bara användas en gång. Alla enheter loggas ut när lösenordet byts.",
|
||||
"auth.resetDoneBody": "Ditt lösenord är bytt. Logga in med det nya lösenordet.",
|
||||
"auth.resetDoneTitle": "Klart!",
|
||||
"auth.resetSubmit": "Byt lösenord",
|
||||
"auth.resetTitle": "Välj nytt lösenord",
|
||||
"auth.resetTokenPlaceholder": "Klistra in koden från mejlet",
|
||||
"auth.trialNote": "7 dagars full tillgång – inget kort behövs.",
|
||||
"auth.verifyDoneBody": "Tack! Ditt konto är nu verifierat.",
|
||||
"auth.verifyDoneTitle": "E-postadressen är bekräftad",
|
||||
"auth.verifyFailedBody": "Länken är ogiltig eller har gått ut. Begär en ny från profilen.",
|
||||
"auth.verifyFailedTitle": "Länken fungerade inte",
|
||||
"barcode.addPrompt": "Lägga till i lagret?",
|
||||
"barcode.aim": "Rikta kameran mot streckkoden",
|
||||
"barcode.allowCamera": "Tillåt kamera",
|
||||
"barcode.cameraBody": "För att skanna streckkoder behöver {brand} kameran.",
|
||||
"barcode.cameraTitle": "Kameran behövs",
|
||||
"barcode.kcalPer100": "{kcal} kcal/100 g",
|
||||
"barcode.needHousehold": "Du behöver ett hushåll först.",
|
||||
"barcode.noLocation": "Ingen förvaringsplats hittades.",
|
||||
"barcode.noNutrition": "Näringsdata saknas",
|
||||
"barcode.photoPackage": "Fota förpackningen",
|
||||
"barcode.unknownBody": "Produkten finns inte i databasen ännu. Fota framsidan och näringsdeklarationen så lägger vi till den.",
|
||||
"barcode.unknownTitle": "Okänd produkt",
|
||||
"common.add": "Lägg till",
|
||||
"common.back": "Tillbaka",
|
||||
"common.cancel": "Avbryt",
|
||||
"common.done": "Klar",
|
||||
"common.error": "Något gick fel. Försök igen.",
|
||||
"common.estimate": "Uppskattning",
|
||||
"common.loading": "Laddar …",
|
||||
"common.next": "Nästa",
|
||||
"common.off": "Av",
|
||||
"common.offline": "Offline – visar senast hämtade data",
|
||||
"common.on": "På",
|
||||
"common.oops": "Hoppsan",
|
||||
"common.remove": "Ta bort",
|
||||
"common.retry": "Försök igen",
|
||||
"common.save": "Spara",
|
||||
"common.cancel": "Avbryt",
|
||||
"common.next": "Nästa",
|
||||
"common.back": "Tillbaka",
|
||||
"common.done": "Klar",
|
||||
"common.skip": "Hoppa över",
|
||||
"common.offline": "Offline – visar senast hämtade data",
|
||||
"common.estimate": "Uppskattning",
|
||||
"tabs.whatToEat": "Vad ska vi äta?",
|
||||
"tabs.scan": "Skanna",
|
||||
"tabs.myDay": "Min dag",
|
||||
"tabs.home": "Hemma",
|
||||
"auth.login": "Logga in",
|
||||
"auth.register": "Skapa konto",
|
||||
"auth.email": "E-post",
|
||||
"auth.password": "Lösenord",
|
||||
"auth.displayName": "Vad ska vi kalla dig?",
|
||||
"auth.noAccount": "Ny här? Skapa konto",
|
||||
"auth.hasAccount": "Har du redan ett konto? Logga in",
|
||||
"auth.trialNote": "7 dagars full tillgång – inget kort behövs.",
|
||||
"onboarding.title": "Berätta lite om dig",
|
||||
"onboarding.subtitle": "Allt är frivilligt och kan ändras när som helst. Ju mer du fyller i, desto bättre förslag.",
|
||||
"onboarding.goal": "Vad är ditt främsta mål?",
|
||||
"onboarding.diet": "Hur äter du?",
|
||||
"common.undo": "Ångra",
|
||||
"cooked.deductPantry": "Dra ingredienser från lagret",
|
||||
"cooked.deductPantryNote": "Ingredienserna dras från lagret enligt först-utgången-först.",
|
||||
"cooked.mealBoxes": "Portioner till matlådor",
|
||||
"cooked.portionsCooked": "Portioner lagade",
|
||||
"cooked.rate": "Betygsätt",
|
||||
"cooked.title": "Hur gick det?",
|
||||
"cooked.whoAte": "Vilka åt?",
|
||||
"cooking.finish": "Klart – logga måltiden",
|
||||
"cooking.keepAwake": "Skärmen hålls vaken medan du lagar",
|
||||
"cooking.scale": "Portioner",
|
||||
"cooking.step": "Steg {current} av {total}",
|
||||
"cooking.timer": "Starta timer",
|
||||
"cooking.timerDone": "Klart!",
|
||||
"cooking.timerRunning": "{time} kvar",
|
||||
"home.budget": "Matbudget",
|
||||
"home.emptyInventory": "Lagret är tomt. Skanna kylen eller lägg till varor manuellt.",
|
||||
"home.expired": "Utgången",
|
||||
"home.expiresIn_one": "1 dag kvar",
|
||||
"home.expiresIn_other": "{days} dagar kvar",
|
||||
"home.expiresToday": "Går ut i dag",
|
||||
"home.household": "Hushåll",
|
||||
"home.inventory": "Matlager",
|
||||
"home.mealBoxes": "Matlådor",
|
||||
"home.moreItems_one": "+ 1 till …",
|
||||
"home.moreItems_other": "+ {count} till …",
|
||||
"home.pastBestBefore": "Passerat bäst före – lukta och smaka",
|
||||
"home.shopping": "Inköpslista",
|
||||
"home.thisWeek": "Denna vecka",
|
||||
"home.title": "Hemma",
|
||||
"home.useSoon": "Använd snart",
|
||||
"home.useSoonHint": "Bäst före handlar om kvalitet – lukta, titta och smaka innan du slänger. Sista förbrukningsdag ska däremot respekteras.",
|
||||
"home.waste": "Matsvinn",
|
||||
"home.wasteWeek": "Matsvinn denna vecka",
|
||||
"household.empty": "Du har inget hushåll ännu. Skapa ett i onboardingen eller gå med via kod.",
|
||||
"household.invite": "Bjud in med kod: {code}",
|
||||
"household.locations": "Förvaringsplatser",
|
||||
"household.members": "Medlemmar",
|
||||
"household.portionFactor": "×{factor} portion",
|
||||
"household.private": "Privat per person: mål, allergier, hälsodata och måltidshistorik.",
|
||||
"household.role.adult": "Vuxen",
|
||||
"household.role.child": "Barn",
|
||||
"household.role.member": "Medlem",
|
||||
"household.role.owner": "Ägare",
|
||||
"household.shareCode": "Dela koden med familjen så delar ni lager, lista och plan.",
|
||||
"household.shared": "Delas i hushållet: matlager, inköpslista, veckoplan, matlådor och budget.",
|
||||
"logmeal.manualTitle": "Manuellt",
|
||||
"logmeal.mealTypeTitle": "Måltidstyp",
|
||||
"logmeal.photoTip": "Tips: fota tallriken under Skanna så uppskattar appen åt dig – du bekräftar alltid.",
|
||||
"logmeal.proteinPlaceholder": "protein (g)",
|
||||
"logmeal.quickSubtitle": "Loggar samma näringsvärden som förra gången.",
|
||||
"logmeal.quickTitle": "Snabb återloggning",
|
||||
"logmeal.validationBody": "Namn och kalorier behövs för manuell loggning.",
|
||||
"logmeal.validationTitle": "Fyll i",
|
||||
"logmeal.whatPlaceholder": "Vad åt du?",
|
||||
"mealbox.eat": "Ät nu",
|
||||
"mealbox.eatBy": "Ät senast {date}",
|
||||
"mealbox.empty": "Inga matlådor just nu. När du lagar mat kan du spara portioner här.",
|
||||
"mealbox.enjoyBody": "Portionen är loggad i Min dag.",
|
||||
"mealbox.enjoyTitle": "Smaklig måltid!",
|
||||
"mealbox.guidanceNote": "Rekommenderad användningstid är vägledning – lita på lukt och smak.",
|
||||
"mealbox.portionsLeft_one": "1 portion kvar",
|
||||
"mealbox.portionsLeft_other": "{count} portioner kvar",
|
||||
"mealbox.title": "Matlådor",
|
||||
"memory.delete": "Radera",
|
||||
"memory.deleteAll": "Radera allt minne",
|
||||
"memory.deleteAllTitle": "Radera allt minne?",
|
||||
"memory.empty": "{brand} har inte lärt sig något om dig ännu.",
|
||||
"memory.irreversible": "Detta går inte att ångra.",
|
||||
"memory.origin.ai_inferred": "AI-antagande",
|
||||
"memory.origin.observed": "Observerat mönster",
|
||||
"memory.origin.user_stated": "Du har berättat",
|
||||
"memory.pause": "Pausa",
|
||||
"memory.paused": "Pausad",
|
||||
"memory.resume": "Återuppta",
|
||||
"memory.subtitle": "Full insyn. Rätta det som är fel, pausa eller radera – du äger ditt minne.",
|
||||
"memory.title": "Vad {brand} vet om mig",
|
||||
"memory.verified": "Verifierad",
|
||||
"memory.verify": "Stämmer",
|
||||
"myday.calories": "Kalorier",
|
||||
"myday.carbs": "Kolhydrater",
|
||||
"myday.estimateNote": "Värden med ~ är uppskattningar som du kan justera.",
|
||||
"myday.estimateRange": "{min}–{max} kcal, troligen {kcal}",
|
||||
"myday.fat": "Fett",
|
||||
"myday.fiber": "Fibrer",
|
||||
"myday.kcalProtein": "{kcal} kcal · {protein} g protein",
|
||||
"myday.logMeal": "Logga måltid",
|
||||
"myday.mealType.breakfast": "Frukost",
|
||||
"myday.mealType.dessert": "Efterrätt",
|
||||
"myday.mealType.dinner": "Middag",
|
||||
"myday.mealType.lunch": "Lunch",
|
||||
"myday.mealType.snack": "Mellanmål",
|
||||
"myday.noMeals": "Inga måltider loggade i dag.",
|
||||
"myday.over": "{kcal} kcal över målet",
|
||||
"myday.overRecommended": "Över rekommenderat",
|
||||
"myday.protein": "Protein",
|
||||
"myday.remaining": "{kcal} kcal kvar",
|
||||
"myday.salt": "Salt",
|
||||
"myday.targetsNote": "Målen är vägledning, inte medicinsk rådgivning.",
|
||||
"myday.title": "Min dag",
|
||||
"myday.todaysMeals": "Dagens måltider",
|
||||
"onboarding.allergen.crustaceans": "Skaldjur",
|
||||
"onboarding.allergen.eggs": "Ägg",
|
||||
"onboarding.allergen.fish": "Fisk",
|
||||
"onboarding.allergen.gluten": "Gluten",
|
||||
"onboarding.allergen.milk": "Mjölk/laktos",
|
||||
"onboarding.allergen.peanuts": "Jordnötter",
|
||||
"onboarding.allergen.sesame": "Sesam",
|
||||
"onboarding.allergen.soy": "Soja",
|
||||
"onboarding.allergen.tree_nuts": "Nötter",
|
||||
"onboarding.allergies": "Allergier och intoleranser",
|
||||
"onboarding.allergyNote": "Allergifiltrering är alltid strikt – rätter med dina allergener visas aldrig.",
|
||||
"onboarding.birthYearPlaceholder": "Födelseår",
|
||||
"onboarding.bodyTitle": "För personliga kalorimål (frivilligt)",
|
||||
"onboarding.diet": "Hur äter du?",
|
||||
"onboarding.diet.flexitarian": "Flexitarian",
|
||||
"onboarding.diet.omnivore": "Allätare",
|
||||
"onboarding.diet.pescatarian": "Pescetarian",
|
||||
"onboarding.diet.vegan": "Vegan",
|
||||
"onboarding.diet.vegetarian": "Vegetarian",
|
||||
"onboarding.goal": "Vad är ditt främsta mål?",
|
||||
"onboarding.goal.build_muscle": "Bygga muskler",
|
||||
"onboarding.goal.cook_more": "Laga mer hemma",
|
||||
"onboarding.goal.less_waste": "Minska matsvinn",
|
||||
"onboarding.goal.lose_weight": "Gå ner i vikt",
|
||||
"onboarding.goal.lower_cost": "Sänka matkostnaden",
|
||||
"onboarding.goal.maintain_weight": "Behålla vikten",
|
||||
"onboarding.goal.more_protein": "Äta mer protein",
|
||||
"onboarding.heightPlaceholder": "Längd (cm)",
|
||||
"onboarding.household": "Ditt hushåll",
|
||||
"onboarding.householdCreate": "Skapa hushåll",
|
||||
"onboarding.householdDefaultName": "Hemma",
|
||||
"onboarding.householdJoin": "Gå med via kod",
|
||||
"onboarding.householdName": "Hushållets namn",
|
||||
"onboarding.inviteCode": "Inbjudningskod",
|
||||
"onboarding.mode": "Hur noggrann vill du vara?",
|
||||
"onboarding.modeSimple": "Enkelt läge",
|
||||
"onboarding.modeSimpleDesc": "Fota maten, acceptera uppskattningar, minimalt pyssel.",
|
||||
"onboarding.modeExact": "Exakt läge",
|
||||
"onboarding.modeExactDesc": "Väg mat, ange gram, full kontroll på siffrorna.",
|
||||
"onboarding.modesCombine": "Lägena kan kombineras – enkelt till vardags, exakt när du vill.",
|
||||
"onboarding.modeSimple": "Enkelt läge",
|
||||
"onboarding.modeSimpleDesc": "Fota maten, acceptera uppskattningar, minimalt pyssel.",
|
||||
"onboarding.notMedical": "{brand} ger vägledning – inte medicinsk rådgivning.",
|
||||
"wte.title": "Vad ska vi äta?",
|
||||
"wte.subtitle": "Utifrån vad ni har hemma, vad som bör användas och vad ni gillar.",
|
||||
"wte.cravingPlaceholder": "Jag är sugen på … (t.ex. krämigt, asiatiskt, under 500 kcal)",
|
||||
"wte.mealBoxFirst": "Färdig mat hemma",
|
||||
"wte.whyTitle": "Varför detta förslag?",
|
||||
"wte.coverage": "{pct} % hemma",
|
||||
"wte.missing": "Saknas: {items}",
|
||||
"wte.empty": "Inga förslag ännu – lägg in lite mat i lagret eller lätta på filtren.",
|
||||
"wte.refresh": "Nya förslag",
|
||||
"scan.title": "Skanna",
|
||||
"scan.subtitle": "Fyll på matlagret eller logga en måltid med kameran.",
|
||||
"scan.fridge": "Kyl",
|
||||
"scan.freezer": "Frys",
|
||||
"scan.pantry": "Skafferi",
|
||||
"scan.ingredients": "Ingredienser",
|
||||
"scan.plate": "Tallrik",
|
||||
"scan.receipt": "Kvitto",
|
||||
"onboarding.stepBSubtitle": "Anpassa din profil för bättre förslag.",
|
||||
"onboarding.stepBTitle": "Fortsätt konfiguration",
|
||||
"onboarding.stepCSubtitle": "Valfritt – används för personliga kalorimål.",
|
||||
"onboarding.stepCTitle": "Din hälsoprofil",
|
||||
"onboarding.subtitle": "Allt är frivilligt och kan ändras när som helst. Ju mer du fyller i, desto bättre förslag.",
|
||||
"onboarding.title": "Berätta lite om dig",
|
||||
"onboarding.weightPlaceholder": "Vikt (kg)",
|
||||
"paywall.choose": "Välj",
|
||||
"paywall.fairUse": "Fair use-kvot för AI-skanningar ingår.",
|
||||
"paywall.family": "Family · upp till 6 personer",
|
||||
"paywall.freeNote": "Gratis: 10 AI-skanningar/mån, manuellt lager, sparade recept och enkel loggning.",
|
||||
"paywall.household": "Household · upp till 3 personer",
|
||||
"paywall.large": "Large Household · upp till 12 personer",
|
||||
"paywall.perMonth": "{price}/mån",
|
||||
"paywall.popular": "Populärast",
|
||||
"paywall.restore": "Återställ köp",
|
||||
"paywall.soonBody": "Köp aktiveras via App Store/Google Play när butiksintegrationen slås på (fas 7). Backend-flödet är redan klart.",
|
||||
"paywall.soonTitle": "Snart!",
|
||||
"paywall.subtitle": "Hela hushållets mat-OS: obegränsat lager, veckoplan och delning.",
|
||||
"paywall.title": "{brand} Premium",
|
||||
"paywall.trialActive_one": "1 dag kvar av din provperiod",
|
||||
"paywall.trialActive_other": "{days} dagar kvar av din provperiod",
|
||||
"profile.aiScansUsed": "AI-skanningar: {used} / {total} denna månad",
|
||||
"profile.consent.anonymized_improvement": "Anonymiserad förbättring av AI:n",
|
||||
"profile.consent.health_integration": "Hälsodata (Apple Health / Health Connect)",
|
||||
"profile.consent.image_training": "Mina bilder får användas för träning",
|
||||
"profile.consent.location_weather": "Plats för väderbaserade förslag",
|
||||
"profile.consent.personalization": "Personliga funktioner (minne & smakprofil)",
|
||||
"profile.consent.product_analytics": "Produktanalys",
|
||||
"profile.consent.push_notifications": "Push-notiser",
|
||||
"profile.consents": "Samtycken & data",
|
||||
"profile.consentsNote": "Samtyckena är separata – personlig funktion kräver aldrig träningssamtycke.",
|
||||
"profile.deleteAccount": "Radera konto",
|
||||
"profile.deleteBody": "All din data raderas permanent enligt GDPR.",
|
||||
"profile.deleteTitle": "Radera kontot?",
|
||||
"profile.emailUnverified": "E-postadressen är inte bekräftad",
|
||||
"profile.export": "Exportera min data",
|
||||
"profile.goals": "Mål & kost",
|
||||
"profile.language": "Språk",
|
||||
"profile.language.da": "Dansk",
|
||||
"profile.language.de": "Deutsch",
|
||||
"profile.language.en": "English",
|
||||
"profile.language.es": "Español",
|
||||
"profile.language.fi": "Suomi",
|
||||
"profile.language.fr": "Français",
|
||||
"profile.language.it": "Italiano",
|
||||
"profile.language.nb": "Norsk",
|
||||
"profile.language.nl": "Nederlands",
|
||||
"profile.language.pl": "Polski",
|
||||
"profile.language.pt": "Português",
|
||||
"profile.language.sv": "Svenska",
|
||||
"profile.logout": "Logga ut",
|
||||
"profile.memory": "Vad {brand} vet om mig",
|
||||
"profile.memorySubtitle": "Se, rätta, pausa eller radera det plattformen lärt sig.",
|
||||
"profile.modeLabel": "Läge:",
|
||||
"profile.resendVerification": "Skicka bekräftelsemejl igen",
|
||||
"profile.subscription": "Prenumeration",
|
||||
"profile.title": "Profil",
|
||||
"profile.verificationSent": "Skickat! Kolla din inkorg.",
|
||||
"recipe.avgRating": "Snitt {avg} av {count} betyg",
|
||||
"recipe.cook": "Laga nu",
|
||||
"recipe.cost": "ca {amount}/portion",
|
||||
"recipe.iCookedThis": "Jag lagade detta",
|
||||
"recipe.ingredients": "Ingredienser",
|
||||
"recipe.notSafe": "Passar inte dina kostinställningar",
|
||||
"recipe.perPortion": "per portion",
|
||||
"recipe.portions_one": "1 portion",
|
||||
"recipe.portions_other": "{count} portioner",
|
||||
"recipe.save": "Spara",
|
||||
"recipe.saved": "Sparad",
|
||||
"recipe.steps": "Gör så här",
|
||||
"recipe.substitutions": "Byt ut",
|
||||
"recipe.time": "{min} min",
|
||||
"scan.analyzing": "Analyserar …",
|
||||
"scan.barcode": "Streckkod",
|
||||
"scan.expiry": "Bäst före-datum",
|
||||
"scan.failed": "Analysen misslyckades. Försök igen eller registrera manuellt.",
|
||||
"scan.freezer": "Frys",
|
||||
"scan.fridge": "Kyl",
|
||||
"scan.ingredients": "Ingredienser",
|
||||
"scan.nutrition": "Näringsdeklaration",
|
||||
"scan.pantry": "Skafferi",
|
||||
"scan.plate": "Tallrik",
|
||||
"scan.quotaLeft_one": "1 AI-skanning kvar denna månad",
|
||||
"scan.quotaLeft_other": "{count} AI-skanningar kvar denna månad",
|
||||
"scan.receipt": "Kvitto",
|
||||
"scan.review.addItem": "+ Lägg till vara",
|
||||
"scan.review.approveAll": "Spara till lagret",
|
||||
"scan.review.bestBeforeNote": "Kvalitetsgräns – varan kan vara god längre. Lukta och smaka innan du slänger.",
|
||||
"scan.review.dateKindBestBefore": "Bäst före",
|
||||
"scan.review.dateKindUseBy": "Sista förbrukningsdag",
|
||||
"scan.review.datePlaceholder": "ÅÅÅÅ-MM-DD (valfritt)",
|
||||
"scan.review.itemPlaceholder": "Vara",
|
||||
"scan.review.quantityPlaceholder": "Mängd",
|
||||
"scan.review.rejected": "Borttagen",
|
||||
"scan.review.subtitle": "AI:n är osäker ibland – du bestämmer. Ändra, ta bort eller lägg till innan du sparar.",
|
||||
"scan.review.title": "Granska resultatet",
|
||||
"scan.review.uncertain": "Osäker – kontrollera",
|
||||
"scan.review.unitPlaceholder": "Enhet (g, l, st …)",
|
||||
"scan.review.unknownItem": "Okänd vara",
|
||||
"scan.review.useByNote": "Säkerhetsgräns – ät inte efter detta datum.",
|
||||
"scan.subtitle": "Fyll på matlagret eller logga en måltid med kameran.",
|
||||
"scan.takePhoto": "Ta bild",
|
||||
"scan.tips.title": "Så blir analysen bäst",
|
||||
"scan.tips.overview": "Ta en översiktsbild först",
|
||||
"scan.tips.shelf": "Fota gärna hylla för hylla",
|
||||
"scan.tips.light": "Undvik mörker och skugga",
|
||||
"scan.tips.move": "Flytta varor som skymmer varandra",
|
||||
"scan.analyzing": "Analyserar …",
|
||||
"scan.review.title": "Granska resultatet",
|
||||
"scan.review.subtitle": "AI:n är osäker ibland – du bestämmer. Ändra, ta bort eller lägg till innan du sparar.",
|
||||
"scan.review.uncertain": "Osäker – kontrollera",
|
||||
"scan.review.approveAll": "Spara till lagret",
|
||||
"scan.review.rejected": "Borttagen",
|
||||
"scan.failed": "Analysen misslyckades. Försök igen eller registrera manuellt.",
|
||||
"myday.title": "Min dag",
|
||||
"myday.calories": "Kalorier",
|
||||
"myday.protein": "Protein",
|
||||
"myday.carbs": "Kolhydrater",
|
||||
"myday.fat": "Fett",
|
||||
"myday.fiber": "Fibrer",
|
||||
"myday.salt": "Salt",
|
||||
"myday.remaining": "{kcal} kcal kvar",
|
||||
"myday.over": "{kcal} kcal över målet",
|
||||
"myday.logMeal": "Logga måltid",
|
||||
"myday.noMeals": "Inga måltider loggade i dag.",
|
||||
"myday.estimateNote": "Värden med ~ är uppskattningar som du kan justera.",
|
||||
"myday.targetsNote": "Målen är vägledning, inte medicinsk rådgivning.",
|
||||
"home.title": "Hemma",
|
||||
"home.inventory": "Matlager",
|
||||
"home.useSoon": "Använd snart",
|
||||
"home.mealBoxes": "Matlådor",
|
||||
"home.shopping": "Inköpslista",
|
||||
"home.budget": "Matbudget",
|
||||
"home.household": "Hushåll",
|
||||
"home.waste": "Matsvinn",
|
||||
"home.emptyInventory": "Lagret är tomt. Skanna kylen eller lägg till varor manuellt.",
|
||||
"home.expiresToday": "Går ut i dag",
|
||||
"home.expired": "Utgången",
|
||||
"home.pastBestBefore": "Passerat bäst före – lukta och smaka",
|
||||
"recipe.time": "{min} min",
|
||||
"recipe.perPortion": "per portion",
|
||||
"recipe.ingredients": "Ingredienser",
|
||||
"recipe.steps": "Gör så här",
|
||||
"recipe.cook": "Laga nu",
|
||||
"recipe.notSafe": "Passar inte dina kostinställningar",
|
||||
"recipe.substitutions": "Byt ut",
|
||||
"recipe.iCookedThis": "Jag lagade detta",
|
||||
"recipe.cost": "ca {amount}/portion",
|
||||
"cooking.step": "Steg {current} av {total}",
|
||||
"cooking.timer": "Starta timer",
|
||||
"cooking.timerRunning": "{time} kvar",
|
||||
"cooking.finish": "Klart – logga måltiden",
|
||||
"cooking.scale": "Portioner",
|
||||
"cooking.keepAwake": "Skärmen hålls vaken medan du lagar",
|
||||
"cooked.title": "Hur gick det?",
|
||||
"cooked.portionsCooked": "Portioner lagade",
|
||||
"cooked.whoAte": "Vilka åt?",
|
||||
"cooked.mealBoxes": "Portioner till matlådor",
|
||||
"cooked.deductPantry": "Dra ingredienser från lagret",
|
||||
"cooked.rate": "Betygsätt",
|
||||
"shopping.title": "Inköpslista",
|
||||
"scan.tips.overview": "Ta en översiktsbild först",
|
||||
"scan.tips.shelf": "Fota gärna hylla för hylla",
|
||||
"scan.tips.title": "Så blir analysen bäst",
|
||||
"scan.title": "Skanna",
|
||||
"shopping.addPlaceholder": "Lägg till vara …",
|
||||
"shopping.complete": "Avsluta köprundan",
|
||||
"shopping.completedBody_one": "1 vara lades in i matlagret.",
|
||||
"shopping.completedBody_other": "{count} varor lades in i matlagret.",
|
||||
"shopping.completedTitle": "Klart!",
|
||||
"shopping.completeNote": "Bockade varor läggs in i matlagret.",
|
||||
"shopping.empty": "Listan är tom.",
|
||||
"shopping.estimated": "ca {amount}",
|
||||
"shopping.estimatedTotal": "Uppskattad totalsumma: ca {amount}",
|
||||
"mealbox.title": "Matlådor",
|
||||
"mealbox.eatBy": "Ät senast {date}",
|
||||
"mealbox.eat": "Ät nu",
|
||||
"mealbox.empty": "Inga matlådor just nu. När du lagar mat kan du spara portioner här.",
|
||||
"household.members": "Medlemmar",
|
||||
"household.invite": "Bjud in med kod: {code}",
|
||||
"household.shared": "Delas i hushållet: matlager, inköpslista, veckoplan, matlådor och budget.",
|
||||
"household.private": "Privat per person: mål, allergier, hälsodata och måltidshistorik.",
|
||||
"memory.title": "Vad {brand} vet om mig",
|
||||
"memory.subtitle": "Full insyn. Rätta det som är fel, pausa eller radera – du äger ditt minne.",
|
||||
"memory.paused": "Pausad",
|
||||
"memory.verify": "Stämmer",
|
||||
"memory.pause": "Pausa",
|
||||
"memory.delete": "Radera",
|
||||
"memory.deleteAll": "Radera allt minne",
|
||||
"memory.empty": "{brand} har inte lärt sig något om dig ännu.",
|
||||
"memory.origin.user_stated": "Du har berättat",
|
||||
"memory.origin.observed": "Observerat mönster",
|
||||
"memory.origin.ai_inferred": "AI-antagande",
|
||||
"paywall.title": "{brand} Premium",
|
||||
"paywall.subtitle": "Hela hushållets mat-OS: obegränsat lager, veckoplan och delning.",
|
||||
"paywall.household": "Household · upp till 3 personer",
|
||||
"paywall.family": "Family · upp till 6 personer",
|
||||
"paywall.large": "Large Household · upp till 12 personer",
|
||||
"paywall.perMonth": "{price}/mån",
|
||||
"paywall.fairUse": "Fair use-kvot för AI-skanningar ingår.",
|
||||
"paywall.restore": "Återställ köp",
|
||||
"paywall.freeNote": "Gratis: 10 AI-skanningar/mån, manuellt lager, sparade recept och enkel loggning.",
|
||||
"profile.title": "Profil",
|
||||
"profile.goals": "Mål & kost",
|
||||
"profile.consents": "Samtycken & data",
|
||||
"profile.memory": "Vad {brand} vet om mig",
|
||||
"profile.subscription": "Prenumeration",
|
||||
"profile.logout": "Logga ut",
|
||||
"profile.export": "Exportera min data",
|
||||
"profile.deleteAccount": "Radera konto",
|
||||
"home.expiresIn_one": "1 dag kvar",
|
||||
"home.expiresIn_other": "{days} dagar kvar",
|
||||
"scan.quotaLeft_one": "1 AI-skanning kvar denna månad",
|
||||
"scan.quotaLeft_other": "{count} AI-skanningar kvar denna månad",
|
||||
"mealbox.portionsLeft_one": "1 portion kvar",
|
||||
"mealbox.portionsLeft_other": "{count} portioner kvar",
|
||||
"recipe.portions_one": "1 portion",
|
||||
"recipe.portions_other": "{count} portioner",
|
||||
"paywall.trialActive_one": "1 dag kvar av din provperiod",
|
||||
"paywall.trialActive_other": "{days} dagar kvar av din provperiod",
|
||||
"profile.language": "Språk",
|
||||
"profile.language.sv": "Svenska",
|
||||
"profile.language.en": "English",
|
||||
"auth.forgotLink": "Glömt lösenordet?",
|
||||
"auth.forgotTitle": "Återställ lösenord",
|
||||
"auth.forgotBody": "Ange din e-postadress så skickar vi en återställningslänk om kontot finns.",
|
||||
"auth.forgotSubmit": "Skicka länk",
|
||||
"auth.forgotSentTitle": "Kolla din inkorg",
|
||||
"auth.forgotSentBody": "Om adressen har ett konto har vi skickat en länk som gäller i 30 minuter. Öppna den på den här enheten.",
|
||||
"auth.backToLogin": "Tillbaka till inloggning",
|
||||
"auth.resetTitle": "Välj nytt lösenord",
|
||||
"auth.resetBody": "Länken gäller i 30 minuter och kan bara användas en gång. Alla enheter loggas ut när lösenordet byts.",
|
||||
"auth.resetTokenPlaceholder": "Klistra in koden från mejlet",
|
||||
"auth.newPassword": "Nytt lösenord (minst 10 tecken)",
|
||||
"auth.resetSubmit": "Byt lösenord",
|
||||
"auth.resetDoneTitle": "Klart!",
|
||||
"auth.resetDoneBody": "Ditt lösenord är bytt. Logga in med det nya lösenordet.",
|
||||
"auth.verifyDoneTitle": "E-postadressen är bekräftad",
|
||||
"auth.verifyDoneBody": "Tack! Ditt konto är nu verifierat.",
|
||||
"auth.verifyFailedTitle": "Länken fungerade inte",
|
||||
"auth.verifyFailedBody": "Länken är ogiltig eller har gått ut. Begär en ny från profilen.",
|
||||
"profile.emailUnverified": "E-postadressen är inte bekräftad",
|
||||
"profile.resendVerification": "Skicka bekräftelsemejl igen",
|
||||
"profile.verificationSent": "Skickat! Kolla din inkorg.",
|
||||
"profile.language.es": "Español",
|
||||
"profile.language.it": "Italiano",
|
||||
"profile.language.de": "Deutsch",
|
||||
"profile.language.fr": "Français",
|
||||
"profile.language.da": "Dansk",
|
||||
"profile.language.nb": "Norsk",
|
||||
"profile.language.fi": "Suomi",
|
||||
"profile.language.nl": "Nederlands",
|
||||
"profile.language.pl": "Polski",
|
||||
"profile.language.pt": "Português",
|
||||
"common.oops": "Hoppsan",
|
||||
"common.undo": "Ångra",
|
||||
"common.remove": "Ta bort",
|
||||
"common.add": "Lägg till",
|
||||
"common.on": "På",
|
||||
"common.off": "Av",
|
||||
"home.thisWeek": "Denna vecka",
|
||||
"home.wasteWeek": "Matsvinn denna vecka",
|
||||
"home.useSoonHint": "Bäst före handlar om kvalitet – lukta, titta och smaka innan du slänger. Sista förbrukningsdag ska däremot respekteras.",
|
||||
"scan.review.itemPlaceholder": "Vara",
|
||||
"scan.review.quantityPlaceholder": "Mängd",
|
||||
"scan.review.unitPlaceholder": "Enhet (g, l, st …)",
|
||||
"scan.review.datePlaceholder": "ÅÅÅÅ-MM-DD (valfritt)",
|
||||
"scan.review.dateKindBestBefore": "Bäst före",
|
||||
"scan.review.dateKindUseBy": "Sista förbrukningsdag",
|
||||
"scan.review.bestBeforeNote": "Kvalitetsgräns – varan kan vara god längre. Lukta och smaka innan du slänger.",
|
||||
"scan.review.useByNote": "Säkerhetsgräns – ät inte efter detta datum.",
|
||||
"scan.review.addItem": "+ Lägg till vara",
|
||||
"scan.review.unknownItem": "Okänd vara",
|
||||
"barcode.aim": "Rikta kameran mot streckkoden",
|
||||
"barcode.addPrompt": "Lägga till i lagret?",
|
||||
"barcode.kcalPer100": "{kcal} kcal/100 g",
|
||||
"barcode.noNutrition": "Näringsdata saknas",
|
||||
"barcode.unknownTitle": "Okänd produkt",
|
||||
"barcode.unknownBody": "Produkten finns inte i databasen ännu. Fota framsidan och näringsdeklarationen så lägger vi till den.",
|
||||
"barcode.photoPackage": "Fota förpackningen",
|
||||
"barcode.needHousehold": "Du behöver ett hushåll först.",
|
||||
"barcode.noLocation": "Ingen förvaringsplats hittades.",
|
||||
"barcode.cameraTitle": "Kameran behövs",
|
||||
"barcode.cameraBody": "För att skanna streckkoder behöver {brand} kameran.",
|
||||
"barcode.allowCamera": "Tillåt kamera",
|
||||
"onboarding.goal.lose_weight": "Gå ner i vikt",
|
||||
"onboarding.goal.build_muscle": "Bygga muskler",
|
||||
"onboarding.goal.maintain_weight": "Behålla vikten",
|
||||
"onboarding.goal.more_protein": "Äta mer protein",
|
||||
"onboarding.goal.less_waste": "Minska matsvinn",
|
||||
"onboarding.goal.lower_cost": "Sänka matkostnaden",
|
||||
"onboarding.goal.cook_more": "Laga mer hemma",
|
||||
"onboarding.diet.omnivore": "Allätare",
|
||||
"onboarding.diet.flexitarian": "Flexitarian",
|
||||
"onboarding.diet.pescatarian": "Pescetarian",
|
||||
"onboarding.diet.vegetarian": "Vegetarian",
|
||||
"onboarding.diet.vegan": "Vegan",
|
||||
"onboarding.allergen.gluten": "Gluten",
|
||||
"onboarding.allergen.milk": "Mjölk/laktos",
|
||||
"onboarding.allergen.eggs": "Ägg",
|
||||
"onboarding.allergen.tree_nuts": "Nötter",
|
||||
"onboarding.allergen.peanuts": "Jordnötter",
|
||||
"onboarding.allergen.fish": "Fisk",
|
||||
"onboarding.allergen.crustaceans": "Skaldjur",
|
||||
"onboarding.allergen.soy": "Soja",
|
||||
"onboarding.allergen.sesame": "Sesam",
|
||||
"onboarding.bodyTitle": "För personliga kalorimål (frivilligt)",
|
||||
"onboarding.weightPlaceholder": "Vikt (kg)",
|
||||
"onboarding.heightPlaceholder": "Längd (cm)",
|
||||
"onboarding.birthYearPlaceholder": "Födelseår",
|
||||
"onboarding.householdDefaultName": "Hemma",
|
||||
"onboarding.modesCombine": "Lägena kan kombineras – enkelt till vardags, exakt när du vill.",
|
||||
"myday.mealType.breakfast": "Frukost",
|
||||
"myday.mealType.lunch": "Lunch",
|
||||
"myday.mealType.dinner": "Middag",
|
||||
"myday.mealType.snack": "Mellanmål",
|
||||
"myday.mealType.dessert": "Efterrätt",
|
||||
"myday.overRecommended": "Över rekommenderat",
|
||||
"myday.todaysMeals": "Dagens måltider",
|
||||
"myday.estimateRange": "{min}–{max} kcal, troligen {kcal}",
|
||||
"myday.kcalProtein": "{kcal} kcal · {protein} g protein",
|
||||
"logmeal.mealTypeTitle": "Måltidstyp",
|
||||
"logmeal.quickTitle": "Snabb återloggning",
|
||||
"logmeal.quickSubtitle": "Loggar samma näringsvärden som förra gången.",
|
||||
"logmeal.manualTitle": "Manuellt",
|
||||
"logmeal.whatPlaceholder": "Vad åt du?",
|
||||
"logmeal.proteinPlaceholder": "protein (g)",
|
||||
"logmeal.validationTitle": "Fyll i",
|
||||
"logmeal.validationBody": "Namn och kalorier behövs för manuell loggning.",
|
||||
"logmeal.photoTip": "Tips: fota tallriken under Skanna så uppskattar appen åt dig – du bekräftar alltid.",
|
||||
"shopping.section.frukt_gront": "Frukt & grönt",
|
||||
"shopping.section.brod": "Bröd",
|
||||
"shopping.section.mejeri": "Mejeri",
|
||||
"shopping.section.kott_fagel": "Kött & fågel",
|
||||
"shopping.section.fisk": "Fisk",
|
||||
"shopping.section.chark": "Chark",
|
||||
"shopping.section.frys": "Frys",
|
||||
"shopping.section.skafferi": "Skafferi",
|
||||
"shopping.section.konserver": "Konserver",
|
||||
"shopping.section.kryddor_bak": "Kryddor & bakning",
|
||||
"shopping.section.dryck": "Dryck",
|
||||
"shopping.section.snacks": "Snacks",
|
||||
"shopping.section.fisk": "Fisk",
|
||||
"shopping.section.frukt_gront": "Frukt & grönt",
|
||||
"shopping.section.frys": "Frys",
|
||||
"shopping.section.hygien_ovrigt": "Övrigt",
|
||||
"shopping.completedTitle": "Klart!",
|
||||
"household.role.owner": "Ägare",
|
||||
"household.role.adult": "Vuxen",
|
||||
"household.role.member": "Medlem",
|
||||
"household.role.child": "Barn",
|
||||
"household.empty": "Du har inget hushåll ännu. Skapa ett i onboardingen eller gå med via kod.",
|
||||
"household.shareCode": "Dela koden med familjen så delar ni lager, lista och plan.",
|
||||
"household.locations": "Förvaringsplatser",
|
||||
"household.portionFactor": "×{factor} portion",
|
||||
"mealbox.enjoyTitle": "Smaklig måltid!",
|
||||
"mealbox.enjoyBody": "Portionen är loggad i Min dag.",
|
||||
"mealbox.guidanceNote": "Rekommenderad användningstid är vägledning – lita på lukt och smak.",
|
||||
"paywall.soonTitle": "Snart!",
|
||||
"paywall.soonBody": "Köp aktiveras via App Store/Google Play när butiksintegrationen slås på (fas 7). Backend-flödet är redan klart.",
|
||||
"paywall.popular": "Populärast",
|
||||
"paywall.choose": "Välj",
|
||||
"recipe.saved": "Sparad",
|
||||
"recipe.save": "Spara",
|
||||
"recipe.avgRating": "Snitt {avg} av {count} betyg",
|
||||
"memory.verified": "Verifierad",
|
||||
"memory.resume": "Återuppta",
|
||||
"memory.deleteAllTitle": "Radera allt minne?",
|
||||
"memory.irreversible": "Detta går inte att ångra.",
|
||||
"profile.consent.personalization": "Personliga funktioner (minne & smakprofil)",
|
||||
"profile.consent.anonymized_improvement": "Anonymiserad förbättring av AI:n",
|
||||
"profile.consent.image_training": "Mina bilder får användas för träning",
|
||||
"profile.consent.health_integration": "Hälsodata (Apple Health / Health Connect)",
|
||||
"profile.consent.location_weather": "Plats för väderbaserade förslag",
|
||||
"profile.consent.push_notifications": "Push-notiser",
|
||||
"profile.modeLabel": "Läge:",
|
||||
"profile.memorySubtitle": "Se, rätta, pausa eller radera det plattformen lärt sig.",
|
||||
"profile.consentsNote": "Samtyckena är separata – personlig funktion kräver aldrig träningssamtycke.",
|
||||
"profile.aiScansUsed": "AI-skanningar: {used} / {total} denna månad",
|
||||
"profile.deleteTitle": "Radera kontot?",
|
||||
"profile.deleteBody": "All din data raderas permanent enligt GDPR.",
|
||||
"cooking.timerDone": "Klart!",
|
||||
"cooked.deductPantryNote": "Ingredienserna dras från lagret enligt först-utgången-först.",
|
||||
"home.moreItems_one": "+ 1 till …",
|
||||
"home.moreItems_other": "+ {count} till …",
|
||||
"shopping.completedBody_one": "1 vara lades in i matlagret.",
|
||||
"shopping.completedBody_other": "{count} varor lades in i matlagret.",
|
||||
"profile.consent.product_analytics": "Produktanalys"
|
||||
"shopping.section.konserver": "Konserver",
|
||||
"shopping.section.kott_fagel": "Kött & fågel",
|
||||
"shopping.section.kryddor_bak": "Kryddor & bakning",
|
||||
"shopping.section.mejeri": "Mejeri",
|
||||
"shopping.section.skafferi": "Skafferi",
|
||||
"shopping.section.snacks": "Snacks",
|
||||
"shopping.title": "Inköpslista",
|
||||
"tabs.home": "Hemma",
|
||||
"tabs.myDay": "Min dag",
|
||||
"tabs.scan": "Skanna",
|
||||
"tabs.whatToEat": "Vad ska vi äta?",
|
||||
"wte.coverage": "{pct} % hemma",
|
||||
"wte.cravingPlaceholder": "Jag är sugen på … (t.ex. krämigt, asiatiskt, under 500 kcal)",
|
||||
"wte.empty": "Inga förslag ännu – lägg in lite mat i lagret eller lätta på filtren.",
|
||||
"wte.mealBoxFirst": "Färdig mat hemma",
|
||||
"wte.missing": "Saknas: {items}",
|
||||
"wte.refresh": "Nya förslag",
|
||||
"wte.subtitle": "Utifrån vad ni har hemma, vad som bör användas och vad ni gillar.",
|
||||
"wte.title": "Vad ska vi äta?",
|
||||
"wte.whyTitle": "Varför detta förslag?"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user