feat: localise scan screen (12 languages)

Replace all hardcoded Russian strings in ScanScreen and _LoadingDialog
with AppLocalizations keys; add addFromReceiptOrPhoto, chooseMethod,
photoReceipt, photoReceiptSubtitle, photoProducts, photoProductsSubtitle,
recognizing keys to all 12 ARB files and regenerate AppLocalizations.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
dbastrikin
2026-03-19 23:05:11 +02:00
parent 54b10d51e2
commit 9e7fc09f4b
27 changed files with 373 additions and 46 deletions

View File

@@ -3,6 +3,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import 'package:image_picker/image_picker.dart';
import '../../l10n/app_localizations.dart';
import 'recognition_service.dart';
/// Entry screen — lets the user choose how to add products.
@@ -16,38 +17,32 @@ class ScanScreen extends ConsumerStatefulWidget {
class _ScanScreenState extends ConsumerState<ScanScreen> {
@override
Widget build(BuildContext context) {
final l10n = AppLocalizations.of(context)!;
return Scaffold(
appBar: AppBar(title: const Text('Добавить продукты')),
appBar: AppBar(title: Text(l10n.addFromReceiptOrPhoto)),
body: ListView(
padding: const EdgeInsets.all(20),
children: [
const SizedBox(height: 16),
Text(
'Выберите способ',
l10n.chooseMethod,
style: Theme.of(context).textTheme.titleLarge,
textAlign: TextAlign.center,
),
const SizedBox(height: 32),
_ModeCard(
emoji: '🧾',
title: 'Сфотографировать чек',
subtitle: 'Распознаем все продукты из чека',
title: l10n.photoReceipt,
subtitle: l10n.photoReceiptSubtitle,
onTap: () => _pickAndRecognize(context, _Mode.receipt),
),
const SizedBox(height: 16),
_ModeCard(
emoji: '🥦',
title: 'Сфотографировать продукты',
subtitle: 'Холодильник, стол, полка — до 3 фото',
title: l10n.photoProducts,
subtitle: l10n.photoProductsSubtitle,
onTap: () => _pickAndRecognize(context, _Mode.products),
),
const SizedBox(height: 16),
_ModeCard(
emoji: '✏️',
title: 'Добавить вручную',
subtitle: 'Ввести название, количество и срок',
onTap: () => context.push('/products/add'),
),
],
),
);
@@ -85,12 +80,13 @@ class _ScanScreenState extends ConsumerState<ScanScreen> {
if (!context.mounted) return;
final service = ref.read(recognitionServiceProvider);
final l10n = AppLocalizations.of(context)!;
// Show loading overlay while the AI processes.
showDialog(
context: context,
barrierDismissible: false,
builder: (_) => const _LoadingDialog(),
builder: (dialogContext) => _LoadingDialog(label: l10n.recognizing),
);
try {
@@ -113,15 +109,14 @@ class _ScanScreenState extends ConsumerState<ScanScreen> {
if (context.mounted) {
Navigator.pop(context); // close loading
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Не удалось распознать. Попробуйте ещё раз.'),
),
SnackBar(content: Text(l10n.recognitionFailed)),
);
}
}
}
Future<ImageSource?> _chooseSource(BuildContext context) async {
final l10n = AppLocalizations.of(context)!;
return showModalBottomSheet<ImageSource>(
context: context,
builder: (_) => SafeArea(
@@ -130,12 +125,12 @@ class _ScanScreenState extends ConsumerState<ScanScreen> {
children: [
ListTile(
leading: const Icon(Icons.camera_alt),
title: const Text('Камера'),
title: Text(l10n.camera),
onTap: () => Navigator.pop(context, ImageSource.camera),
),
ListTile(
leading: const Icon(Icons.photo_library),
title: const Text('Галерея'),
title: Text(l10n.gallery),
onTap: () => Navigator.pop(context, ImageSource.gallery),
),
],
@@ -186,17 +181,19 @@ class _ModeCard extends StatelessWidget {
}
class _LoadingDialog extends StatelessWidget {
const _LoadingDialog();
const _LoadingDialog({required this.label});
final String label;
@override
Widget build(BuildContext context) {
return const AlertDialog(
return AlertDialog(
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
CircularProgressIndicator(),
SizedBox(height: 16),
Text('Распознаём...'),
const CircularProgressIndicator(),
const SizedBox(height: 16),
Text(label),
],
),
);

View File

@@ -96,5 +96,11 @@
"mealTypeSnack": "وجبة خفيفة",
"navHome": "الرئيسية",
"navProducts": "المنتجات",
"navRecipes": "الوصفات"
"navRecipes": "الوصفات",
"addFromReceiptOrPhoto": "إضافة من الإيصال أو الصورة",
"chooseMethod": "اختر الطريقة",
"photoReceipt": "تصوير الإيصال",
"photoReceiptSubtitle": "التعرف على جميع المنتجات من الإيصال",
"photoProducts": "تصوير المنتجات",
"photoProductsSubtitle": "الثلاجة، الطاولة، الرف — حتى 3 صور"
}

View File

@@ -96,5 +96,11 @@
"mealTypeSnack": "Snack",
"navHome": "Startseite",
"navProducts": "Produkte",
"navRecipes": "Rezepte"
"navRecipes": "Rezepte",
"addFromReceiptOrPhoto": "Aus Kassenbon oder Foto hinzufügen",
"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"
}

View File

@@ -96,5 +96,11 @@
"mealTypeSnack": "Snack",
"navHome": "Home",
"navProducts": "Products",
"navRecipes": "Recipes"
"navRecipes": "Recipes",
"addFromReceiptOrPhoto": "Add from receipt or photo",
"chooseMethod": "Choose method",
"photoReceipt": "Photo of receipt",
"photoReceiptSubtitle": "Recognize all items from a receipt",
"photoProducts": "Photo of products",
"photoProductsSubtitle": "Fridge, table, shelf — up to 3 photos"
}

View File

@@ -96,5 +96,11 @@
"mealTypeSnack": "Aperitivo",
"navHome": "Inicio",
"navProducts": "Productos",
"navRecipes": "Recetas"
"navRecipes": "Recetas",
"addFromReceiptOrPhoto": "Añadir desde recibo o foto",
"chooseMethod": "Elegir método",
"photoReceipt": "Fotografiar recibo",
"photoReceiptSubtitle": "Reconocemos todos los productos del recibo",
"photoProducts": "Fotografiar productos",
"photoProductsSubtitle": "Nevera, mesa, estante — hasta 3 fotos"
}

View File

@@ -96,5 +96,11 @@
"mealTypeSnack": "Collation",
"navHome": "Accueil",
"navProducts": "Produits",
"navRecipes": "Recettes"
"navRecipes": "Recettes",
"addFromReceiptOrPhoto": "Ajouter depuis ticket ou photo",
"chooseMethod": "Choisir la méthode",
"photoReceipt": "Photographier le ticket",
"photoReceiptSubtitle": "Reconnaissance de tous les produits du ticket",
"photoProducts": "Photographier les produits",
"photoProductsSubtitle": "Réfrigérateur, table, étagère — jusqu'à 3 photos"
}

View File

@@ -96,5 +96,11 @@
"mealTypeSnack": "स्नैक",
"navHome": "होम",
"navProducts": "उत्पाद",
"navRecipes": "रेसिपी"
"navRecipes": "रेसिपी",
"addFromReceiptOrPhoto": "रसीद या फ़ोटो से जोड़ें",
"chooseMethod": "तरीका चुनें",
"photoReceipt": "रसीद की फ़ोटो",
"photoReceiptSubtitle": "रसीद से सभी उत्पाद पहचानें",
"photoProducts": "उत्पादों की फ़ोटो",
"photoProductsSubtitle": "फ्रिज, टेबल, शेल्फ — 3 फ़ोटो तक"
}

View File

@@ -96,5 +96,11 @@
"mealTypeSnack": "Spuntino",
"navHome": "Home",
"navProducts": "Prodotti",
"navRecipes": "Ricette"
"navRecipes": "Ricette",
"addFromReceiptOrPhoto": "Aggiungi da scontrino o foto",
"chooseMethod": "Scegli il metodo",
"photoReceipt": "Fotografa scontrino",
"photoReceiptSubtitle": "Riconosciamo tutti i prodotti dallo scontrino",
"photoProducts": "Fotografa i prodotti",
"photoProductsSubtitle": "Frigo, tavolo, scaffale — fino a 3 foto"
}

