Files
food-ai/client/lib/l10n/app_de.arb
dbastrikin 180c741424 feat: dish recognition UX, background mode, and backend bug fixes
Flutter client:
- Progress dialog: redesigned with pulsing animated icon, info hint about
  background mode, full-width Minimize button; dismiss signal via ValueNotifier
  so the dialog always closes regardless of widget lifecycle
- Background recognition: when user taps Minimize, wasMinimizedByUser flag is
  set; on completion a snackbar is shown instead of opening DishResultSheet
  directly; snackbar action opens the sheet on demand
- Fix dialog spinning forever: finally block guarantees dismissSignal=true on
  all exit paths including early returns from context.mounted checks
- Fix DishResultSheet not appearing: add ValueKey to _DailyMealsSection and
  meal card Padding so Flutter reuses elements when _TodayJobsWidget is
  inserted/removed from the SliverChildListDelegate list
- todayJobsProvider refresh: added refresh() method; called after job submit
  and on DishJobDone; all ref.read() calls guarded with context.mounted checks
- food_search_sheet: scan buttons replaced with full-width stacked OutlinedButtons
- app.dart: WidgetsBindingObserver refreshes scan providers on app resume
- L10n: added dishRecognitionHint and minimize keys to all 12 locales

Backend:
- migrations/003: ALTER TYPE recipe_source ADD VALUE 'recommendation' to fix
  22P02 error in GET /home/summary -> getRecommendations()
- item_enricher: normalizeProductCategory() validates AI-returned category
  against known slugs, falls back to "other" — fixes products_category_fkey
  FK violation during receipt recognition
- recognition prompt: enumerate valid categories so AI returns correct values

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 00:03:17 +02:00

238 lines
8.1 KiB
Plaintext

