Localize app for German and English

This commit is contained in:
Marco 2024-12-10 22:37:13 +01:00
parent be5cbb5988
commit 8fed59f6dd
15 changed files with 97 additions and 57 deletions

3
l10n.yaml Normal file
View File

@ -0,0 +1,3 @@
arb-dir: lib/l10n
template-arb-file: app_en.arb
output-localization-file: app_localizations.dart

View File

@ -4,6 +4,7 @@ import 'package:calorimeter/storage/storage.dart';
import 'package:flutter/material.dart';
import 'package:calorimeter/food_entry/food_entry_bloc.dart';
import 'package:calorimeter/utils/row_with_spacers_widget.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
class EnterFoodWidget extends StatefulWidget {
final Function(BuildContext context, FoodEntryState entry) onAdd;
@ -42,8 +43,8 @@ class _EnterFoodWidgetState extends State<EnterFoodWidget> {
return TextFormField(
controller: controller,
focusNode: focusNode,
decoration: const InputDecoration(
label: Text("Name"),
decoration: InputDecoration(
label: Text(AppLocalizations.of(context)!.name),
),
);
},
@ -70,9 +71,11 @@ class _EnterFoodWidgetState extends State<EnterFoodWidget> {
}),
TextField(
textAlign: TextAlign.end,
decoration: const InputDecoration(
label:
Align(alignment: Alignment.centerRight, child: Text("Menge")),
decoration: InputDecoration(
label: Align(
alignment: Alignment.centerRight,
child: Text(AppLocalizations.of(context)!.amount),
),
),
keyboardType: TextInputType.number,
controller: massController,
@ -80,9 +83,10 @@ class _EnterFoodWidgetState extends State<EnterFoodWidget> {
),
TextField(
textAlign: TextAlign.end,
decoration: const InputDecoration(
decoration: InputDecoration(
label: Align(
alignment: Alignment.centerRight, child: Text("kcal pro"))),
alignment: Alignment.centerRight,
child: Text(AppLocalizations.of(context)!.kcalper))),
keyboardType: TextInputType.number,
controller: kcalPerMassController,
onSubmitted: (value) => onSubmitAction(),
@ -107,7 +111,8 @@ class _EnterFoodWidgetState extends State<EnterFoodWidget> {
try {
massAsNumber = int.parse(massController.text.replaceAll(",", "."));
} catch (e) {
var snackbar = const SnackBar(content: Text("Menge muss eine Zahl sein"));
var snackbar = SnackBar(
content: Text(AppLocalizations.of(context)!.errAmountNotANumber));
ScaffoldMessenger.of(context).clearSnackBars();
ScaffoldMessenger.of(context).showSnackBar(snackbar);
return;
@ -117,8 +122,8 @@ class _EnterFoodWidgetState extends State<EnterFoodWidget> {
kcalPerMassAsNumber =
int.parse(kcalPerMassController.text.replaceAll(",", "."));
} catch (e) {
var snackbar =
const SnackBar(content: Text("'kcal pro 100g' muss eine Zahl sein"));
var snackbar = SnackBar(
content: Text(AppLocalizations.of(context)!.errKcalNotANumber));
ScaffoldMessenger.of(context).removeCurrentSnackBar();
ScaffoldMessenger.of(context).showSnackBar(snackbar);
return;

View File

@ -3,6 +3,7 @@
import 'package:flutter/material.dart';
import 'package:calorimeter/food_entry/food_entry_bloc.dart';
import 'package:calorimeter/utils/row_with_spacers_widget.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
class FoodEntryWidget extends StatefulWidget {
final FoodEntryState entry;
@ -165,8 +166,8 @@ class _FoodEntryChangeDialogState extends State<FoodEntryChangeDialog> {
child: TextField(
onSubmitted: (val) => _onSubmitAction(),
controller: nameController,
decoration: const InputDecoration(
label: Text("Name"),
decoration: InputDecoration(
label: Text(AppLocalizations.of(context)!.name),
),
),
),
@ -177,8 +178,8 @@ class _FoodEntryChangeDialogState extends State<FoodEntryChangeDialog> {
child: TextField(
onSubmitted: (val) => _onSubmitAction(),
controller: massController,
decoration: const InputDecoration(
label: Text("Menge"),
decoration: InputDecoration(
label: Text(AppLocalizations.of(context)!.amount),
),
),
),
@ -189,8 +190,8 @@ class _FoodEntryChangeDialogState extends State<FoodEntryChangeDialog> {
child: TextField(
onSubmitted: (val) => _onSubmitAction(),
controller: kcalPer100Controller,
decoration: const InputDecoration(
label: Text("kcal pro Menge"),
decoration: InputDecoration(
label: Text(AppLocalizations.of(context)!.kcalper),
),
),
)

13
lib/l10n/app_de.arb Normal file
View File

@ -0,0 +1,13 @@
{
"ok": "OK",
"name": "Name",
"amount": "Menge in 100 g/ml",
"kcalper": "kcal pro Menge",
"kcalToday": "kcal heute",
"menu": "Menü",
"settings": "Einstellungen",
"yourSettings": "Deine persönlichen Einstellungen",
"dayLimit": "Kalorienlimit pro Tag",
"errAmountNotANumber": "Menge muss eine Zahl sein",
"errKcalNotANumber": "kcal muss eine Zahl sein"
}

13
lib/l10n/app_en.arb Normal file
View File

@ -0,0 +1,13 @@
{
"ok": "OK",
"name": "Name",
"amount": "Amount in 100 grams/ml",
"kcalper": "kcal per amount",
"kcalToday": "kcal today",
"menu": "Menu",
"settings": "Settings",
"yourSettings": "Your personal settings",
"dayLimit": "Calorie limit per day",
"errAmountNotANumber": "Amount must be a number",
"errKcalNotANumber": "kcal must be a number"
}

View File

@ -8,8 +8,8 @@ import 'package:calorimeter/utils/settings_bloc.dart';
import 'package:calorimeter/utils/theme_bloc.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:go_router/go_router.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
List<FoodEntryState> entriesForToday = [];
DateTime timeNow = DateTime.now();
@ -17,8 +17,6 @@ DateTime timeNow = DateTime.now();
void main() async {
WidgetsFlutterBinding.ensureInitialized();
WidgetsFlutterBinding.ensureInitialized();
var storage = await FoodStorage.create();
await storage.buildFoodLookupDatabase();
@ -79,6 +77,7 @@ class MainApp extends StatelessWidget {
}
return MaterialApp.router(
//locale: Locale('de'),
routerConfig: GoRouter(
routes: [
GoRoute(
@ -91,14 +90,8 @@ class MainApp extends StatelessWidget {
),
],
),
localizationsDelegates: const [
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
supportedLocales: const [
Locale('de'),
],
localizationsDelegates: AppLocalizations.localizationsDelegates,
supportedLocales: AppLocalizations.supportedLocales,
theme: ThemeData(
dividerTheme: const DividerThemeData(space: 2),
colorScheme: ColorScheme.fromSeed(

View File

@ -58,7 +58,9 @@ class PerDatePageViewController extends StatelessWidget {
child: Scaffold(
appBar: AppBar(
title: Builder(builder: (context) {
return Text(DateFormat.yMMMMd('de').format(
return Text(DateFormat.yMMMMd(
Localizations.localeOf(context).toString())
.format(
context.watch<PageViewStateProvider>().displayedDate));
}),
actions: const [ThemeSwitcherButton()],

View File

@ -2,6 +2,7 @@
/* Copyright (C) 2024 Marco Groß <mgross@sw-gross.de> */
import 'package:calorimeter/utils/settings.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
class AppDrawer extends StatelessWidget {
const AppDrawer({
@ -25,10 +26,11 @@ class AppDrawer extends StatelessWidget {
Navigator.of(context).pop();
},
),
title: const Text('Menü')),
title: Text(AppLocalizations.of(context)!.menu),
),
),
ListTile(
title: const Text('Einstellungen'),
title: Text(AppLocalizations.of(context)!.settings),
trailing: const Icon(Icons.settings),
onTap: () {
Navigator.of(context).pop();

View File

@ -15,7 +15,6 @@ class CalendarFAB extends StatelessWidget {
return FloatingActionButton(
onPressed: () async {
var datePicked = await showDatePicker(
locale: const Locale('de'),
context: context,
initialDate: startFromDate,
currentDate: DateTimeHelper.now(),

View File

@ -5,6 +5,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:calorimeter/utils/app_drawer.dart';
import 'package:calorimeter/utils/settings_bloc.dart';
import 'package:settings_ui/settings_ui.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
class SettingsWidget extends StatefulWidget {
const SettingsWidget({super.key});
@ -23,33 +24,24 @@ class _SettingsWidgetState extends State<SettingsWidget> {
builder: (context, state) {
return SettingsList(sections: [
SettingsSection(
title: const Text('Deine persönlichen Einstellungen'),
title: Text(AppLocalizations.of(context)!.yourSettings),
tiles: [
SettingsTile.navigation(
leading: const Icon(Icons.food_bank),
title: const Text('Kalorienlimit pro Tag'),
title: Text(AppLocalizations.of(context)!.dayLimit),
value: Text(state.kcalLimit.toString()),
onPressed: (context) async {
await showDialog(
builder: (ctx) {
return AlertDialog(
title: const Text("Kalorienlimit pro Tag"),
content: TextField(controller: kcalPerDayCtrl),
title: Text(AppLocalizations.of(context)!.dayLimit),
content: TextField(
controller: kcalPerDayCtrl,
onSubmitted: (val) => submitDailyKcal()),
actions: [
TextButton(
onPressed: () {
double setting;
try {
setting =
double.parse(kcalPerDayCtrl.text);
} catch (e) {
setting = 2000.0;
}
context.read<SettingsDataBloc>().add(
DailyKcalLimitUpdated(kcal: setting));
Navigator.of(context).pop();
},
child: const Text('Ok'))
onPressed: () => submitDailyKcal(),
child: Text(AppLocalizations.of(context)!.ok))
],
);
},
@ -60,7 +52,18 @@ class _SettingsWidgetState extends State<SettingsWidget> {
]);
}),
drawer: const AppDrawer(),
appBar: AppBar(title: const Text('Einstellungen')),
appBar: AppBar(title: Text(AppLocalizations.of(context)!.settings)),
);
}
void submitDailyKcal() {
double setting;
try {
setting = double.parse(kcalPerDayCtrl.text);
} catch (e) {
setting = 2000.0;
}
context.read<SettingsDataBloc>().add(DailyKcalLimitUpdated(kcal: setting));
Navigator.of(context).pop();
}
}

View File

@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:calorimeter/food_entry/food_entry_bloc.dart';
import 'package:calorimeter/utils/settings_bloc.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
class SumWidget extends StatelessWidget {
final DateTime date;
@ -48,7 +49,7 @@ class SumWidget extends StatelessWidget {
return Align(
alignment: Alignment.centerLeft,
child: Text(
'kcal heute: ${sum.ceil().toString()}/${settingsState.kcalLimit.ceil()}',
'${AppLocalizations.of(context)!.kcalToday}: ${sum.ceil().toString()}/${settingsState.kcalLimit.ceil()}',
style: Theme.of(context)
.textTheme
.bodyLarge!

View File

@ -0,0 +1,2 @@
With Calorimeter you can track your daily calorie intake.
As simple as it gets.

View File

@ -0,0 +1 @@
Track your calories!

1
metadata/en-US/title.txt Normal file
View File

@ -0,0 +1 @@
Calorimeter

View File

@ -30,6 +30,7 @@ dev_dependencies:
flutter:
uses-material-design: true
generate: true
flutter_launcher_icons:
android: "launcher_icon"