Localize app for German and English
This commit is contained in:
parent
be5cbb5988
commit
8fed59f6dd
3
l10n.yaml
Normal file
3
l10n.yaml
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
arb-dir: lib/l10n
|
||||||
|
template-arb-file: app_en.arb
|
||||||
|
output-localization-file: app_localizations.dart
|
@ -4,6 +4,7 @@ import 'package:calorimeter/storage/storage.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:calorimeter/food_entry/food_entry_bloc.dart';
|
import 'package:calorimeter/food_entry/food_entry_bloc.dart';
|
||||||
import 'package:calorimeter/utils/row_with_spacers_widget.dart';
|
import 'package:calorimeter/utils/row_with_spacers_widget.dart';
|
||||||
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||||
|
|
||||||
class EnterFoodWidget extends StatefulWidget {
|
class EnterFoodWidget extends StatefulWidget {
|
||||||
final Function(BuildContext context, FoodEntryState entry) onAdd;
|
final Function(BuildContext context, FoodEntryState entry) onAdd;
|
||||||
@ -42,8 +43,8 @@ class _EnterFoodWidgetState extends State<EnterFoodWidget> {
|
|||||||
return TextFormField(
|
return TextFormField(
|
||||||
controller: controller,
|
controller: controller,
|
||||||
focusNode: focusNode,
|
focusNode: focusNode,
|
||||||
decoration: const InputDecoration(
|
decoration: InputDecoration(
|
||||||
label: Text("Name"),
|
label: Text(AppLocalizations.of(context)!.name),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@ -70,9 +71,11 @@ class _EnterFoodWidgetState extends State<EnterFoodWidget> {
|
|||||||
}),
|
}),
|
||||||
TextField(
|
TextField(
|
||||||
textAlign: TextAlign.end,
|
textAlign: TextAlign.end,
|
||||||
decoration: const InputDecoration(
|
decoration: InputDecoration(
|
||||||
label:
|
label: Align(
|
||||||
Align(alignment: Alignment.centerRight, child: Text("Menge")),
|
alignment: Alignment.centerRight,
|
||||||
|
child: Text(AppLocalizations.of(context)!.amount),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
keyboardType: TextInputType.number,
|
keyboardType: TextInputType.number,
|
||||||
controller: massController,
|
controller: massController,
|
||||||
@ -80,9 +83,10 @@ class _EnterFoodWidgetState extends State<EnterFoodWidget> {
|
|||||||
),
|
),
|
||||||
TextField(
|
TextField(
|
||||||
textAlign: TextAlign.end,
|
textAlign: TextAlign.end,
|
||||||
decoration: const InputDecoration(
|
decoration: InputDecoration(
|
||||||
label: Align(
|
label: Align(
|
||||||
alignment: Alignment.centerRight, child: Text("kcal pro"))),
|
alignment: Alignment.centerRight,
|
||||||
|
child: Text(AppLocalizations.of(context)!.kcalper))),
|
||||||
keyboardType: TextInputType.number,
|
keyboardType: TextInputType.number,
|
||||||
controller: kcalPerMassController,
|
controller: kcalPerMassController,
|
||||||
onSubmitted: (value) => onSubmitAction(),
|
onSubmitted: (value) => onSubmitAction(),
|
||||||
@ -107,7 +111,8 @@ class _EnterFoodWidgetState extends State<EnterFoodWidget> {
|
|||||||
try {
|
try {
|
||||||
massAsNumber = int.parse(massController.text.replaceAll(",", "."));
|
massAsNumber = int.parse(massController.text.replaceAll(",", "."));
|
||||||
} catch (e) {
|
} 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).clearSnackBars();
|
||||||
ScaffoldMessenger.of(context).showSnackBar(snackbar);
|
ScaffoldMessenger.of(context).showSnackBar(snackbar);
|
||||||
return;
|
return;
|
||||||
@ -117,8 +122,8 @@ class _EnterFoodWidgetState extends State<EnterFoodWidget> {
|
|||||||
kcalPerMassAsNumber =
|
kcalPerMassAsNumber =
|
||||||
int.parse(kcalPerMassController.text.replaceAll(",", "."));
|
int.parse(kcalPerMassController.text.replaceAll(",", "."));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
var snackbar =
|
var snackbar = SnackBar(
|
||||||
const SnackBar(content: Text("'kcal pro 100g' muss eine Zahl sein"));
|
content: Text(AppLocalizations.of(context)!.errKcalNotANumber));
|
||||||
ScaffoldMessenger.of(context).removeCurrentSnackBar();
|
ScaffoldMessenger.of(context).removeCurrentSnackBar();
|
||||||
ScaffoldMessenger.of(context).showSnackBar(snackbar);
|
ScaffoldMessenger.of(context).showSnackBar(snackbar);
|
||||||
return;
|
return;
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:calorimeter/food_entry/food_entry_bloc.dart';
|
import 'package:calorimeter/food_entry/food_entry_bloc.dart';
|
||||||
import 'package:calorimeter/utils/row_with_spacers_widget.dart';
|
import 'package:calorimeter/utils/row_with_spacers_widget.dart';
|
||||||
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||||
|
|
||||||
class FoodEntryWidget extends StatefulWidget {
|
class FoodEntryWidget extends StatefulWidget {
|
||||||
final FoodEntryState entry;
|
final FoodEntryState entry;
|
||||||
@ -165,8 +166,8 @@ class _FoodEntryChangeDialogState extends State<FoodEntryChangeDialog> {
|
|||||||
child: TextField(
|
child: TextField(
|
||||||
onSubmitted: (val) => _onSubmitAction(),
|
onSubmitted: (val) => _onSubmitAction(),
|
||||||
controller: nameController,
|
controller: nameController,
|
||||||
decoration: const InputDecoration(
|
decoration: InputDecoration(
|
||||||
label: Text("Name"),
|
label: Text(AppLocalizations.of(context)!.name),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -177,8 +178,8 @@ class _FoodEntryChangeDialogState extends State<FoodEntryChangeDialog> {
|
|||||||
child: TextField(
|
child: TextField(
|
||||||
onSubmitted: (val) => _onSubmitAction(),
|
onSubmitted: (val) => _onSubmitAction(),
|
||||||
controller: massController,
|
controller: massController,
|
||||||
decoration: const InputDecoration(
|
decoration: InputDecoration(
|
||||||
label: Text("Menge"),
|
label: Text(AppLocalizations.of(context)!.amount),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -189,8 +190,8 @@ class _FoodEntryChangeDialogState extends State<FoodEntryChangeDialog> {
|
|||||||
child: TextField(
|
child: TextField(
|
||||||
onSubmitted: (val) => _onSubmitAction(),
|
onSubmitted: (val) => _onSubmitAction(),
|
||||||
controller: kcalPer100Controller,
|
controller: kcalPer100Controller,
|
||||||
decoration: const InputDecoration(
|
decoration: InputDecoration(
|
||||||
label: Text("kcal pro Menge"),
|
label: Text(AppLocalizations.of(context)!.kcalper),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
13
lib/l10n/app_de.arb
Normal file
13
lib/l10n/app_de.arb
Normal 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
13
lib/l10n/app_en.arb
Normal 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"
|
||||||
|
}
|
@ -8,8 +8,8 @@ import 'package:calorimeter/utils/settings_bloc.dart';
|
|||||||
import 'package:calorimeter/utils/theme_bloc.dart';
|
import 'package:calorimeter/utils/theme_bloc.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:flutter_localizations/flutter_localizations.dart';
|
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||||
|
|
||||||
List<FoodEntryState> entriesForToday = [];
|
List<FoodEntryState> entriesForToday = [];
|
||||||
DateTime timeNow = DateTime.now();
|
DateTime timeNow = DateTime.now();
|
||||||
@ -17,8 +17,6 @@ DateTime timeNow = DateTime.now();
|
|||||||
void main() async {
|
void main() async {
|
||||||
WidgetsFlutterBinding.ensureInitialized();
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
|
|
||||||
WidgetsFlutterBinding.ensureInitialized();
|
|
||||||
|
|
||||||
var storage = await FoodStorage.create();
|
var storage = await FoodStorage.create();
|
||||||
await storage.buildFoodLookupDatabase();
|
await storage.buildFoodLookupDatabase();
|
||||||
|
|
||||||
@ -79,6 +77,7 @@ class MainApp extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return MaterialApp.router(
|
return MaterialApp.router(
|
||||||
|
//locale: Locale('de'),
|
||||||
routerConfig: GoRouter(
|
routerConfig: GoRouter(
|
||||||
routes: [
|
routes: [
|
||||||
GoRoute(
|
GoRoute(
|
||||||
@ -91,14 +90,8 @@ class MainApp extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
localizationsDelegates: const [
|
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
||||||
GlobalMaterialLocalizations.delegate,
|
supportedLocales: AppLocalizations.supportedLocales,
|
||||||
GlobalWidgetsLocalizations.delegate,
|
|
||||||
GlobalCupertinoLocalizations.delegate,
|
|
||||||
],
|
|
||||||
supportedLocales: const [
|
|
||||||
Locale('de'),
|
|
||||||
],
|
|
||||||
theme: ThemeData(
|
theme: ThemeData(
|
||||||
dividerTheme: const DividerThemeData(space: 2),
|
dividerTheme: const DividerThemeData(space: 2),
|
||||||
colorScheme: ColorScheme.fromSeed(
|
colorScheme: ColorScheme.fromSeed(
|
||||||
|
@ -58,7 +58,9 @@ class PerDatePageViewController extends StatelessWidget {
|
|||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Builder(builder: (context) {
|
title: Builder(builder: (context) {
|
||||||
return Text(DateFormat.yMMMMd('de').format(
|
return Text(DateFormat.yMMMMd(
|
||||||
|
Localizations.localeOf(context).toString())
|
||||||
|
.format(
|
||||||
context.watch<PageViewStateProvider>().displayedDate));
|
context.watch<PageViewStateProvider>().displayedDate));
|
||||||
}),
|
}),
|
||||||
actions: const [ThemeSwitcherButton()],
|
actions: const [ThemeSwitcherButton()],
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
/* Copyright (C) 2024 Marco Groß <mgross@sw-gross.de> */
|
/* Copyright (C) 2024 Marco Groß <mgross@sw-gross.de> */
|
||||||
import 'package:calorimeter/utils/settings.dart';
|
import 'package:calorimeter/utils/settings.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||||
|
|
||||||
class AppDrawer extends StatelessWidget {
|
class AppDrawer extends StatelessWidget {
|
||||||
const AppDrawer({
|
const AppDrawer({
|
||||||
@ -25,10 +26,11 @@ class AppDrawer extends StatelessWidget {
|
|||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
title: const Text('Menü')),
|
title: Text(AppLocalizations.of(context)!.menu),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
title: const Text('Einstellungen'),
|
title: Text(AppLocalizations.of(context)!.settings),
|
||||||
trailing: const Icon(Icons.settings),
|
trailing: const Icon(Icons.settings),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
|
@ -15,7 +15,6 @@ class CalendarFAB extends StatelessWidget {
|
|||||||
return FloatingActionButton(
|
return FloatingActionButton(
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
var datePicked = await showDatePicker(
|
var datePicked = await showDatePicker(
|
||||||
locale: const Locale('de'),
|
|
||||||
context: context,
|
context: context,
|
||||||
initialDate: startFromDate,
|
initialDate: startFromDate,
|
||||||
currentDate: DateTimeHelper.now(),
|
currentDate: DateTimeHelper.now(),
|
||||||
|
@ -5,6 +5,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
|
|||||||
import 'package:calorimeter/utils/app_drawer.dart';
|
import 'package:calorimeter/utils/app_drawer.dart';
|
||||||
import 'package:calorimeter/utils/settings_bloc.dart';
|
import 'package:calorimeter/utils/settings_bloc.dart';
|
||||||
import 'package:settings_ui/settings_ui.dart';
|
import 'package:settings_ui/settings_ui.dart';
|
||||||
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||||
|
|
||||||
class SettingsWidget extends StatefulWidget {
|
class SettingsWidget extends StatefulWidget {
|
||||||
const SettingsWidget({super.key});
|
const SettingsWidget({super.key});
|
||||||
@ -23,33 +24,24 @@ class _SettingsWidgetState extends State<SettingsWidget> {
|
|||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
return SettingsList(sections: [
|
return SettingsList(sections: [
|
||||||
SettingsSection(
|
SettingsSection(
|
||||||
title: const Text('Deine persönlichen Einstellungen'),
|
title: Text(AppLocalizations.of(context)!.yourSettings),
|
||||||
tiles: [
|
tiles: [
|
||||||
SettingsTile.navigation(
|
SettingsTile.navigation(
|
||||||
leading: const Icon(Icons.food_bank),
|
leading: const Icon(Icons.food_bank),
|
||||||
title: const Text('Kalorienlimit pro Tag'),
|
title: Text(AppLocalizations.of(context)!.dayLimit),
|
||||||
value: Text(state.kcalLimit.toString()),
|
value: Text(state.kcalLimit.toString()),
|
||||||
onPressed: (context) async {
|
onPressed: (context) async {
|
||||||
await showDialog(
|
await showDialog(
|
||||||
builder: (ctx) {
|
builder: (ctx) {
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
title: const Text("Kalorienlimit pro Tag"),
|
title: Text(AppLocalizations.of(context)!.dayLimit),
|
||||||
content: TextField(controller: kcalPerDayCtrl),
|
content: TextField(
|
||||||
|
controller: kcalPerDayCtrl,
|
||||||
|
onSubmitted: (val) => submitDailyKcal()),
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () {
|
onPressed: () => submitDailyKcal(),
|
||||||
double setting;
|
child: Text(AppLocalizations.of(context)!.ok))
|
||||||
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'))
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@ -60,7 +52,18 @@ class _SettingsWidgetState extends State<SettingsWidget> {
|
|||||||
]);
|
]);
|
||||||
}),
|
}),
|
||||||
drawer: const AppDrawer(),
|
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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:calorimeter/food_entry/food_entry_bloc.dart';
|
import 'package:calorimeter/food_entry/food_entry_bloc.dart';
|
||||||
import 'package:calorimeter/utils/settings_bloc.dart';
|
import 'package:calorimeter/utils/settings_bloc.dart';
|
||||||
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||||
|
|
||||||
class SumWidget extends StatelessWidget {
|
class SumWidget extends StatelessWidget {
|
||||||
final DateTime date;
|
final DateTime date;
|
||||||
@ -48,7 +49,7 @@ class SumWidget extends StatelessWidget {
|
|||||||
return Align(
|
return Align(
|
||||||
alignment: Alignment.centerLeft,
|
alignment: Alignment.centerLeft,
|
||||||
child: Text(
|
child: Text(
|
||||||
'kcal heute: ${sum.ceil().toString()}/${settingsState.kcalLimit.ceil()}',
|
'${AppLocalizations.of(context)!.kcalToday}: ${sum.ceil().toString()}/${settingsState.kcalLimit.ceil()}',
|
||||||
style: Theme.of(context)
|
style: Theme.of(context)
|
||||||
.textTheme
|
.textTheme
|
||||||
.bodyLarge!
|
.bodyLarge!
|
||||||
|
2
metadata/en-US/full_description.txt
Normal file
2
metadata/en-US/full_description.txt
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
With Calorimeter you can track your daily calorie intake.
|
||||||
|
As simple as it gets.
|
1
metadata/en-US/short_description.txt
Normal file
1
metadata/en-US/short_description.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
Track your calories!
|
1
metadata/en-US/title.txt
Normal file
1
metadata/en-US/title.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
Calorimeter
|
@ -30,6 +30,7 @@ dev_dependencies:
|
|||||||
|
|
||||||
flutter:
|
flutter:
|
||||||
uses-material-design: true
|
uses-material-design: true
|
||||||
|
generate: true
|
||||||
|
|
||||||
flutter_launcher_icons:
|
flutter_launcher_icons:
|
||||||
android: "launcher_icon"
|
android: "launcher_icon"
|
||||||
|
Loading…
Reference in New Issue
Block a user