View File

@@ -96,5 +96,11 @@
"mealTypeSnack": "間食",
"navHome": "ホーム",
"navProducts": "食品",
"navRecipes": "レシピ"
"navRecipes": "レシピ",
"addFromReceiptOrPhoto": "レシートや写真から追加",
"chooseMethod": "方法を選択",
"photoReceipt": "レシートを撮影",
"photoReceiptSubtitle": "レシートから全商品を認識",
"photoProducts": "食品を撮影",
"photoProductsSubtitle": "冷蔵庫・テーブル・棚 — 最大3枚"
}

View File

@@ -96,5 +96,11 @@
"mealTypeSnack": "스낵",
"navHome": "홈",
"navProducts": "식품",
"navRecipes": "레시피"
"navRecipes": "레시피",
"addFromReceiptOrPhoto": "영수증 또는 사진으로 추가",
"chooseMethod": "방법 선택",
"photoReceipt": "영수증 촬영",
"photoReceiptSubtitle": "영수증의 모든 상품 인식",
"photoProducts": "식품 촬영",
"photoProductsSubtitle": "냉장고, 테이블, 선반 — 최대 3장"
}

View File

@@ -669,6 +669,42 @@ abstract class AppLocalizations {
/// 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;
}
class _AppLocalizationsDelegate

