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:
Sven (AAMOS AI)
2026-08-06 00:39:44 +07:00
parent b2b6601f54
commit 2f3cdf8016
24 changed files with 4338 additions and 3837 deletions
+325 -321
View File
@@ -1,353 +1,357 @@
{
"common.loading": "Ładowanie …",
"auth.backToLogin": "Wróć do logowania",
"auth.displayName": "Jak mamy się do Ciebie zwracać?",
"auth.email": "E-mail",
"auth.forgotBody": "Podaj swój e-mail, a wyślemy link, jeśli konto istnieje.",
"auth.forgotLink": "Nie pamiętasz hasła?",
"auth.forgotSentBody": "Jeśli adres ma konto, wysłaliśmy link ważny 30 minut. Otwórz go na tym urządzeniu.",
"auth.forgotSentTitle": "Sprawdź skrzynkę",
"auth.forgotSubmit": "Wyślij link",
"auth.forgotTitle": "Zresetuj hasło",
"auth.hasAccount": "Masz już konto? Zaloguj się",
"auth.login": "Zaloguj się",
"auth.newPassword": "Nowe hasło (min. 10 znaków)",
"auth.noAccount": "Nowy tutaj? Utwórz konto",
"auth.password": "Hasło",
"auth.register": "Utwórz konto",
"auth.resetBody": "Link jest ważny 30 minut i można go użyć tylko raz. Wszystkie urządzenia zostaną wylogowane.",
"auth.resetDoneBody": "Hasło zostało zmienione. Zaloguj się nowym hasłem.",
"auth.resetDoneTitle": "Gotowe!",
"auth.resetSubmit": "Zmień hasło",
"auth.resetTitle": "Wybierz nowe hasło",
"auth.resetTokenPlaceholder": "Wklej kod z e-maila",
"auth.trialNote": "7 dni pełnego dostępu bez karty.",
"auth.verifyDoneBody": "Dziękujemy! Twoje konto jest zweryfikowane.",
"auth.verifyDoneTitle": "E-mail potwierdzony",
"auth.verifyFailedBody": "Link jest nieprawidłowy lub wygasł. Poproś o nowy w profilu.",
"auth.verifyFailedTitle": "Link nie zadziałał",
"barcode.addPrompt": "Dodać do spiżarni?",
"barcode.aim": "Skieruj aparat na kod kreskowy",
"barcode.allowCamera": "Zezwól na aparat",
"barcode.cameraBody": "{brand} potrzebuje aparatu do skanowania kodów kreskowych.",
"barcode.cameraTitle": "Potrzebny aparat",
"barcode.kcalPer100": "{kcal} kcal/100 g",
"barcode.needHousehold": "Najpierw potrzebujesz gospodarstwa.",
"barcode.noLocation": "Nie znaleziono miejsca przechowywania.",
"barcode.noNutrition": "Brak danych żywieniowych",
"barcode.photoPackage": "Sfotografuj opakowanie",
"barcode.unknownBody": "Tego produktu nie ma jeszcze w bazie. Sfotografuj przód i etykietę wartości odżywczych, a my go dodamy.",
"barcode.unknownTitle": "Nieznany produkt",
"common.add": "Dodaj",
"common.back": "Wstecz",
"common.cancel": "Anuluj",
"common.done": "Gotowe",
"common.error": "Coś poszło nie tak. Spróbuj ponownie.",
"common.estimate": "Szacunek",
"common.loading": "Ładowanie …",
"common.next": "Dalej",
"common.off": "Wył.",
"common.offline": "Offline ostatnio pobrane dane",
"common.on": "Wł.",
"common.oops": "Ups",
"common.remove": "Usuń",
"common.retry": "Spróbuj ponownie",
"common.save": "Zapisz",
"common.cancel": "Anuluj",
"common.next": "Dalej",
"common.back": "Wstecz",
"common.done": "Gotowe",
"common.skip": "Pomiń",
"common.offline": "Offline ostatnio pobrane dane",
"common.estimate": "Szacunek",
"tabs.whatToEat": "Co jemy?",
"tabs.scan": "Skanuj",
"tabs.myDay": "Mój dzień",
"tabs.home": "W domu",
"auth.login": "Zaloguj się",
"auth.register": "Utwórz konto",
"auth.email": "E-mail",
"auth.password": "Hasło",
"auth.displayName": "Jak mamy się do Ciebie zwracać?",
"auth.noAccount": "Nowy tutaj? Utwórz konto",
"auth.hasAccount": "Masz już konto? Zaloguj się",
"auth.trialNote": "7 dni pełnego dostępu bez karty.",
"auth.forgotLink": "Nie pamiętasz hasła?",
"auth.forgotTitle": "Zresetuj hasło",
"auth.forgotBody": "Podaj swój e-mail, a wyślemy link, jeśli konto istnieje.",
"auth.forgotSubmit": "Wyślij link",
"auth.forgotSentTitle": "Sprawdź skrzynkę",
"auth.forgotSentBody": "Jeśli adres ma konto, wysłaliśmy link ważny 30 minut. Otwórz go na tym urządzeniu.",
"auth.backToLogin": "Wróć do logowania",
"auth.resetTitle": "Wybierz nowe hasło",
"auth.resetBody": "Link jest ważny 30 minut i można go użyć tylko raz. Wszystkie urządzenia zostaną wylogowane.",
"auth.resetTokenPlaceholder": "Wklej kod z e-maila",
"auth.newPassword": "Nowe hasło (min. 10 znaków)",
"auth.resetSubmit": "Zmień hasło",
"auth.resetDoneTitle": "Gotowe!",
"auth.resetDoneBody": "Hasło zostało zmienione. Zaloguj się nowym hasłem.",
"auth.verifyDoneTitle": "E-mail potwierdzony",
"auth.verifyDoneBody": "Dziękujemy! Twoje konto jest zweryfikowane.",
"auth.verifyFailedTitle": "Link nie zadziałał",
"auth.verifyFailedBody": "Link jest nieprawidłowy lub wygasł. Poproś o nowy w profilu.",
"onboarding.title": "Opowiedz nam coś o sobie",
"onboarding.subtitle": "Wszystko jest opcjonalne i można to zmienić w każdej chwili. Im więcej uzupełnisz, tym lepsze propozycje.",
"onboarding.goal": "Jaki jest Twój główny cel?",
"onboarding.diet": "Jak się odżywiasz?",
"common.undo": "Cofnij",
"cooked.deductPantry": "Odejmij składniki ze spiżarni",
"cooked.deductPantryNote": "Składniki są odejmowane ze spiżarni najpierw te z najkrótszym terminem.",
"cooked.mealBoxes": "Porcje do pojemników",
"cooked.portionsCooked": "Ugotowane porcje",
"cooked.rate": "Oceń",
"cooked.title": "Jak poszło?",
"cooked.whoAte": "Kto jadł?",
"cooking.finish": "Gotowe zarejestruj posiłek",
"cooking.keepAwake": "Ekran pozostaje włączony podczas gotowania",
"cooking.scale": "Porcje",
"cooking.step": "Krok {current} z {total}",
"cooking.timer": "Uruchom minutnik",
"cooking.timerDone": "Gotowe!",
"cooking.timerRunning": "Pozostało {time}",
"home.budget": "Budżet na jedzenie",
"home.emptyInventory": "Spiżarnia jest pusta. Zeskanuj lodówkę albo dodaj produkty ręcznie.",
"home.expired": "Przeterminowane",
"home.expiresIn_few": "Zostały {days} dni",
"home.expiresIn_many": "Zostało {days} dni",
"home.expiresIn_one": "Został 1 dzień",
"home.expiresIn_other": "Zostało {days} dnia",
"home.expiresToday": "Kończy się dziś",
"home.household": "Gospodarstwo",
"home.inventory": "Spiżarnia",
"home.mealBoxes": "Pojemniki z jedzeniem",
"home.moreItems_few": "+ jeszcze {count} …",
"home.moreItems_many": "+ jeszcze {count} …",
"home.moreItems_one": "+ jeszcze 1 …",
"home.moreItems_other": "+ jeszcze {count} …",
"home.pastBestBefore": "Po terminie przydatności najpierw powąchaj i spróbuj",
"home.shopping": "Lista zakupów",
"home.thisWeek": "W tym tygodniu",
"home.title": "W domu",
"home.useSoon": "Zużyj wkrótce",
"home.useSoonHint": "„Najlepiej spożyć przed” dotyczy jakości powąchaj, obejrzyj i spróbuj, zanim wyrzucisz. Terminu „należy spożyć do” należy natomiast przestrzegać.",
"home.waste": "Marnowanie żywności",
"home.wasteWeek": "Marnowanie w tym tygodniu",
"household.empty": "Nie masz jeszcze gospodarstwa. Utwórz je podczas konfiguracji lub dołącz kodem.",
"household.invite": "Zaproś kodem: {code}",
"household.locations": "Miejsca przechowywania",
"household.members": "Członkowie",
"household.portionFactor": "×{factor} porcji",
"household.private": "Prywatne dla każdej osoby: cele, alergie, dane zdrowotne i historia posiłków.",
"household.role.adult": "Dorosły",
"household.role.child": "Dziecko",
"household.role.member": "Członek",
"household.role.owner": "Właściciel",
"household.shareCode": "Udostępnij kod rodzinie, aby dzielić spiżarnię, listę i plan.",
"household.shared": "Wspólne w gospodarstwie: spiżarnia, lista zakupów, plan tygodnia, pojemniki i budżet.",
"logmeal.manualTitle": "Ręcznie",
"logmeal.mealTypeTitle": "Rodzaj posiłku",
"logmeal.photoTip": "Wskazówka: sfotografuj talerz w zakładce Skanuj, a aplikacja oszacuje za Ciebie zawsze potwierdzasz.",
"logmeal.proteinPlaceholder": "białko (g)",
"logmeal.quickSubtitle": "Zapisuje te same wartości odżywcze co ostatnio.",
"logmeal.quickTitle": "Szybkie ponowne dodanie",
"logmeal.validationBody": "Do ręcznego dodania potrzebne są nazwa i kalorie.",
"logmeal.validationTitle": "Uzupełnij",
"logmeal.whatPlaceholder": "Co zjadłeś/aś?",
"mealbox.eat": "Zjedz teraz",
"mealbox.eatBy": "Zjedz do {date}",
"mealbox.empty": "Brak pojemników. Gotując, możesz zapisać tu porcje.",
"mealbox.enjoyBody": "Porcja została zapisana w Mój dzień.",
"mealbox.enjoyTitle": "Smacznego!",
"mealbox.guidanceNote": "Zalecany termin to wskazówka zaufaj węchowi i smakowi.",
"mealbox.portionsLeft_few": "Zostały {count} porcje",
"mealbox.portionsLeft_many": "Zostało {count} porcji",
"mealbox.portionsLeft_one": "Została 1 porcja",
"mealbox.portionsLeft_other": "Zostało {count} porcji",
"mealbox.title": "Pojemniki z jedzeniem",
"memory.delete": "Usuń",
"memory.deleteAll": "Usuń całą pamięć",
"memory.deleteAllTitle": "Usunąć całą pamięć?",
"memory.empty": "{brand} jeszcze niczego się o Tobie nie nauczył.",
"memory.irreversible": "Tego nie można cofnąć.",
"memory.origin.ai_inferred": "Przypuszczenie AI",
"memory.origin.observed": "Zaobserwowany wzorzec",
"memory.origin.user_stated": "Sam(a) powiedziałeś/-aś",
"memory.pause": "Wstrzymaj",
"memory.paused": "Wstrzymane",
"memory.resume": "Wznów",
"memory.subtitle": "Pełna przejrzystość. Poprawiaj, wstrzymuj lub usuwaj Twoja pamięć należy do Ciebie.",
"memory.title": "Co {brand} o mnie wie",
"memory.verified": "Zweryfikowane",
"memory.verify": "Zgadza się",
"myday.calories": "Kalorie",
"myday.carbs": "Węglowodany",
"myday.estimateNote": "Wartości z ~ to szacunki, które możesz poprawić.",
"myday.estimateRange": "{min}{max} kcal, prawdopodobnie {kcal}",
"myday.fat": "Tłuszcze",
"myday.fiber": "Błonnik",
"myday.kcalProtein": "{kcal} kcal · {protein} g białka",
"myday.logMeal": "Zarejestruj posiłek",
"myday.mealType.breakfast": "Śniadanie",
"myday.mealType.dessert": "Deser",
"myday.mealType.dinner": "Kolacja",
"myday.mealType.lunch": "Obiad",
"myday.mealType.snack": "Przekąska",
"myday.noMeals": "Dziś nie zarejestrowano posiłków.",
"myday.over": "{kcal} kcal ponad cel",
"myday.overRecommended": "Powyżej zaleceń",
"myday.protein": "Białko",
"myday.remaining": "Pozostało {kcal} kcal",
"myday.salt": "Sól",
"myday.targetsNote": "Cele to wskazówki, nie porada medyczna.",
"myday.title": "Mój dzień",
"myday.todaysMeals": "Dzisiejsze posiłki",
"onboarding.allergen.crustaceans": "Skorupiaki",
"onboarding.allergen.eggs": "Jaja",
"onboarding.allergen.fish": "Ryby",
"onboarding.allergen.gluten": "Gluten",
"onboarding.allergen.milk": "Mleko/laktoza",
"onboarding.allergen.peanuts": "Orzeszki ziemne",
"onboarding.allergen.sesame": "Sezam",
"onboarding.allergen.soy": "Soja",
"onboarding.allergen.tree_nuts": "Orzechy",
"onboarding.allergies": "Alergie i nietolerancje",
"onboarding.allergyNote": "Filtr alergii jest zawsze rygorystyczny dania z Twoimi alergenami nigdy się nie pojawią.",
"onboarding.birthYearPlaceholder": "Rok urodzenia",
"onboarding.bodyTitle": "Dla osobistych celów kalorycznych (opcjonalnie)",
"onboarding.diet": "Jak się odżywiasz?",
"onboarding.diet.flexitarian": "Fleksitarianin",
"onboarding.diet.omnivore": "Wszystkożerca",
"onboarding.diet.pescatarian": "Peskatarianin",
"onboarding.diet.vegan": "Weganin",
"onboarding.diet.vegetarian": "Wegetarianin",
"onboarding.goal": "Jaki jest Twój główny cel?",
"onboarding.goal.build_muscle": "Zbudować mięśnie",
"onboarding.goal.cook_more": "Częściej gotować w domu",
"onboarding.goal.less_waste": "Mniej marnować",
"onboarding.goal.lose_weight": "Schudnąć",
"onboarding.goal.lower_cost": "Wydawać mniej na jedzenie",
"onboarding.goal.maintain_weight": "Utrzymać wagę",
"onboarding.goal.more_protein": "Jeść więcej białka",
"onboarding.heightPlaceholder": "Wzrost (cm)",
"onboarding.household": "Twoje gospodarstwo",
"onboarding.householdCreate": "Utwórz gospodarstwo",
"onboarding.householdDefaultName": "Dom",
"onboarding.householdJoin": "Dołącz kodem",
"onboarding.householdName": "Nazwa gospodarstwa",
"onboarding.inviteCode": "Kod zaproszenia",
"onboarding.mode": "Jak dokładnie chcesz to robić?",
"onboarding.modeSimple": "Tryb prosty",
"onboarding.modeSimpleDesc": "Fotografuj jedzenie, akceptuj szacunki, minimum zachodu.",
"onboarding.modeExact": "Tryb dokładny",
"onboarding.modeExactDesc": "Waż jedzenie, wpisuj gramy, pełna kontrola nad liczbami.",
"onboarding.modesCombine": "Tryby można łączyć na co dzień prosto, dokładnie, gdy chcesz.",
"onboarding.modeSimple": "Tryb prosty",
"onboarding.modeSimpleDesc": "Fotografuj jedzenie, akceptuj szacunki, minimum zachodu.",
"onboarding.notMedical": "{brand} to wskazówki nie porada medyczna.",
"wte.title": "Co jemy?",
"wte.subtitle": "Na podstawie tego, co macie w domu, co trzeba wkrótce zużyć i co lubicie.",
"wte.cravingPlaceholder": "Mam ochotę na … (np. kremowe, azjatyckie, poniżej 500 kcal)",
"wte.mealBoxFirst": "Gotowe jedzenie w domu",
"wte.whyTitle": "Dlaczego ta propozycja?",
"wte.coverage": "{pct}% w domu",
"wte.missing": "Brakuje: {items}",
"wte.empty": "Brak propozycji dodaj jedzenie do spiżarni albo poluzuj filtry.",
"wte.refresh": "Nowe propozycje",
"scan.title": "Skanuj",
"scan.subtitle": "Uzupełnij spiżarnię lub zarejestruj posiłek aparatem.",
"scan.fridge": "Lodówka",
"scan.freezer": "Zamrażarka",
"scan.pantry": "Spiżarnia",
"scan.ingredients": "Składniki",
"scan.plate": "Talerz",
"scan.receipt": "Paragon",
"scan.barcode": "Kod kreskowy",
"scan.expiry": "Data przydatności",
"scan.nutrition": "Etykieta wartości odżywczych",
"scan.takePhoto": "Zrób zdjęcie",
"scan.tips.title": "Jak uzyskać najlepszą analizę",
"scan.tips.overview": "Zacznij od zdjęcia ogólnego",
"scan.tips.shelf": "Fotografuj półka po półce",
"scan.tips.light": "Unikaj ciemności i cieni",
"scan.tips.move": "Rozsuń zasłaniające się produkty",
"scan.analyzing": "Analizowanie …",
"scan.quotaLeft_one": "Został 1 skan AI w tym miesiącu",
"scan.quotaLeft_few": "Zostały {count} skany AI w tym miesiącu",
"scan.quotaLeft_many": "Zostało {count} skanów AI w tym miesiącu",
"scan.quotaLeft_other": "Zostało {count} skanu AI w tym miesiącu",
"scan.review.title": "Sprawdź wynik",
"scan.review.subtitle": "AI czasem się waha Ty decydujesz. Popraw, usuń lub dodaj przed zapisaniem.",
"scan.review.uncertain": "Niepewne sprawdź",
"scan.review.approveAll": "Zapisz do spiżarni",
"scan.review.rejected": "Usunięto",
"scan.failed": "Analiza się nie powiodła. Spróbuj ponownie lub dodaj ręcznie.",
"myday.title": "Mój dzień",
"myday.calories": "Kalorie",
"myday.protein": "Białko",
"myday.carbs": "Węglowodany",
"myday.fat": "Tłuszcze",
"myday.fiber": "Błonnik",
"myday.salt": "Sól",
"myday.remaining": "Pozostało {kcal} kcal",
"myday.over": "{kcal} kcal ponad cel",
"myday.logMeal": "Zarejestruj posiłek",
"myday.noMeals": "Dziś nie zarejestrowano posiłków.",
"myday.estimateNote": "Wartości z ~ to szacunki, które możesz poprawić.",
"myday.targetsNote": "Cele to wskazówki, nie porada medyczna.",
"home.title": "W domu",
"home.inventory": "Spiżarnia",
"home.useSoon": "Zużyj wkrótce",
"home.mealBoxes": "Pojemniki z jedzeniem",
"home.shopping": "Lista zakupów",
"home.budget": "Budżet na jedzenie",
"home.household": "Gospodarstwo",
"home.waste": "Marnowanie żywności",
"home.emptyInventory": "Spiżarnia jest pusta. Zeskanuj lodówkę albo dodaj produkty ręcznie.",
"home.expiresIn_one": "Został 1 dzień",
"home.expiresIn_few": "Zostały {days} dni",
"home.expiresIn_many": "Zostało {days} dni",
"home.expiresIn_other": "Zostało {days} dnia",
"home.expiresToday": "Kończy się dziś",
"home.expired": "Przeterminowane",
"home.pastBestBefore": "Po terminie przydatności najpierw powąchaj i spróbuj",
"recipe.portions_one": "1 porcja",
"onboarding.stepBSubtitle": "Dostosuj swój profil, aby uzyskać lepsze sugestie.",
"onboarding.stepBTitle": "Kontynuuj konfigurację",
"onboarding.stepCSubtitle": "Opcjonalne używane do osobistych celów kalorycznych.",
"onboarding.stepCTitle": "Twój profil zdrowia",
"onboarding.subtitle": "Wszystko jest opcjonalne i można to zmienić w każdej chwili. Im więcej uzupełnisz, tym lepsze propozycje.",
"onboarding.title": "Opowiedz nam coś o sobie",
"onboarding.weightPlaceholder": "Waga (kg)",
"paywall.choose": "Wybierz",
"paywall.fairUse": "Limit fair use skanów AI w cenie.",
"paywall.family": "Family · do 6 osób",
"paywall.freeNote": "Za darmo: 10 skanów AI/mies., ręczna spiżarnia, zapisane przepisy i proste rejestrowanie.",
"paywall.household": "Household · do 3 osób",
"paywall.large": "Large Household · do 12 osób",
"paywall.perMonth": "{price}/mies.",
"paywall.popular": "Najpopularniejszy",
"paywall.restore": "Przywróć zakupy",
"paywall.soonBody": "Zakupy będą aktywowane przez App Store/Google Play po włączeniu integracji ze sklepami (faza 7). Backend jest już gotowy.",
"paywall.soonTitle": "Wkrótce!",
"paywall.subtitle": "System operacyjny jedzenia dla całego gospodarstwa: nielimitowana spiżarnia, plan tygodnia i udostępnianie.",
"paywall.title": "{brand} Premium",
"paywall.trialActive_few": "Zostały {days} dni okresu próbnego",
"paywall.trialActive_many": "Zostało {days} dni okresu próbnego",
"paywall.trialActive_one": "Został 1 dzień okresu próbnego",
"paywall.trialActive_other": "Zostało {days} dnia okresu próbnego",
"profile.aiScansUsed": "Skany AI: {used} / {total} w tym miesiącu",
"profile.consent.anonymized_improvement": "Anonimowe ulepszanie AI",
"profile.consent.health_integration": "Dane zdrowotne (Apple Health / Health Connect)",
"profile.consent.image_training": "Moje zdjęcia mogą służyć do trenowania",
"profile.consent.location_weather": "Lokalizacja dla propozycji zależnych od pogody",
"profile.consent.personalization": "Funkcje osobiste (pamięć i profil smaku)",
"profile.consent.product_analytics": "Analiza produktu",
"profile.consent.push_notifications": "Powiadomienia push",
"profile.consents": "Zgody i dane",
"profile.consentsNote": "Zgody są rozdzielne funkcje osobiste nigdy nie wymagają zgody na trenowanie.",
"profile.deleteAccount": "Usuń konto",
"profile.deleteBody": "Wszystkie Twoje dane zostaną trwale usunięte zgodnie z RODO.",
"profile.deleteTitle": "Usunąć konto?",
"profile.emailUnverified": "Adres e-mail niepotwierdzony",
"profile.export": "Eksportuj moje dane",
"profile.goals": "Cele i dieta",
"profile.language": "Język",
"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": "Wyloguj się",
"profile.memory": "Co {brand} o mnie wie",
"profile.memorySubtitle": "Zobacz, popraw, wstrzymaj lub usuń to, czego nauczyła się platforma.",
"profile.modeLabel": "Tryb:",
"profile.resendVerification": "Wyślij ponownie e-mail potwierdzający",
"profile.subscription": "Subskrypcja",
"profile.title": "Profil",
"profile.verificationSent": "Wysłano! Sprawdź skrzynkę.",
"recipe.avgRating": "Średnia {avg} z {count} ocen",
"recipe.cook": "Gotuj teraz",
"recipe.cost": "ok. {amount}/porcja",
"recipe.iCookedThis": "Ugotowałem/-am to",
"recipe.ingredients": "Składniki",
"recipe.notSafe": "Nie pasuje do Twoich ustawień żywieniowych",
"recipe.perPortion": "na porcję",
"recipe.portions_few": "{count} porcje",
"recipe.portions_many": "{count} porcji",
"recipe.portions_one": "1 porcja",
"recipe.portions_other": "{count} porcji",
"recipe.time": "{min} min",
"recipe.perPortion": "na porcję",
"recipe.ingredients": "Składniki",
"recipe.save": "Zapisz",
"recipe.saved": "Zapisany",
"recipe.steps": "Przygotowanie",
"recipe.cook": "Gotuj teraz",
"recipe.notSafe": "Nie pasuje do Twoich ustawień żywieniowych",
"recipe.substitutions": "Zamień",
"recipe.iCookedThis": "Ugotowałem/-am to",
"recipe.cost": "ok. {amount}/porcja",
"cooking.step": "Krok {current} z {total}",
"cooking.timer": "Uruchom minutnik",
"cooking.timerRunning": "Pozostało {time}",
"cooking.finish": "Gotowe zarejestruj posiłek",
"cooking.scale": "Porcje",
"cooking.keepAwake": "Ekran pozostaje włączony podczas gotowania",
"cooked.title": "Jak poszło?",
"cooked.portionsCooked": "Ugotowane porcje",
"cooked.whoAte": "Kto jadł?",
"cooked.mealBoxes": "Porcje do pojemników",
"cooked.deductPantry": "Odejmij składniki ze spiżarni",
"cooked.rate": "Oceń",
"shopping.title": "Lista zakupów",
"recipe.time": "{min} min",
"scan.analyzing": "Analizowanie …",
"scan.barcode": "Kod kreskowy",
"scan.expiry": "Data przydatności",
"scan.failed": "Analiza się nie powiodła. Spróbuj ponownie lub dodaj ręcznie.",
"scan.freezer": "Zamrażarka",
"scan.fridge": "Lodówka",
"scan.ingredients": "Składniki",
"scan.nutrition": "Etykieta wartości odżywczych",
"scan.pantry": "Spiżarnia",
"scan.plate": "Talerz",
"scan.quotaLeft_few": "Zostały {count} skany AI w tym miesiącu",
"scan.quotaLeft_many": "Zostało {count} skanów AI w tym miesiącu",
"scan.quotaLeft_one": "Został 1 skan AI w tym miesiącu",
"scan.quotaLeft_other": "Zostało {count} skanu AI w tym miesiącu",
"scan.receipt": "Paragon",
"scan.review.addItem": "+ Dodaj produkt",
"scan.review.approveAll": "Zapisz do spiżarni",
"scan.review.bestBeforeNote": "Data jakości produkt często jest dobry dłużej. Powąchaj i spróbuj, zanim wyrzucisz.",
"scan.review.dateKindBestBefore": "Najlepiej spożyć przed",
"scan.review.dateKindUseBy": "Należy spożyć do",
"scan.review.datePlaceholder": "RRRR-MM-DD (opcjonalnie)",
"scan.review.itemPlaceholder": "Produkt",
"scan.review.quantityPlaceholder": "Ilość",
"scan.review.rejected": "Usunięto",
"scan.review.subtitle": "AI czasem się waha Ty decydujesz. Popraw, usuń lub dodaj przed zapisaniem.",
"scan.review.title": "Sprawdź wynik",
"scan.review.uncertain": "Niepewne sprawdź",
"scan.review.unitPlaceholder": "Jednostka (g, l, szt. …)",
"scan.review.unknownItem": "Nieznany produkt",
"scan.review.useByNote": "Data bezpieczeństwa nie jedz po tej dacie.",
"scan.subtitle": "Uzupełnij spiżarnię lub zarejestruj posiłek aparatem.",
"scan.takePhoto": "Zrób zdjęcie",
"scan.tips.light": "Unikaj ciemności i cieni",
"scan.tips.move": "Rozsuń zasłaniające się produkty",
"scan.tips.overview": "Zacznij od zdjęcia ogólnego",
"scan.tips.shelf": "Fotografuj półka po półce",
"scan.tips.title": "Jak uzyskać najlepszą analizę",
"scan.title": "Skanuj",
"shopping.addPlaceholder": "Dodaj produkt …",
"shopping.complete": "Zakończ zakupy",
"shopping.completedBody_few": "{count} produkty trafiły do spiżarni.",
"shopping.completedBody_many": "{count} produktów trafiło do spiżarni.",
"shopping.completedBody_one": "1 produkt trafił do spiżarni.",
"shopping.completedBody_other": "{count} produktu trafiło do spiżarni.",
"shopping.completedTitle": "Gotowe!",
"shopping.completeNote": "Odhaczone produkty trafiają do spiżarni.",
"shopping.empty": "Lista jest pusta.",
"shopping.estimated": "ok. {amount}",
"shopping.estimatedTotal": "Szacowana suma: ok. {amount}",
"mealbox.title": "Pojemniki z jedzeniem",
"mealbox.eatBy": "Zjedz do {date}",
"mealbox.portionsLeft_one": "Została 1 porcja",
"mealbox.portionsLeft_few": "Zostały {count} porcje",
"mealbox.portionsLeft_many": "Zostało {count} porcji",
"mealbox.portionsLeft_other": "Zostało {count} porcji",
"mealbox.eat": "Zjedz teraz",
"mealbox.empty": "Brak pojemników. Gotując, możesz zapisać tu porcje.",
"household.members": "Członkowie",
"household.invite": "Zaproś kodem: {code}",
"household.shared": "Wspólne w gospodarstwie: spiżarnia, lista zakupów, plan tygodnia, pojemniki i budżet.",
"household.private": "Prywatne dla każdej osoby: cele, alergie, dane zdrowotne i historia posiłków.",
"memory.title": "Co {brand} o mnie wie",
"memory.subtitle": "Pełna przejrzystość. Poprawiaj, wstrzymuj lub usuwaj Twoja pamięć należy do Ciebie.",
"memory.paused": "Wstrzymane",
"memory.verify": "Zgadza się",
"memory.pause": "Wstrzymaj",
"memory.delete": "Usuń",
"memory.deleteAll": "Usuń całą pamięć",
"memory.empty": "{brand} jeszcze niczego się o Tobie nie nauczył.",
"memory.origin.user_stated": "Sam(a) powiedziałeś/-aś",
"memory.origin.observed": "Zaobserwowany wzorzec",
"memory.origin.ai_inferred": "Przypuszczenie AI",
"paywall.title": "{brand} Premium",
"paywall.subtitle": "System operacyjny jedzenia dla całego gospodarstwa: nielimitowana spiżarnia, plan tygodnia i udostępnianie.",
"paywall.trialActive_one": "Został 1 dzień okresu próbnego",
"paywall.trialActive_few": "Zostały {days} dni okresu próbnego",
"paywall.trialActive_many": "Zostało {days} dni okresu próbnego",
"paywall.trialActive_other": "Zostało {days} dnia okresu próbnego",
"paywall.household": "Household · do 3 osób",
"paywall.family": "Family · do 6 osób",
"paywall.large": "Large Household · do 12 osób",
"paywall.perMonth": "{price}/mies.",
"paywall.fairUse": "Limit fair use skanów AI w cenie.",
"paywall.restore": "Przywróć zakupy",
"paywall.freeNote": "Za darmo: 10 skanów AI/mies., ręczna spiżarnia, zapisane przepisy i proste rejestrowanie.",
"profile.title": "Profil",
"profile.goals": "Cele i dieta",
"profile.consents": "Zgody i dane",
"profile.memory": "Co {brand} o mnie wie",
"profile.subscription": "Subskrypcja",
"profile.logout": "Wyloguj się",
"profile.export": "Eksportuj moje dane",
"profile.deleteAccount": "Usuń konto",
"profile.language": "Język",
"profile.emailUnverified": "Adres e-mail niepotwierdzony",
"profile.resendVerification": "Wyślij ponownie e-mail potwierdzający",
"profile.verificationSent": "Wysłano! Sprawdź skrzynkę.",
"profile.language.sv": "Svenska",
"profile.language.en": "English",
"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": "Ups",
"common.undo": "Cofnij",
"common.remove": "Usuń",
"common.add": "Dodaj",
"common.on": "Wł.",
"common.off": "Wył.",
"home.thisWeek": "W tym tygodniu",
"home.wasteWeek": "Marnowanie w tym tygodniu",
"home.useSoonHint": "„Najlepiej spożyć przed” dotyczy jakości powąchaj, obejrzyj i spróbuj, zanim wyrzucisz. Terminu „należy spożyć do” należy natomiast przestrzegać.",
"scan.review.itemPlaceholder": "Produkt",
"scan.review.quantityPlaceholder": "Ilość",
"scan.review.unitPlaceholder": "Jednostka (g, l, szt. …)",
"scan.review.datePlaceholder": "RRRR-MM-DD (opcjonalnie)",
"scan.review.dateKindBestBefore": "Najlepiej spożyć przed",
"scan.review.dateKindUseBy": "Należy spożyć do",
"scan.review.bestBeforeNote": "Data jakości produkt często jest dobry dłużej. Powąchaj i spróbuj, zanim wyrzucisz.",
"scan.review.useByNote": "Data bezpieczeństwa nie jedz po tej dacie.",
"scan.review.addItem": "+ Dodaj produkt",
"scan.review.unknownItem": "Nieznany produkt",
"barcode.aim": "Skieruj aparat na kod kreskowy",
"barcode.addPrompt": "Dodać do spiżarni?",
"barcode.kcalPer100": "{kcal} kcal/100 g",
"barcode.noNutrition": "Brak danych żywieniowych",
"barcode.unknownTitle": "Nieznany produkt",
"barcode.unknownBody": "Tego produktu nie ma jeszcze w bazie. Sfotografuj przód i etykietę wartości odżywczych, a my go dodamy.",
"barcode.photoPackage": "Sfotografuj opakowanie",
"barcode.needHousehold": "Najpierw potrzebujesz gospodarstwa.",
"barcode.noLocation": "Nie znaleziono miejsca przechowywania.",
"barcode.cameraTitle": "Potrzebny aparat",
"barcode.cameraBody": "{brand} potrzebuje aparatu do skanowania kodów kreskowych.",
"barcode.allowCamera": "Zezwól na aparat",
"onboarding.goal.lose_weight": "Schudnąć",
"onboarding.goal.build_muscle": "Zbudować mięśnie",
"onboarding.goal.maintain_weight": "Utrzymać wagę",
"onboarding.goal.more_protein": "Jeść więcej białka",
"onboarding.goal.less_waste": "Mniej marnować",
"onboarding.goal.lower_cost": "Wydawać mniej na jedzenie",
"onboarding.goal.cook_more": "Częściej gotować w domu",
"onboarding.diet.omnivore": "Wszystkożerca",
"onboarding.diet.flexitarian": "Fleksitarianin",
"onboarding.diet.pescatarian": "Peskatarianin",
"onboarding.diet.vegetarian": "Wegetarianin",
"onboarding.diet.vegan": "Weganin",
"onboarding.allergen.gluten": "Gluten",
"onboarding.allergen.milk": "Mleko/laktoza",
"onboarding.allergen.eggs": "Jaja",
"onboarding.allergen.tree_nuts": "Orzechy",
"onboarding.allergen.peanuts": "Orzeszki ziemne",
"onboarding.allergen.fish": "Ryby",
"onboarding.allergen.crustaceans": "Skorupiaki",
"onboarding.allergen.soy": "Soja",
"onboarding.allergen.sesame": "Sezam",
"onboarding.bodyTitle": "Dla osobistych celów kalorycznych (opcjonalnie)",
"onboarding.weightPlaceholder": "Waga (kg)",
"onboarding.heightPlaceholder": "Wzrost (cm)",
"onboarding.birthYearPlaceholder": "Rok urodzenia",
"onboarding.householdDefaultName": "Dom",
"onboarding.modesCombine": "Tryby można łączyć na co dzień prosto, dokładnie, gdy chcesz.",
"myday.mealType.breakfast": "Śniadanie",
"myday.mealType.lunch": "Obiad",
"myday.mealType.dinner": "Kolacja",
"myday.mealType.snack": "Przekąska",
"myday.mealType.dessert": "Deser",
"myday.overRecommended": "Powyżej zaleceń",
"myday.todaysMeals": "Dzisiejsze posiłki",
"myday.estimateRange": "{min}{max} kcal, prawdopodobnie {kcal}",
"myday.kcalProtein": "{kcal} kcal · {protein} g białka",
"logmeal.mealTypeTitle": "Rodzaj posiłku",
"logmeal.quickTitle": "Szybkie ponowne dodanie",
"logmeal.quickSubtitle": "Zapisuje te same wartości odżywcze co ostatnio.",
"logmeal.manualTitle": "Ręcznie",
"logmeal.whatPlaceholder": "Co zjadłeś/aś?",
"logmeal.proteinPlaceholder": "białko (g)",
"logmeal.validationTitle": "Uzupełnij",
"logmeal.validationBody": "Do ręcznego dodania potrzebne są nazwa i kalorie.",
"logmeal.photoTip": "Wskazówka: sfotografuj talerz w zakładce Skanuj, a aplikacja oszacuje za Ciebie zawsze potwierdzasz.",
"shopping.section.frukt_gront": "Owoce i warzywa",
"shopping.section.brod": "Pieczywo",
"shopping.section.mejeri": "Nabiał",
"shopping.section.kott_fagel": "Mięso i drób",
"shopping.section.fisk": "Ryby",
"shopping.section.chark": "Wędliny",
"shopping.section.frys": "Mrożonki",
"shopping.section.skafferi": "Spiżarnia",
"shopping.section.konserver": "Konserwy",
"shopping.section.kryddor_bak": "Przyprawy i pieczenie",
"shopping.section.dryck": "Napoje",
"shopping.section.snacks": "Przekąski",
"shopping.section.fisk": "Ryby",
"shopping.section.frukt_gront": "Owoce i warzywa",
"shopping.section.frys": "Mrożonki",
"shopping.section.hygien_ovrigt": "Inne",
"shopping.completedTitle": "Gotowe!",
"household.role.owner": "Właściciel",
"household.role.adult": "Dorosły",
"household.role.member": "Członek",
"household.role.child": "Dziecko",
"household.empty": "Nie masz jeszcze gospodarstwa. Utwórz je podczas konfiguracji lub dołącz kodem.",
"household.shareCode": "Udostępnij kod rodzinie, aby dzielić spiżarnię, listę i plan.",
"household.locations": "Miejsca przechowywania",
"household.portionFactor": "×{factor} porcji",
"mealbox.enjoyTitle": "Smacznego!",
"mealbox.enjoyBody": "Porcja została zapisana w Mój dzień.",
"mealbox.guidanceNote": "Zalecany termin to wskazówka zaufaj węchowi i smakowi.",
"paywall.soonTitle": "Wkrótce!",
"paywall.soonBody": "Zakupy będą aktywowane przez App Store/Google Play po włączeniu integracji ze sklepami (faza 7). Backend jest już gotowy.",
"paywall.popular": "Najpopularniejszy",
"paywall.choose": "Wybierz",
"recipe.saved": "Zapisany",
"recipe.save": "Zapisz",
"recipe.avgRating": "Średnia {avg} z {count} ocen",
"memory.verified": "Zweryfikowane",
"memory.resume": "Wznów",
"memory.deleteAllTitle": "Usunąć całą pamięć?",
"memory.irreversible": "Tego nie można cofnąć.",
"profile.consent.personalization": "Funkcje osobiste (pamięć i profil smaku)",
"profile.consent.anonymized_improvement": "Anonimowe ulepszanie AI",
"profile.consent.image_training": "Moje zdjęcia mogą służyć do trenowania",
"profile.consent.health_integration": "Dane zdrowotne (Apple Health / Health Connect)",
"profile.consent.location_weather": "Lokalizacja dla propozycji zależnych od pogody",
"profile.consent.push_notifications": "Powiadomienia push",
"profile.modeLabel": "Tryb:",
"profile.memorySubtitle": "Zobacz, popraw, wstrzymaj lub usuń to, czego nauczyła się platforma.",
"profile.consentsNote": "Zgody są rozdzielne funkcje osobiste nigdy nie wymagają zgody na trenowanie.",
"profile.aiScansUsed": "Skany AI: {used} / {total} w tym miesiącu",
"profile.deleteTitle": "Usunąć konto?",
"profile.deleteBody": "Wszystkie Twoje dane zostaną trwale usunięte zgodnie z RODO.",
"cooking.timerDone": "Gotowe!",
"cooked.deductPantryNote": "Składniki są odejmowane ze spiżarni najpierw te z najkrótszym terminem.",
"home.moreItems_one": "+ jeszcze 1 …",
"home.moreItems_few": "+ jeszcze {count} …",
"home.moreItems_many": "+ jeszcze {count} …",
"home.moreItems_other": "+ jeszcze {count} …",
"shopping.completedBody_one": "1 produkt trafił do spiżarni.",
"shopping.completedBody_few": "{count} produkty trafiły do spiżarni.",
"shopping.completedBody_many": "{count} produktów trafiło do spiżarni.",
"shopping.completedBody_other": "{count} produktu trafiło do spiżarni.",
"profile.consent.product_analytics": "Analiza produktu"
"shopping.section.konserver": "Konserwy",
"shopping.section.kott_fagel": "Mięso i drób",
"shopping.section.kryddor_bak": "Przyprawy i pieczenie",
"shopping.section.mejeri": "Nabiał",
"shopping.section.skafferi": "Spiżarnia",
"shopping.section.snacks": "Przekąski",
"shopping.title": "Lista zakupów",
"tabs.home": "W domu",
"tabs.myDay": "Mój dzień",
"tabs.scan": "Skanuj",
"tabs.whatToEat": "Co jemy?",
"wte.coverage": "{pct}% w domu",
"wte.cravingPlaceholder": "Mam ochotę na … (np. kremowe, azjatyckie, poniżej 500 kcal)",
"wte.empty": "Brak propozycji dodaj jedzenie do spiżarni albo poluzuj filtry.",
"wte.mealBoxFirst": "Gotowe jedzenie w domu",
"wte.missing": "Brakuje: {items}",
"wte.refresh": "Nowe propozycje",
"wte.subtitle": "Na podstawie tego, co macie w domu, co trzeba wkrótce zużyć i co lubicie.",
"wte.title": "Co jemy?",
"wte.whyTitle": "Dlaczego ta propozycja?"
}