Add Swedish localization for various app actions and inventory management strings
This commit is contained in:
@@ -4,6 +4,7 @@ import 'package:go_router/go_router.dart';
|
||||
|
||||
import '../../../core/api/api_error_mapper.dart';
|
||||
import '../../../core/api/api_exception.dart';
|
||||
import '../../../core/l10n/l10n.dart';
|
||||
import '../../auth/data/auth_providers.dart';
|
||||
import '../data/recipe_providers.dart';
|
||||
import '../domain/parsed_recipe.dart';
|
||||
@@ -84,7 +85,7 @@ class _CreateRecipeScreenState extends ConsumerState<CreateRecipeScreen> {
|
||||
Future<void> _parseMarkdown() async {
|
||||
final markdown = _markdownCtrl.text.trim();
|
||||
if (markdown.isEmpty) {
|
||||
setState(() => _parseError = 'Klistra in eller skriv ett recept i Markdown-format.');
|
||||
setState(() => _parseError = context.l10n.recipeCreateMarkdownHint);
|
||||
return;
|
||||
}
|
||||
setState(() {
|
||||
@@ -117,7 +118,7 @@ class _CreateRecipeScreenState extends ConsumerState<CreateRecipeScreen> {
|
||||
Future<void> _save() async {
|
||||
final name = _nameCtrl.text.trim();
|
||||
if (name.isEmpty) {
|
||||
setState(() => _saveError = 'Receptnamnet får inte vara tomt.');
|
||||
setState(() => _saveError = context.l10n.recipeCreateNameRequired);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -175,7 +176,7 @@ class _CreateRecipeScreenState extends ConsumerState<CreateRecipeScreen> {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title:
|
||||
Text(_step == _Step.input ? 'Nytt recept' : 'Granska ingredienser'),
|
||||
Text(_step == _Step.input ? context.l10n.recipeCreateTitle : context.l10n.recipeCreateReviewIngredients),
|
||||
leading: _step == _Step.review
|
||||
? IconButton(
|
||||
icon: const Icon(Icons.arrow_back),
|
||||
@@ -202,9 +203,9 @@ class _CreateRecipeScreenState extends ConsumerState<CreateRecipeScreen> {
|
||||
maxLines: null,
|
||||
expands: true,
|
||||
textAlignVertical: TextAlignVertical.top,
|
||||
decoration: const InputDecoration(
|
||||
hintText: '# Receptnamn\n\n## Ingredienser\n- 500 g köttfärs\n- 1 st lök\n\n## Tillvägagångssätt\nStek löken...',
|
||||
border: OutlineInputBorder(),
|
||||
decoration: InputDecoration(
|
||||
hintText: context.l10n.recipeCreateMarkdownPlaceholder,
|
||||
border: const OutlineInputBorder(),
|
||||
alignLabelWithHint: true,
|
||||
),
|
||||
),
|
||||
@@ -230,7 +231,7 @@ class _CreateRecipeScreenState extends ConsumerState<CreateRecipeScreen> {
|
||||
width: 18,
|
||||
child: CircularProgressIndicator(strokeWidth: 2),
|
||||
)
|
||||
: const Text('Granska ingredienser'),
|
||||
: Text(context.l10n.recipeCreateReviewIngredients),
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -248,22 +249,22 @@ class _CreateRecipeScreenState extends ConsumerState<CreateRecipeScreen> {
|
||||
children: [
|
||||
TextField(
|
||||
controller: _nameCtrl,
|
||||
decoration: const InputDecoration(labelText: 'Receptnamn'),
|
||||
decoration: InputDecoration(labelText: context.l10n.recipeEditNameLabel),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
TextField(
|
||||
controller: _servingsCtrl,
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'Antal portioner (valfritt)'),
|
||||
decoration: InputDecoration(
|
||||
labelText: context.l10n.recipeEditServingsLabel),
|
||||
keyboardType: TextInputType.number,
|
||||
),
|
||||
if (parsed.ingredients.isNotEmpty) ...[
|
||||
const SizedBox(height: 20),
|
||||
Text('Ingredienser',
|
||||
Text(context.l10n.recipeEditIngredientsLabel,
|
||||
style: Theme.of(context).textTheme.titleMedium),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
'Bocka av ingredienser att inkludera och välj rätt produkt.',
|
||||
context.l10n.recipeCreateIngredientsHint,
|
||||
style: Theme.of(context).textTheme.bodySmall,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
@@ -295,7 +296,7 @@ class _CreateRecipeScreenState extends ConsumerState<CreateRecipeScreen> {
|
||||
width: 18,
|
||||
child: CircularProgressIndicator(strokeWidth: 2),
|
||||
)
|
||||
: const Text('Spara recept'),
|
||||
: Text(context.l10n.recipeCreateSaveAction),
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -321,7 +322,7 @@ class _CreateRecipeScreenState extends ConsumerState<CreateRecipeScreen> {
|
||||
title: Text(label),
|
||||
subtitle: ing.suggestions.isEmpty
|
||||
? Text(
|
||||
'Ingen produkt hittades — ingrediensen hoppas över.',
|
||||
context.l10n.recipeCreateNoProductFound,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
fontSize: 12),
|
||||
|
||||
@@ -5,6 +5,7 @@ import 'package:go_router/go_router.dart';
|
||||
import '../../../core/api/api_error_mapper.dart';
|
||||
import '../../../core/api/api_exception.dart';
|
||||
import '../../../core/auth/jwt_decoder.dart';
|
||||
import '../../../core/l10n/l10n.dart';
|
||||
import '../../../core/ui/async_state_views.dart';
|
||||
import '../../auth/data/auth_providers.dart';
|
||||
import '../data/recipe_providers.dart';
|
||||
@@ -38,31 +39,31 @@ class RecipeDetailScreen extends ConsumerWidget {
|
||||
leading: IconButton(
|
||||
icon: const Icon(Icons.arrow_back),
|
||||
onPressed: () => context.go('/recipes'),
|
||||
tooltip: 'Tillbaka till receptlistan',
|
||||
tooltip: context.l10n.recipeDetailBackToList,
|
||||
),
|
||||
actions: recipe == null
|
||||
? []
|
||||
: [
|
||||
if (isOwner)
|
||||
IconButton(
|
||||
tooltip: recipe.isPublic ? 'Gör privat' : 'Gör publik',
|
||||
tooltip: recipe.isPublic ? context.l10n.recipeDetailMakePrivate : context.l10n.recipeDetailMakePublic,
|
||||
icon: Icon(recipe.isPublic ? Icons.public : Icons.lock_outline),
|
||||
onPressed: () => _toggleVisibility(context, ref, recipe),
|
||||
),
|
||||
if (isOwner)
|
||||
IconButton(
|
||||
tooltip: 'Dela med användare',
|
||||
tooltip: context.l10n.recipeDetailShareWithUser,
|
||||
icon: const Icon(Icons.person_add_alt_1_outlined),
|
||||
onPressed: () => _shareRecipe(context, ref, recipe),
|
||||
),
|
||||
IconButton(
|
||||
tooltip: 'Redigera',
|
||||
tooltip: context.l10n.editTooltip,
|
||||
icon: const Icon(Icons.edit_outlined),
|
||||
onPressed: () =>
|
||||
context.push('/recipes/$recipeId/edit'),
|
||||
),
|
||||
IconButton(
|
||||
tooltip: 'Gå till inventarie',
|
||||
tooltip: context.l10n.recipeDetailGoToInventory,
|
||||
icon: const Icon(Icons.inventory_2_outlined),
|
||||
onPressed: () => context.go('/inventory'),
|
||||
),
|
||||
@@ -70,7 +71,7 @@ class RecipeDetailScreen extends ConsumerWidget {
|
||||
],
|
||||
),
|
||||
body: recipeAsync.when(
|
||||
loading: () => const LoadingStateView(label: 'Laddar recept...'),
|
||||
loading: () => LoadingStateView(label: context.l10n.recipeDetailLoading),
|
||||
error: (error, _) => ErrorStateView(
|
||||
message: mapErrorToUserMessage(error, context),
|
||||
onRetry: () => ref.invalidate(recipeDetailProvider(recipeId)),
|
||||
@@ -184,8 +185,8 @@ class RecipeDetailScreen extends ConsumerWidget {
|
||||
SnackBar(
|
||||
content: Text(
|
||||
!recipe.isPublic
|
||||
? 'Receptet är nu publikt.'
|
||||
: 'Receptet är nu privat.',
|
||||
? context.l10n.recipeDetailNowPublic
|
||||
: context.l10n.recipeDetailNowPrivate,
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -206,13 +207,13 @@ class RecipeDetailScreen extends ConsumerWidget {
|
||||
final result = await showDialog<(_ShareAction, String)>(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: const Text('Dela recept'),
|
||||
title: Text(context.l10n.recipeDetailShareTitle),
|
||||
content: TextField(
|
||||
controller: ctrl,
|
||||
autofocus: true,
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'Användarnamn',
|
||||
hintText: 't.ex. anna',
|
||||
decoration: InputDecoration(
|
||||
labelText: context.l10n.recipeDetailUsernameLabel,
|
||||
hintText: context.l10n.recipeDetailUsernameHint,
|
||||
),
|
||||
onSubmitted: (_) => Navigator.pop(
|
||||
context,
|
||||
@@ -222,21 +223,21 @@ class RecipeDetailScreen extends ConsumerWidget {
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context),
|
||||
child: const Text('Avbryt'),
|
||||
child: Text(context.l10n.cancelAction),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(
|
||||
context,
|
||||
(_ShareAction.unshare, ctrl.text.trim()),
|
||||
),
|
||||
child: const Text('Ta bort delning'),
|
||||
child: Text(context.l10n.recipeDetailRemoveShare),
|
||||
),
|
||||
FilledButton(
|
||||
onPressed: () => Navigator.pop(
|
||||
context,
|
||||
(_ShareAction.share, ctrl.text.trim()),
|
||||
),
|
||||
child: const Text('Dela'),
|
||||
child: Text(context.l10n.recipeDetailShareAction),
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -272,8 +273,8 @@ class RecipeDetailScreen extends ConsumerWidget {
|
||||
SnackBar(
|
||||
content: Text(
|
||||
action == _ShareAction.unshare
|
||||
? 'Delning borttagen för $trimmed.'
|
||||
: 'Receptet delades med $trimmed.',
|
||||
? context.l10n.recipeDetailSharingRemoved(trimmed)
|
||||
: context.l10n.recipeDetailSharedWith(trimmed),
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -310,7 +311,7 @@ class _DeleteButton extends ConsumerWidget {
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
return IconButton(
|
||||
tooltip: 'Ta bort',
|
||||
tooltip: context.l10n.deleteTooltip,
|
||||
icon: const Icon(Icons.delete_outline),
|
||||
onPressed: () => _confirmDelete(context, ref),
|
||||
);
|
||||
@@ -320,19 +321,18 @@ class _DeleteButton extends ConsumerWidget {
|
||||
final confirmed = await showDialog<bool>(
|
||||
context: context,
|
||||
builder: (_) => AlertDialog(
|
||||
title: const Text('Ta bort recept?'),
|
||||
content: Text(
|
||||
'Vill du ta bort "${recipe.title}"? Åtgärden kan inte ångras.'),
|
||||
title: Text(context.l10n.recipeDetailDeleteTitle),
|
||||
content: Text(context.l10n.recipeDetailDeleteContent(recipe.title)),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context, false),
|
||||
child: const Text('Avbryt'),
|
||||
child: Text(context.l10n.cancelAction),
|
||||
),
|
||||
FilledButton(
|
||||
style: FilledButton.styleFrom(
|
||||
backgroundColor: Theme.of(context).colorScheme.error),
|
||||
onPressed: () => Navigator.pop(context, true),
|
||||
child: const Text('Ta bort'),
|
||||
child: Text(context.l10n.deleteAction),
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -385,14 +385,14 @@ class _RecipeBody extends StatelessWidget {
|
||||
children: [
|
||||
const Icon(Icons.people_outline, size: 16),
|
||||
const SizedBox(width: 4),
|
||||
Text('${recipe.servings} portioner',
|
||||
Text('${recipe.servings} ${context.l10n.recipeDetailServings}',
|
||||
style: theme.textTheme.bodySmall),
|
||||
],
|
||||
),
|
||||
],
|
||||
if (recipe.ingredients.isNotEmpty) ...[
|
||||
const SizedBox(height: 24),
|
||||
Text('Ingredienser', style: theme.textTheme.titleMedium),
|
||||
Text(context.l10n.recipeDetailIngredients, style: theme.textTheme.titleMedium),
|
||||
const SizedBox(height: 12),
|
||||
...recipe.ingredients.map((ing) {
|
||||
final qtyStr = ing.quantity == 0 ? '' : _fmtQty(ing.quantity);
|
||||
@@ -443,7 +443,7 @@ class _RecipeBody extends StatelessWidget {
|
||||
if (recipe.instructions != null &&
|
||||
recipe.instructions!.isNotEmpty) ...[
|
||||
const SizedBox(height: 32),
|
||||
Text('Tillvägagångssätt', style: theme.textTheme.titleMedium),
|
||||
Text(context.l10n.recipeDetailInstructions, style: theme.textTheme.titleMedium),
|
||||
const SizedBox(height: 16),
|
||||
..._buildSteps(recipe.instructions!, theme),
|
||||
],
|
||||
|
||||
@@ -7,6 +7,7 @@ import '../../../core/api/api_exception.dart';
|
||||
import '../../../core/api/api_paths.dart';
|
||||
import '../../../core/api/api_providers.dart';
|
||||
import '../../../core/forms/form_options.dart';
|
||||
import '../../../core/l10n/l10n.dart';
|
||||
import '../../../core/ui/async_state_views.dart';
|
||||
import '../../auth/data/auth_providers.dart';
|
||||
import '../data/recipe_providers.dart';
|
||||
@@ -152,20 +153,20 @@ class _RecipeEditScreenState extends ConsumerState<RecipeEditScreen> {
|
||||
|
||||
String? _validateIngredients() {
|
||||
if (_ingredients.isEmpty) {
|
||||
return 'Minst en ingrediens krävs.';
|
||||
return context.l10n.recipeEditMinIngredients;
|
||||
}
|
||||
for (final ingredient in _ingredients) {
|
||||
if (ingredient.productId == null) {
|
||||
return 'Välj produkt för alla ingredienser.';
|
||||
return context.l10n.recipeEditSelectProduct;
|
||||
}
|
||||
final quantity = double.tryParse(
|
||||
ingredient.quantityCtrl.text.trim().replaceAll(',', '.'),
|
||||
);
|
||||
if (quantity == null || quantity < 0) {
|
||||
return 'Ange giltig mängd för alla ingredienser.';
|
||||
return context.l10n.recipeEditValidQuantity;
|
||||
}
|
||||
if (ingredient.unit.trim().isEmpty) {
|
||||
return 'Välj enhet för alla ingredienser.';
|
||||
return context.l10n.recipeEditSelectUnit;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
@@ -237,7 +238,7 @@ class _RecipeEditScreenState extends ConsumerState<RecipeEditScreen> {
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Redigera recept'),
|
||||
title: Text(context.l10n.recipeEditTitle),
|
||||
actions: [
|
||||
if (_initialized)
|
||||
TextButton(
|
||||
@@ -248,12 +249,12 @@ class _RecipeEditScreenState extends ConsumerState<RecipeEditScreen> {
|
||||
width: 16,
|
||||
child: CircularProgressIndicator(strokeWidth: 2),
|
||||
)
|
||||
: const Text('Spara'),
|
||||
: Text(context.l10n.saveAction),
|
||||
),
|
||||
],
|
||||
),
|
||||
body: recipeAsync.when(
|
||||
loading: () => const LoadingStateView(label: 'Laddar recept...'),
|
||||
loading: () => LoadingStateView(label: context.l10n.recipeDetailLoading),
|
||||
error: (error, _) => ErrorStateView(
|
||||
message: mapErrorToUserMessage(error, context),
|
||||
onRetry: () => ref.invalidate(recipeDetailProvider(widget.recipeId)),
|
||||
@@ -276,27 +277,27 @@ class _RecipeEditScreenState extends ConsumerState<RecipeEditScreen> {
|
||||
children: [
|
||||
TextFormField(
|
||||
controller: _nameCtrl,
|
||||
decoration: const InputDecoration(labelText: 'Receptnamn'),
|
||||
decoration: InputDecoration(labelText: context.l10n.recipeEditNameLabel),
|
||||
validator: (v) =>
|
||||
(v == null || v.trim().isEmpty) ? 'Ange ett receptnamn.' : null,
|
||||
(v == null || v.trim().isEmpty) ? context.l10n.recipeEditNameRequired : null,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
TextFormField(
|
||||
controller: _descCtrl,
|
||||
decoration:
|
||||
const InputDecoration(labelText: 'Beskrivning (valfritt)'),
|
||||
InputDecoration(labelText: context.l10n.recipeEditDescriptionLabel),
|
||||
maxLines: 3,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
TextFormField(
|
||||
controller: _servingsCtrl,
|
||||
decoration:
|
||||
const InputDecoration(labelText: 'Antal portioner (valfritt)'),
|
||||
InputDecoration(labelText: context.l10n.recipeEditServingsLabel),
|
||||
keyboardType: TextInputType.number,
|
||||
validator: (v) {
|
||||
if (v == null || v.trim().isEmpty) return null;
|
||||
if (int.tryParse(v.trim()) == null) {
|
||||
return 'Ange ett heltal.';
|
||||
return context.l10n.recipeEditServingsInvalid;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
@@ -305,7 +306,7 @@ class _RecipeEditScreenState extends ConsumerState<RecipeEditScreen> {
|
||||
TextFormField(
|
||||
controller: _instructionsCtrl,
|
||||
decoration:
|
||||
const InputDecoration(labelText: 'Tillvägagångssätt (valfritt)'),
|
||||
InputDecoration(labelText: context.l10n.recipeEditInstructionsLabel),
|
||||
maxLines: 10,
|
||||
textAlignVertical: TextAlignVertical.top,
|
||||
),
|
||||
@@ -314,20 +315,20 @@ class _RecipeEditScreenState extends ConsumerState<RecipeEditScreen> {
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text(
|
||||
'Ingredienser',
|
||||
context.l10n.recipeEditIngredientsLabel,
|
||||
style: Theme.of(context).textTheme.titleMedium,
|
||||
),
|
||||
),
|
||||
OutlinedButton.icon(
|
||||
onPressed: _isSaving ? null : _addIngredient,
|
||||
icon: const Icon(Icons.add),
|
||||
label: const Text('Lägg till'),
|
||||
label: Text(context.l10n.addAction),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
'Välj produkt, mängd och enhet för varje ingrediens.',
|
||||
context.l10n.recipeEditIngredientsHint,
|
||||
style: Theme.of(context).textTheme.bodySmall?.copyWith(
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant),
|
||||
),
|
||||
@@ -338,10 +339,10 @@ class _RecipeEditScreenState extends ConsumerState<RecipeEditScreen> {
|
||||
child: LinearProgressIndicator(),
|
||||
),
|
||||
if (_ingredients.isEmpty)
|
||||
const Card(
|
||||
Card(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(16),
|
||||
child: Text('Inga ingredienser tillagda än.'),
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Text(context.l10n.recipeEditNoIngredients),
|
||||
),
|
||||
),
|
||||
...List.generate(
|
||||
@@ -366,7 +367,7 @@ class _RecipeEditScreenState extends ConsumerState<RecipeEditScreen> {
|
||||
width: 18,
|
||||
child: CircularProgressIndicator(strokeWidth: 2),
|
||||
)
|
||||
: const Text('Spara ändringar'),
|
||||
: Text(context.l10n.recipeEditSaveChanges),
|
||||
),
|
||||
const SizedBox(height: 40),
|
||||
],
|
||||
@@ -387,14 +388,14 @@ class _RecipeEditScreenState extends ConsumerState<RecipeEditScreen> {
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text(
|
||||
'Ingrediens ${index + 1}',
|
||||
'${context.l10n.recipeEditIngredientPrefix}${index + 1}',
|
||||
style: Theme.of(context).textTheme.titleSmall,
|
||||
),
|
||||
),
|
||||
IconButton(
|
||||
onPressed: _isSaving ? null : () => _removeIngredient(index),
|
||||
icon: const Icon(Icons.delete_outline),
|
||||
tooltip: 'Ta bort ingrediens',
|
||||
tooltip: context.l10n.recipeEditRemoveIngredient,
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -440,19 +441,19 @@ class _RecipeEditScreenState extends ConsumerState<RecipeEditScreen> {
|
||||
Expanded(
|
||||
child: TextFormField(
|
||||
controller: ingredient.quantityCtrl,
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'Mängd *',
|
||||
border: OutlineInputBorder(),
|
||||
decoration: InputDecoration(
|
||||
labelText: context.l10n.quantityLabel,
|
||||
border: const OutlineInputBorder(),
|
||||
),
|
||||
keyboardType:
|
||||
const TextInputType.numberWithOptions(decimal: true),
|
||||
validator: (value) {
|
||||
if (value == null || value.trim().isEmpty) {
|
||||
return 'Ange mängd';
|
||||
return context.l10n.quantityHint;
|
||||
}
|
||||
if (double.tryParse(value.trim().replaceAll(',', '.')) ==
|
||||
null) {
|
||||
return 'Ogiltigt tal';
|
||||
return context.l10n.invalidNumber;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
@@ -463,9 +464,9 @@ class _RecipeEditScreenState extends ConsumerState<RecipeEditScreen> {
|
||||
child: DropdownButtonFormField<String>(
|
||||
initialValue: ingredient.unit.trim().isEmpty ? null : ingredient.unit,
|
||||
isExpanded: true,
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'Enhet *',
|
||||
border: OutlineInputBorder(),
|
||||
decoration: InputDecoration(
|
||||
labelText: context.l10n.unitLabel,
|
||||
border: const OutlineInputBorder(),
|
||||
),
|
||||
items: unitOptions
|
||||
.map(
|
||||
|
||||
@@ -3,6 +3,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
import '../../../core/api/api_error_mapper.dart';
|
||||
import '../../../core/l10n/l10n.dart';
|
||||
import '../../../core/ui/async_state_views.dart';
|
||||
import '../data/recipe_providers.dart';
|
||||
import '../data/recipes_grid_provider.dart';
|
||||
@@ -22,16 +23,16 @@ class RecipesScreen extends ConsumerWidget {
|
||||
return Stack(
|
||||
children: [
|
||||
recipesAsync.when(
|
||||
loading: () => const LoadingStateView(label: 'Laddar recept...'),
|
||||
loading: () => LoadingStateView(label: context.l10n.recipesLoading),
|
||||
error: (error, _) => ErrorStateView(
|
||||
message: mapErrorToUserMessage(error, context),
|
||||
onRetry: () => ref.invalidate(recipesProvider),
|
||||
),
|
||||
data: (recipes) {
|
||||
if (recipes.isEmpty) {
|
||||
return const EmptyStateView(
|
||||
title: 'Inga recept hittades',
|
||||
description: 'Lägg till ett recept för att komma igång.',
|
||||
return EmptyStateView(
|
||||
title: context.l10n.recipesEmpty,
|
||||
description: context.l10n.recipesEmptyDescription,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -95,7 +96,7 @@ class RecipesScreen extends ConsumerWidget {
|
||||
right: 16,
|
||||
bottom: 16,
|
||||
child: FloatingActionButton(
|
||||
tooltip: 'Nytt recept',
|
||||
tooltip: context.l10n.recipesNewTooltip,
|
||||
onPressed: () => context.push('/recipes/create'),
|
||||
child: const Icon(Icons.add),
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user