View File

@@ -286,4 +286,22 @@ class AppLocalizationsAr extends AppLocalizations {
@override
String get navRecipes => 'الوصفات';
@override
String get addFromReceiptOrPhoto => 'إضافة من الإيصال أو الصورة';
@override
String get chooseMethod => 'اختر الطريقة';
@override
String get photoReceipt => 'تصوير الإيصال';
@override
String get photoReceiptSubtitle => 'التعرف على جميع المنتجات من الإيصال';
@override
String get photoProducts => 'تصوير المنتجات';
@override
String get photoProductsSubtitle => 'الثلاجة، الطاولة، الرف — حتى 3 صور';
}

View File

@@ -287,4 +287,23 @@ class AppLocalizationsDe extends AppLocalizations {
@override
String get navRecipes => 'Rezepte';
@override
String get addFromReceiptOrPhoto => 'Aus Kassenbon oder Foto hinzufügen';
@override
String get chooseMethod => 'Methode wählen';
@override
String get photoReceipt => 'Kassenbon fotografieren';
@override
String get photoReceiptSubtitle => 'Alle Produkte vom Kassenbon erkennen';
@override
String get photoProducts => 'Produkte fotografieren';
@override
String get photoProductsSubtitle =>
'Kühlschrank, Tisch, Regal — bis zu 3 Fotos';
}

View File

@@ -286,4 +286,22 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get navRecipes => 'Recipes';
@override
String get addFromReceiptOrPhoto => 'Add from receipt or photo';
@override
String get chooseMethod => 'Choose method';
@override
String get photoReceipt => 'Photo of receipt';
@override
String get photoReceiptSubtitle => 'Recognize all items from a receipt';
@override
String get photoProducts => 'Photo of products';
@override
String get photoProductsSubtitle => 'Fridge, table, shelf — up to 3 photos';
}

View File

@@ -287,4 +287,23 @@ class AppLocalizationsEs extends AppLocalizations {
@override
String get navRecipes => 'Recetas';
@override
String get addFromReceiptOrPhoto => 'Añadir desde recibo o foto';
@override
String get chooseMethod => 'Elegir método';
@override
String get photoReceipt => 'Fotografiar recibo';
@override
String get photoReceiptSubtitle =>
'Reconocemos todos los productos del recibo';
@override
String get photoProducts => 'Fotografiar productos';
@override
String get photoProductsSubtitle => 'Nevera, mesa, estante — hasta 3 fotos';
}

View File

@@ -287,4 +287,24 @@ class AppLocalizationsFr extends AppLocalizations {
@override
String get navRecipes => 'Recettes';
@override
String get addFromReceiptOrPhoto => 'Ajouter depuis ticket ou photo';
@override
String get chooseMethod => 'Choisir la méthode';
@override
String get photoReceipt => 'Photographier le ticket';
@override
String get photoReceiptSubtitle =>
'Reconnaissance de tous les produits du ticket';
@override
String get photoProducts => 'Photographier les produits';
@override
String get photoProductsSubtitle =>
'Réfrigérateur, table, étagère — jusqu\'à 3 photos';
}

View File

@@ -287,4 +287,22 @@ class AppLocalizationsHi extends AppLocalizations {
@override
String get navRecipes => 'रेसिपी';
@override
String get addFromReceiptOrPhoto => 'रसीद या फ़ोटो से जोड़ें';
@override
String get chooseMethod => 'तरीका चुनें';
@override
String get photoReceipt => 'रसीद की फ़ोटो';
@override
String get photoReceiptSubtitle => 'रसीद से सभी उत्पाद पहचानें';
@override
String get photoProducts => 'उत्पादों की फ़ोटो';
@override
String get photoProductsSubtitle => 'फ्रिज, टेबल, शेल्फ — 3 फ़ोटो तक';
}

View File

@@ -287,4 +287,23 @@ class AppLocalizationsIt extends AppLocalizations {
@override
String get navRecipes => 'Ricette';
@override
String get addFromReceiptOrPhoto => 'Aggiungi da scontrino o foto';
@override
String get chooseMethod => 'Scegli il metodo';
@override
String get photoReceipt => 'Fotografa scontrino';
@override
String get photoReceiptSubtitle =>
'Riconosciamo tutti i prodotti dallo scontrino';
@override
String get photoProducts => 'Fotografa i prodotti';
@override
String get photoProductsSubtitle => 'Frigo, tavolo, scaffale — fino a 3 foto';
}

