Files
food-ai/client/lib/l10n/app_localizations.dart
dbastrikin 5096df2102 fix: fix menu generation errors and show planned meals on home screen
Backend fixes:
- migration 003: add 'menu' value to recipe_source enum (was causing SQLSTATE 22P02)
- migration 004: rename recipe_products→recipe_ingredients, product_id→ingredient_id (was causing SQLSTATE 42P01)
- dish/repository.go: fix INSERT INTO tags using $1/$1 for two columns → $1/$2 (was causing SQLSTATE 42P08)
- home/handler.go: replace non-existent saved_recipes table with correct joins (recipes→dishes→dish_translations, user_saved_recipes) so today's plan and recommendations load correctly
- reqlog: new slog.Handler wrapper that adds request_id and stack trace to ERROR-level logs
- all handlers: slog.Error→slog.ErrorContext so error logs include request context; writeError includes request_id in response body

Client:
- home_screen.dart: extend home screen to future dates, show planned meals as ghost entries
- l10n: add new localisation keys for home screen date navigation and planned meal UI

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

901 lines
23 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:intl/intl.dart' as intl;
import 'app_localizations_ar.dart';
import 'app_localizations_de.dart';
import 'app_localizations_en.dart';
import 'app_localizations_es.dart';
import 'app_localizations_fr.dart';
import 'app_localizations_hi.dart';
import 'app_localizations_it.dart';
import 'app_localizations_ja.dart';
import 'app_localizations_ko.dart';
import 'app_localizations_pt.dart';
import 'app_localizations_ru.dart';
import 'app_localizations_zh.dart';
// ignore_for_file: type=lint
/// Callers can lookup localized strings with an instance of AppLocalizations
/// returned by `AppLocalizations.of(context)`.
///
/// Applications need to include `AppLocalizations.delegate()` in their app's
/// `localizationDelegates` list, and the locales they support in the app's
/// `supportedLocales` list. For example:
///
/// ```dart
/// import 'l10n/app_localizations.dart';
///
/// return MaterialApp(
/// localizationsDelegates: AppLocalizations.localizationsDelegates,
/// supportedLocales: AppLocalizations.supportedLocales,
/// home: MyApplicationHome(),
/// );
/// ```
///
/// ## Update pubspec.yaml
///
/// Please make sure to update your pubspec.yaml to include the following
/// packages:
///
/// ```yaml
/// dependencies:
/// # Internationalization support.
/// flutter_localizations:
/// sdk: flutter
/// intl: any # Use the pinned version from flutter_localizations
///
/// # Rest of dependencies
/// ```
///
/// ## iOS Applications
///
/// iOS applications define key application metadata, including supported
/// locales, in an Info.plist file that is built into the application bundle.
/// To configure the locales supported by your app, youll need to edit this
/// file.
///
/// First, open your projects ios/Runner.xcworkspace Xcode workspace file.
/// Then, in the Project Navigator, open the Info.plist file under the Runner
/// projects Runner folder.
///
/// Next, select the Information Property List item, select Add Item from the
/// Editor menu, then select Localizations from the pop-up menu.
///
/// Select and expand the newly-created Localizations item then, for each
/// locale your application supports, add a new item and select the locale
/// you wish to add from the pop-up menu in the Value field. This list should
/// be consistent with the languages listed in the AppLocalizations.supportedLocales
/// property.
abstract class AppLocalizations {
AppLocalizations(String locale)
: localeName = intl.Intl.canonicalizedLocale(locale.toString());
final String localeName;
static AppLocalizations? of(BuildContext context) {
return Localizations.of<AppLocalizations>(context, AppLocalizations);
}
static const LocalizationsDelegate<AppLocalizations> delegate =
_AppLocalizationsDelegate();
/// A list of this localizations delegate along with the default localizations
/// delegates.
///
/// Returns a list of localizations delegates containing this delegate along with
/// GlobalMaterialLocalizations.delegate, GlobalCupertinoLocalizations.delegate,
/// and GlobalWidgetsLocalizations.delegate.
///
/// Additional delegates can be added by appending to this list in
/// MaterialApp. This list does not have to be used at all if a custom list
/// of delegates is preferred or required.
static const List<LocalizationsDelegate<dynamic>> localizationsDelegates =
<LocalizationsDelegate<dynamic>>[
delegate,
GlobalMaterialLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
];
/// A list of this localizations delegate's supported locales.
static const List<Locale> supportedLocales = <Locale>[
Locale('ar'),
Locale('de'),
Locale('en'),
Locale('es'),
Locale('fr'),
Locale('hi'),
Locale('it'),
Locale('ja'),
Locale('ko'),
Locale('pt'),
Locale('ru'),
Locale('zh'),
];
/// No description provided for @appTitle.
///
/// In en, this message translates to:
/// **'FoodAI'**
String get appTitle;
/// No description provided for @greetingMorning.
///
/// In en, this message translates to:
/// **'Good morning'**
String get greetingMorning;
/// No description provided for @greetingAfternoon.
///
/// In en, this message translates to:
/// **'Good afternoon'**
String get greetingAfternoon;
/// No description provided for @greetingEvening.
///
/// In en, this message translates to:
/// **'Good evening'**
String get greetingEvening;
/// No description provided for @caloriesUnit.
///
/// In en, this message translates to:
/// **'kcal'**
String get caloriesUnit;
/// No description provided for @gramsUnit.
///
/// In en, this message translates to:
/// **'g'**
String get gramsUnit;
/// No description provided for @goalLabel.
///
/// In en, this message translates to:
/// **'goal:'**
String get goalLabel;
/// No description provided for @consumed.
///
/// In en, this message translates to:
/// **'Consumed'**
String get consumed;
/// No description provided for @remaining.
///
/// In en, this message translates to:
/// **'Remaining'**
String get remaining;
/// No description provided for @exceeded.
///
/// In en, this message translates to:
/// **'Exceeded'**
String get exceeded;
/// No description provided for @proteinLabel.
///
/// In en, this message translates to:
/// **'Protein'**
String get proteinLabel;
/// No description provided for @fatLabel.
///
/// In en, this message translates to:
/// **'Fat'**
String get fatLabel;
/// No description provided for @carbsLabel.
///
/// In en, this message translates to:
/// **'Carbs'**
String get carbsLabel;
/// No description provided for @today.
///
/// In en, this message translates to:
/// **'Today'**
String get today;
/// No description provided for @yesterday.
///
/// In en, this message translates to:
/// **'Yesterday'**
String get yesterday;
/// No description provided for @mealsSection.
///
/// In en, this message translates to:
/// **'Meals'**
String get mealsSection;
/// No description provided for @addDish.
///
/// In en, this message translates to:
/// **'Add dish'**
String get addDish;
/// No description provided for @scanDish.
///
/// In en, this message translates to:
/// **'Scan'**
String get scanDish;
/// No description provided for @menu.
///
/// In en, this message translates to:
/// **'Menu'**
String get menu;
/// No description provided for @dishHistory.
///
/// In en, this message translates to:
/// **'Dish history'**
String get dishHistory;
/// No description provided for @recommendCook.
///
/// In en, this message translates to:
/// **'We recommend cooking'**
String get recommendCook;
/// No description provided for @camera.
///
/// In en, this message translates to:
/// **'Camera'**
String get camera;
/// No description provided for @gallery.
///
/// In en, this message translates to:
/// **'Gallery'**
String get gallery;
/// No description provided for @analyzingPhoto.
///
/// In en, this message translates to:
/// **'Analyzing photo...'**
String get analyzingPhoto;
/// No description provided for @inQueue.
///
/// In en, this message translates to:
/// **'You are in queue'**
String get inQueue;
/// No description provided for @queuePosition.
///
/// In en, this message translates to:
/// **'Position {position}'**
String queuePosition(int position);
/// No description provided for @processing.
///
/// In en, this message translates to:
/// **'Processing...'**
String get processing;
/// No description provided for @upgradePrompt.
///
/// In en, this message translates to:
/// **'Skip the queue? Upgrade →'**
String get upgradePrompt;
/// No description provided for @recognitionFailed.
///
/// In en, this message translates to:
/// **'Recognition failed. Try again.'**
String get recognitionFailed;
/// No description provided for @dishRecognition.
///
/// In en, this message translates to:
/// **'Dish recognition'**
String get dishRecognition;
/// No description provided for @all.
///
/// In en, this message translates to:
/// **'All'**
String get all;
/// No description provided for @dishRecognized.
///
/// In en, this message translates to:
/// **'Dish recognized'**
String get dishRecognized;
/// No description provided for @recognizing.
///
/// In en, this message translates to:
/// **'Recognizing…'**
String get recognizing;
/// No description provided for @recognitionError.
///
/// In en, this message translates to:
/// **'Recognition error'**
String get recognitionError;
/// No description provided for @dishResultTitle.
///
/// In en, this message translates to:
/// **'Dish recognized'**
String get dishResultTitle;
/// No description provided for @selectDish.
///
/// In en, this message translates to:
/// **'Select dish'**
String get selectDish;
/// No description provided for @dishNotRecognized.
///
/// In en, this message translates to:
/// **'Dish not recognized'**
String get dishNotRecognized;
/// No description provided for @tryAgain.
///
/// In en, this message translates to:
/// **'Try again'**
String get tryAgain;
/// No description provided for @nutritionApproximate.
///
/// In en, this message translates to:
/// **'Nutrition is approximate — estimated from photo.'**
String get nutritionApproximate;
/// No description provided for @portion.
///
/// In en, this message translates to:
/// **'Portion'**
String get portion;
/// No description provided for @mealType.
///
/// In en, this message translates to:
/// **'Meal type'**
String get mealType;
/// No description provided for @dateLabel.
///
/// In en, this message translates to:
/// **'Date'**
String get dateLabel;
/// No description provided for @addToJournal.
///
/// In en, this message translates to:
/// **'Add to journal'**
String get addToJournal;
/// No description provided for @addFailed.
///
/// In en, this message translates to:
/// **'Failed to add. Try again.'**
String get addFailed;
/// No description provided for @historyTitle.
///
/// In en, this message translates to:
/// **'Recognition history'**
String get historyTitle;
/// No description provided for @historyLoadError.
///
/// In en, this message translates to:
/// **'Failed to load history'**
String get historyLoadError;
/// No description provided for @retry.
///
/// In en, this message translates to:
/// **'Retry'**
String get retry;
/// No description provided for @noHistory.
///
/// In en, this message translates to:
/// **'No recognitions yet'**
String get noHistory;
/// No description provided for @profileTitle.
///
/// In en, this message translates to:
/// **'Profile'**
String get profileTitle;
/// No description provided for @edit.
///
/// In en, this message translates to:
/// **'Edit'**
String get edit;
/// No description provided for @bodyParams.
///
/// In en, this message translates to:
/// **'BODY PARAMS'**
String get bodyParams;
/// No description provided for @goalActivity.
///
/// In en, this message translates to:
/// **'GOAL & ACTIVITY'**
String get goalActivity;
/// No description provided for @nutrition.
///
/// In en, this message translates to:
/// **'NUTRITION'**
String get nutrition;
/// No description provided for @settings.
///
/// In en, this message translates to:
/// **'SETTINGS'**
String get settings;
/// No description provided for @height.
///
/// In en, this message translates to:
/// **'Height'**
String get height;
/// No description provided for @weight.
///
/// In en, this message translates to:
/// **'Weight'**
String get weight;
/// No description provided for @age.
///
/// In en, this message translates to:
/// **'Age'**
String get age;
/// No description provided for @gender.
///
/// In en, this message translates to:
/// **'Gender'**
String get gender;
/// No description provided for @genderMale.
///
/// In en, this message translates to:
/// **'Male'**
String get genderMale;
/// No description provided for @genderFemale.
///
/// In en, this message translates to:
/// **'Female'**
String get genderFemale;
/// No description provided for @goalLoss.
///
/// In en, this message translates to:
/// **'Weight loss'**
String get goalLoss;
/// No description provided for @goalMaintain.
///
/// In en, this message translates to:
/// **'Maintenance'**
String get goalMaintain;
/// No description provided for @goalGain.
///
/// In en, this message translates to:
/// **'Muscle gain'**
String get goalGain;
/// No description provided for @activityLow.
///
/// In en, this message translates to:
/// **'Low'**
String get activityLow;
/// No description provided for @activityMedium.
///
/// In en, this message translates to:
/// **'Medium'**
String get activityMedium;
/// No description provided for @activityHigh.
///
/// In en, this message translates to:
/// **'High'**
String get activityHigh;
/// No description provided for @calorieGoal.
///
/// In en, this message translates to:
/// **'Calorie goal'**
String get calorieGoal;
/// No description provided for @mealTypes.
///
/// In en, this message translates to:
/// **'Meal types'**
String get mealTypes;
/// No description provided for @formulaNote.
///
/// In en, this message translates to:
/// **'Calculated using the Mifflin-St Jeor formula'**
String get formulaNote;
/// No description provided for @language.
///
/// In en, this message translates to:
/// **'Language'**
String get language;
/// No description provided for @notSet.
///
/// In en, this message translates to:
/// **'Not set'**
String get notSet;
/// No description provided for @calorieHint.
///
/// In en, this message translates to:
/// **'Enter body params to calculate calorie goal'**
String get calorieHint;
/// No description provided for @logout.
///
/// In en, this message translates to:
/// **'Log out'**
String get logout;
/// No description provided for @editProfile.
///
/// In en, this message translates to:
/// **'Edit profile'**
String get editProfile;
/// No description provided for @cancel.
///
/// In en, this message translates to:
/// **'Cancel'**
String get cancel;
/// No description provided for @save.
///
/// In en, this message translates to:
/// **'Save'**
String get save;
/// No description provided for @nameLabel.
///
/// In en, this message translates to:
/// **'Name'**
String get nameLabel;
/// No description provided for @heightCm.
///
/// In en, this message translates to:
/// **'Height (cm)'**
String get heightCm;
/// No description provided for @weightKg.
///
/// In en, this message translates to:
/// **'Weight (kg)'**
String get weightKg;
/// No description provided for @birthDate.
///
/// In en, this message translates to:
/// **'Date of birth'**
String get birthDate;
/// No description provided for @nameRequired.
///
/// In en, this message translates to:
/// **'Enter name'**
String get nameRequired;
/// No description provided for @profileUpdated.
///
/// In en, this message translates to:
/// **'Profile updated'**
String get profileUpdated;
/// No description provided for @profileSaveFailed.
///
/// In en, this message translates to:
/// **'Failed to save'**
String get profileSaveFailed;
/// No description provided for @mealTypeBreakfast.
///
/// In en, this message translates to:
/// **'Breakfast'**
String get mealTypeBreakfast;
/// No description provided for @mealTypeSecondBreakfast.
///
/// In en, this message translates to:
/// **'Second breakfast'**
String get mealTypeSecondBreakfast;
/// No description provided for @mealTypeLunch.
///
/// In en, this message translates to:
/// **'Lunch'**
String get mealTypeLunch;
/// No description provided for @mealTypeAfternoonSnack.
///
/// In en, this message translates to:
/// **'Afternoon snack'**
String get mealTypeAfternoonSnack;
/// No description provided for @mealTypeDinner.
///
/// In en, this message translates to:
/// **'Dinner'**
String get mealTypeDinner;
/// No description provided for @mealTypeSnack.
///
/// In en, this message translates to:
/// **'Snack'**
String get mealTypeSnack;
/// No description provided for @navHome.
///
/// In en, this message translates to:
/// **'Home'**
String get navHome;
/// No description provided for @navProducts.
///
/// In en, this message translates to:
/// **'Products'**
String get navProducts;
/// No description provided for @navRecipes.
///
/// In en, this message translates to:
/// **'Recipes'**
String get navRecipes;
/// No description provided for @addFromReceiptOrPhoto.
///
/// In en, this message translates to:
/// **'Add from receipt or photo'**
String get addFromReceiptOrPhoto;
/// No description provided for @chooseMethod.
///
/// In en, this message translates to:
/// **'Choose method'**
String get chooseMethod;
/// No description provided for @photoReceipt.
///
/// In en, this message translates to:
/// **'Photo of receipt'**
String get photoReceipt;
/// No description provided for @photoReceiptSubtitle.
///
/// In en, this message translates to:
/// **'Recognize all items from a receipt'**
String get photoReceiptSubtitle;
/// No description provided for @photoProducts.
///
/// In en, this message translates to:
/// **'Photo of products'**
String get photoProducts;
/// No description provided for @photoProductsSubtitle.
///
/// In en, this message translates to:
/// **'Fridge, table, shelf — up to 3 photos'**
String get photoProductsSubtitle;
/// No description provided for @addPackagedFood.
///
/// In en, this message translates to:
/// **'Add packaged food'**
String get addPackagedFood;
/// No description provided for @scanBarcode.
///
/// In en, this message translates to:
/// **'Scan barcode'**
String get scanBarcode;
/// No description provided for @portionWeightG.
///
/// In en, this message translates to:
/// **'Portion weight (g)'**
String get portionWeightG;
/// No description provided for @productNotFound.
///
/// In en, this message translates to:
/// **'Product not found'**
String get productNotFound;
/// No description provided for @enterManually.
///
/// In en, this message translates to:
/// **'Enter manually'**
String get enterManually;
/// No description provided for @perHundredG.
///
/// In en, this message translates to:
/// **'per 100 g'**
String get perHundredG;
/// No description provided for @searchFoodHint.
///
/// In en, this message translates to:
/// **'Search products and dishes...'**
String get searchFoodHint;
/// No description provided for @recentlyUsedLabel.
///
/// In en, this message translates to:
/// **'Recently used'**
String get recentlyUsedLabel;
/// No description provided for @productsSection.
///
/// In en, this message translates to:
/// **'Products'**
String get productsSection;
/// No description provided for @dishesSection.
///
/// In en, this message translates to:
/// **'Dishes'**
String get dishesSection;
/// No description provided for @noResultsForQuery.
///
/// In en, this message translates to:
/// **'Nothing found for \"{query}\"'**
String noResultsForQuery(String query);
/// No description provided for @servingsLabel.
///
/// In en, this message translates to:
/// **'Servings'**
String get servingsLabel;
/// No description provided for @addToDiary.
///
/// In en, this message translates to:
/// **'Add to diary'**
String get addToDiary;
/// No description provided for @scanDishPhoto.
///
/// In en, this message translates to:
/// **'Scan photo'**
String get scanDishPhoto;
/// No description provided for @planningForDate.
///
/// In en, this message translates to:
/// **'Planning for {date}'**
String planningForDate(String date);
/// No description provided for @markAsEaten.
///
/// In en, this message translates to:
/// **'Mark as eaten'**
String get markAsEaten;
/// No description provided for @plannedMealLabel.
///
/// In en, this message translates to:
/// **'Planned'**
String get plannedMealLabel;
/// No description provided for @generateWeekLabel.
///
/// In en, this message translates to:
/// **'Plan the week'**
String get generateWeekLabel;
/// No description provided for @generateWeekSubtitle.
///
/// In en, this message translates to:
/// **'AI will create a menu with breakfast, lunch and dinner for the whole week'**
String get generateWeekSubtitle;
/// No description provided for @generatingMenu.
///
/// In en, this message translates to:
/// **'Generating menu...'**
String get generatingMenu;
/// No description provided for @weekPlannedLabel.
///
/// In en, this message translates to:
/// **'Week planned'**
String get weekPlannedLabel;
}
class _AppLocalizationsDelegate
extends LocalizationsDelegate<AppLocalizations> {
const _AppLocalizationsDelegate();
@override
Future<AppLocalizations> load(Locale locale) {
return SynchronousFuture<AppLocalizations>(lookupAppLocalizations(locale));
}
@override
bool isSupported(Locale locale) => <String>[
'ar',
'de',
'en',
'es',
'fr',
'hi',
'it',
'ja',
'ko',
'pt',
'ru',
'zh',
].contains(locale.languageCode);
@override
bool shouldReload(_AppLocalizationsDelegate old) => false;
}
AppLocalizations lookupAppLocalizations(Locale locale) {
// Lookup logic when only language code is specified.
switch (locale.languageCode) {
case 'ar':
return AppLocalizationsAr();
case 'de':
return AppLocalizationsDe();
case 'en':
return AppLocalizationsEn();
case 'es':
return AppLocalizationsEs();
case 'fr':
return AppLocalizationsFr();
case 'hi':
return AppLocalizationsHi();
case 'it':
return AppLocalizationsIt();
case 'ja':
return AppLocalizationsJa();
case 'ko':
return AppLocalizationsKo();
case 'pt':
return AppLocalizationsPt();
case 'ru':
return AppLocalizationsRu();
case 'zh':
return AppLocalizationsZh();
}
throw FlutterError(
'AppLocalizations.delegate failed to load unsupported locale "$locale". This is likely '
'an issue with the localizations generation tool. Please file an issue '
'on GitHub with a reproducible sample app and the gen-l10n configuration '
'that was used.',
);
}