{
"@@locale": "de",
"appTitle": "FoodAI",
"greetingMorning": "Guten Morgen",
"greetingAfternoon": "Guten Tag",
"greetingEvening": "Guten Abend",
"caloriesUnit": "kcal",
"gramsUnit": "g",
"goalLabel": "Ziel:",
"consumed": "Verzehrt",
"remaining": "Verbleibend",
"exceeded": "Überschritten",
"proteinLabel": "Protein",
"fatLabel": "Fett",
"carbsLabel": "Kohlenhydrate",
"today": "Heute",
"yesterday": "Gestern",
"mealsSection": "Mahlzeiten",
"addDish": "Gericht hinzufügen",
"scanDish": "Scannen",
"menu": "Menü",
"dishHistory": "Gerichtverlauf",
"recommendCook": "Wir empfehlen zu kochen",
"camera": "Kamera",
"gallery": "Galerie",
"analyzingPhoto": "Foto wird analysiert...",
"inQueue": "Sie sind in der Warteschlange",
"queuePosition": "Position {position}",
"@queuePosition": {
"placeholders": {
"position": {
"type": "int"
}
}
},
"processing": "Verarbeitung...",
"upgradePrompt": "Warteschlange überspringen? Upgrade →",
"recognitionFailed": "Erkennung fehlgeschlagen. Erneut versuchen.",
"dishRecognition": "Gerichterkennung",
"all": "Alle",
"dishRecognized": "Gericht erkannt",
"recognizing": "Wird erkannt…",
"recognitionError": "Erkennungsfehler",
"dishResultTitle": "Gericht erkannt",
"selectDish": "Gericht auswählen",
"dishNotRecognized": "Gericht nicht erkannt",
"tryAgain": "Erneut versuchen",
"nutritionApproximate": "Nährwerte sind ungefähr — aus dem Foto geschätzt.",
"portion": "Portion",
"mealType": "Mahlzeittyp",
"dateLabel": "Datum",
"addToJournal": "Zum Tagebuch hinzufügen",
"addFailed": "Hinzufügen fehlgeschlagen. Erneut versuchen.",
"historyTitle": "Erkennungsverlauf",
"historyLoadError": "Verlauf konnte nicht geladen werden",
"retry": "Wiederholen",
"noHistory": "Noch keine Erkennungen",
"profileTitle": "Profil",
"edit": "Bearbeiten",
"bodyParams": "KÖRPERPARAMETER",
"goalActivity": "ZIEL & AKTIVITÄT",
"nutrition": "ERNÄHRUNG",
"settings": "EINSTELLUNGEN",
"height": "Größe",
"weight": "Gewicht",
"age": "Alter",
"gender": "Geschlecht",
"genderMale": "Männlich",
"genderFemale": "Weiblich",
"goalLoss": "Gewichtsverlust",
"goalMaintain": "Gewicht halten",
"goalGain": "Muskelaufbau",
"activityLow": "Niedrig",
"activityMedium": "Mittel",
"activityHigh": "Hoch",
"calorieGoal": "Kalorienziel",
"mealTypes": "Mahlzeittypen",
"formulaNote": "Berechnet mit der Mifflin-St Jeor Formel",
"language": "Sprache",
"notSet": "Nicht festgelegt",
"calorieHint": "Körperparameter eingeben, um das Kalorienziel zu berechnen",
"logout": "Abmelden",
"editProfile": "Profil bearbeiten",
"cancel": "Abbrechen",
"save": "Speichern",
"nameLabel": "Name",
"heightCm": "Größe (cm)",
"weightKg": "Gewicht (kg)",
"birthDate": "Geburtsdatum",
"nameRequired": "Name eingeben",
"profileUpdated": "Profil aktualisiert",
"profileSaveFailed": "Speichern fehlgeschlagen",
"mealTypeBreakfast": "Frühstück",
"mealTypeSecondBreakfast": "Zweites Frühstück",
"mealTypeLunch": "Mittagessen",
"mealTypeAfternoonSnack": "Nachmittagssnack",
"mealTypeDinner": "Abendessen",
"mealTypeSnack": "Snack",
"navHome": "Startseite",
"navProducts": "Produkte",
"navRecipes": "Rezepte",
"addFromReceiptOrPhoto": "Aus Kassenbon oder Foto hinzufügen",
"scanScreenTitle": "Scannen & Erkennen",
"barcodeScanSubtitle": "Produkt per Barcode finden",
"chooseMethod": "Methode wählen",
"photoReceipt": "Kassenbon fotografieren",
"photoReceiptSubtitle": "Alle Produkte vom Kassenbon erkennen",
"photoProducts": "Produkte fotografieren",
"photoProductsSubtitle": "Kühlschrank, Tisch, Regal — bis zu 3 Fotos",
"addPackagedFood": "Verpacktes Lebensmittel hinzufügen",
"scanBarcode": "Barcode scannen",
"portionWeightG": "Portionsgewicht (g)",
"productNotFound": "Produkt nicht gefunden",
"enterManually": "Manuell eingeben",
"perHundredG": "pro 100 g",
"searchFoodHint": "Produkte und Gerichte suchen...",
"recentlyUsedLabel": "Zuletzt verwendet",
"productsSection": "Produkte",
"dishesSection": "Gerichte",
"noResultsForQuery": "Keine Ergebnisse für \"{query}\"",
"@noResultsForQuery": {
"placeholders": {
"query": {
"type": "String"
}
}
},
"servingsLabel": "Portionen",
"addToDiary": "Zum Tagebuch hinzufügen",
"scanDishPhoto": "Foto scannen",
"planningForDate": "",
"@planningForDate": {
"placeholders": {
"date": {
"type": "String"
}
}
},
"markAsEaten": "Als gegessen markieren",
"plannedMealLabel": "Geplant",
"generateWeekLabel": "Woche planen",
"generateWeekSubtitle": "KI erstellt einen Menüplan mit Frühstück, Mittagessen und Abendessen für die ganze Woche",
"generatingMenu": "Menü wird erstellt...",
"dayPlannedLabel": "Tag geplant",
"planMenuButton": "Mahlzeiten planen",
"planMenuTitle": "Was planen?",
"planOptionSingleMeal": "Einzelne Mahlzeit",
"planOptionSingleMealDesc": "Tag und Mahlzeittyp wählen",
"planOptionDay": "Ein Tag",
"planOptionDayDesc": "Alle Mahlzeiten für einen Tag",
"planOptionDays": "Mehrere Tage",
"planOptionDaysDesc": "Zeitraum anpassen",
"planOptionWeek": "Eine Woche",
"planOptionWeekDesc": "7 Tage auf einmal",
"planSelectDate": "Datum wählen",
"planSelectMealType": "Mahlzeittyp",
"planSelectRange": "Zeitraum wählen",
"planGenerateButton": "Planen",
"planGenerating": "Plan wird erstellt…",
"planSuccess": "Menü geplant!",
"planProductsTitle": "Zutaten für den Speiseplan",
"planProductsSubtitle": "Die KI berücksichtigt die ausgewählten Produkte bei der Rezeptgenerierung",
"planProductsEmpty": "Keine Produkte hinzugefügt",
"planProductsEmptyMessage": "Füge Produkte hinzu, die du zu Hause hast — die KI schlägt Rezepte aus deinen Vorräten vor",
"planProductsAddProducts": "Produkte hinzufügen",
"planProductsContinue": "Weiter",
"planProductsSkip": "Produktauswahl überspringen",
"planProductsSkipNoProducts": "Ohne Produkte planen",
"planProductsSelectAll": "Alle auswählen",
"planProductsDeselectAll": "Alle abwählen",
"recentScans": "Letzte Scans",
"seeAllScans": "Alle",
"productJobHistoryTitle": "Scan-Verlauf",
"jobTypeReceipt": "Kassenbon",
"jobTypeProducts": "Produkte",
"scanSubmitting": "Wird gesendet...",
"processingProducts": "Verarbeitung...",
"clearAllProducts": "Alles löschen",
"clearAllConfirmTitle": "Alle Produkte löschen?",
"clearAllConfirmMessage": "Alle Produkte werden dauerhaft gelöscht.",
"addManually": "Manuell",
"scan": "Scannen",
"addProduct": "Hinzufügen",
"searchProducts": "Produkte suchen",
"searchProductsHint": "Produktname eingeben oder manuell hinzufügen",
"noSearchResults": "Keine Ergebnisse für \"{query}\"",
"@noSearchResults": {
"placeholders": {
"query": {
"type": "String"
}
}
},
"quantity": "Menge",
"storageDays": "Lagertage",
"addToShelf": "Zum Vorrat hinzufügen",
"errorGeneric": "Etwas ist schiefgelaufen",
"nutritionOptional": "Nährwerte pro 100g (optional)",
"calories": "Kalorien",
"protein": "Eiweiß",
"fat": "Fett",
"carbs": "Kohlenhydrate",
"fiber": "Ballaststoffe",
"productAddedToShelf": "Zum Vorrat hinzugefügt",
"recognitionFoundProducts": "{count} Produkte gefunden",
"@recognitionFoundProducts": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"recognitionAddAll": "Alle hinzufügen",
"recognitionAddToStock": "In Vorrat",
"recognitionAdded": "{count} Produkte hinzugefügt",
"@recognitionAdded": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"recognitionProductsFailed": "Hinzufügen fehlgeschlagen",
"recognitionEmpty": "Keine Produkte gefunden",
"recognitionConfidence": "{percent}% Sicherheit",
"@recognitionConfidence": {
"placeholders": {
"percent": {
"type": "int"
}
}
},
"recognitionReplaceProduct": "Produkt ersetzen",
"scanJobCloseHint": "Du kannst die App schließen — dieser Scan erscheint in Letzte Scans auf dem Produktbildschirm",
"minimize": "Minimieren",
"dishRecognitionHint": "Du kannst minimieren — das Ergebnis erscheint auf dem Startbildschirm, wenn fertig"
}