View File

@@ -285,4 +285,22 @@ class AppLocalizationsJa extends AppLocalizations {
@override
String get navRecipes => 'レシピ';
@override
String get addFromReceiptOrPhoto => 'レシートや写真から追加';
@override
String get chooseMethod => '方法を選択';
@override
String get photoReceipt => 'レシートを撮影';
@override
String get photoReceiptSubtitle => 'レシートから全商品を認識';
@override
String get photoProducts => '食品を撮影';
@override
String get photoProductsSubtitle => '冷蔵庫・テーブル・棚 — 最大3枚';
}

View File

@@ -285,4 +285,22 @@ class AppLocalizationsKo extends AppLocalizations {
@override
String get navRecipes => '레시피';
@override
String get addFromReceiptOrPhoto => '영수증 또는 사진으로 추가';
@override
String get chooseMethod => '방법 선택';
@override
String get photoReceipt => '영수증 촬영';
@override
String get photoReceiptSubtitle => '영수증의 모든 상품 인식';
@override
String get photoProducts => '식품 촬영';
@override
String get photoProductsSubtitle => '냉장고, 테이블, 선반 — 최대 3장';
}

View File

@@ -287,4 +287,23 @@ class AppLocalizationsPt extends AppLocalizations {
@override
String get navRecipes => 'Receitas';
@override
String get addFromReceiptOrPhoto => 'Adicionar de recibo ou foto';
@override
String get chooseMethod => 'Escolher método';
@override
String get photoReceipt => 'Fotografar recibo';
@override
String get photoReceiptSubtitle => 'Reconhecemos todos os produtos do recibo';
@override
String get photoProducts => 'Fotografar produtos';
@override
String get photoProductsSubtitle =>
'Geladeira, mesa, prateleira — até 3 fotos';
}

View File

@@ -286,4 +286,22 @@ class AppLocalizationsRu extends AppLocalizations {
@override
String get navRecipes => 'Рецепты';
@override
String get addFromReceiptOrPhoto => 'Добавить из чека или фото';
@override
String get chooseMethod => 'Выберите способ';
@override
String get photoReceipt => 'Сфотографировать чек';
@override
String get photoReceiptSubtitle => 'Распознаем все продукты из чека';
@override
String get photoProducts => 'Сфотографировать продукты';
@override
String get photoProductsSubtitle => 'Холодильник, стол, полка — до 3 фото';
}

View File

@@ -285,4 +285,22 @@ class AppLocalizationsZh extends AppLocalizations {
@override
String get navRecipes => '食谱';
@override
String get addFromReceiptOrPhoto => '从收据或照片添加';
@override
String get chooseMethod => '选择方式';
@override
String get photoReceipt => '拍摄收据';
@override
String get photoReceiptSubtitle => '识别收据中的所有商品';
@override
String get photoProducts => '拍摄食品';
@override
String get photoProductsSubtitle => '冰箱、桌子、货架 — 最多3张照片';
}

View File

@@ -96,5 +96,11 @@
"mealTypeSnack": "Petisco",
"navHome": "Início",
"navProducts": "Produtos",
"navRecipes": "Receitas"
"navRecipes": "Receitas",
"addFromReceiptOrPhoto": "Adicionar de recibo ou foto",
"chooseMethod": "Escolher método",
"photoReceipt": "Fotografar recibo",
"photoReceiptSubtitle": "Reconhecemos todos os produtos do recibo",
"photoProducts": "Fotografar produtos",
"photoProductsSubtitle": "Geladeira, mesa, prateleira — até 3 fotos"
}

View File

@@ -96,5 +96,11 @@
"mealTypeSnack": "Перекус",
"navHome": "Главная",
"navProducts": "Продукты",
"navRecipes": "Рецепты"
"navRecipes": "Рецепты",
"addFromReceiptOrPhoto": "Добавить из чека или фото",
"chooseMethod": "Выберите способ",
"photoReceipt": "Сфотографировать чек",
"photoReceiptSubtitle": "Распознаем все продукты из чека",
"photoProducts": "Сфотографировать продукты",
"photoProductsSubtitle": "Холодильник, стол, полка — до 3 фото"
}

View File

@@ -96,5 +96,11 @@
"mealTypeSnack": "零食",
"navHome": "首页",
"navProducts": "食品",
"navRecipes": "食谱"
"navRecipes": "食谱",
"addFromReceiptOrPhoto": "从收据或照片添加",
"chooseMethod": "选择方式",
"photoReceipt": "拍摄收据",
"photoReceiptSubtitle": "识别收据中的所有商品",
"photoProducts": "拍摄食品",
"photoProductsSubtitle": "冰箱、桌子、货架 — 最多3张照片"
}