Add i18n support and integrate localized strings across modules (Login, AppShell, etc.)

This commit is contained in:
2025-09-27 12:15:57 +02:00
parent 0a0e421158
commit 140e3a7328
8 changed files with 576 additions and 71 deletions

View File

@@ -1,8 +1,30 @@
{ {
"hello": "Hallo $name", "hello": "Hallo $name",
"login": { "login": {
"title": "Login",
"pleaseSignIn": "Bitte melden Sie sich an",
"signingIn": "Melde Sie an…",
"success": "Login erfolgreich" "success": "Login erfolgreich"
}, },
"dashboard": {
"welcome": "Dashboard Willkommen bei Finlog"
},
"budget": {
"title": "Budgets"
},
"app": {
"navigationSettings": "Einstellungen",
"navigationDashboard": "Dashboard",
"navigationBudgets": "Budgets",
"navigationInventory": "Inventar",
"navigationReports": "Berichte",
"tooltipMenu": "Menü",
"tooltipNotifications": "Benachrichtigungen",
"tooltipUserSettings": "Benutzer-Einstellungen",
"tooltipCollapseRail": "Leiste verkleinern",
"tooltipExpandRail": "Leiste erweitern",
"drawerSettings": "Einstellungen"
},
"settings": { "settings": {
"title": "Einstellungen", "title": "Einstellungen",
"sections": { "sections": {
@@ -21,6 +43,38 @@
}, },
"messages": { "messages": {
"logoutNotImplemented": "Logout… (noch nicht implementiert)" "logoutNotImplemented": "Logout… (noch nicht implementiert)"
},
"app": {
"systemBackground": "System-Hintergrundfarbe",
"systemDefault": "Systemstandard",
"darkMode": "Dark Mode",
"lightMode": "Light Mode",
"textSize": "Textgröße",
"system": "System",
"small": "Klein",
"medium": "Mittel",
"large": "Groß",
"language": "Sprache",
"german": "Deutsch",
"english": "Englisch"
},
"personalData": {
"name": "Name",
"maxMustermann": "Max Mustermann",
"changePassword": "Passwort ändern",
"twoFactor": "2-Faktor-Authentifizierung",
"off": "Aus"
},
"accountManagement": {
"email": "E-Mail"
},
"help": {
"faq": "FAQ",
"sendFeedback": "Feedback senden"
},
"legal": {
"privacy": "Datenschutz",
"termsOfService": "Nutzungsbedingungen"
} }
} }
} }

View File

@@ -1,8 +1,30 @@
{ {
"hello": "Hello $name", "hello": "Hello $name",
"login": { "login": {
"title": "Login",
"pleaseSignIn": "Please sign in",
"signingIn": "Signing you in…",
"success": "Logged in successfully" "success": "Logged in successfully"
}, },
"dashboard": {
"welcome": "Dashboard Welcome to Finlog"
},
"budget": {
"title": "Budgets"
},
"app": {
"navigationSettings": "Settings",
"navigationDashboard": "Dashboard",
"navigationBudgets": "Budgets",
"navigationInventory": "Inventory",
"navigationReports": "Reports",
"tooltipMenu": "Menu",
"tooltipNotifications": "Notifications",
"tooltipUserSettings": "User Settings",
"tooltipCollapseRail": "Collapse Rail",
"tooltipExpandRail": "Expand Rail",
"drawerSettings": "Settings"
},
"settings": { "settings": {
"title": "Settings", "title": "Settings",
"sections": { "sections": {
@@ -21,6 +43,38 @@
}, },
"messages": { "messages": {
"logoutNotImplemented": "Logout… (not implemented yet)" "logoutNotImplemented": "Logout… (not implemented yet)"
},
"app": {
"systemBackground": "System Background Color",
"systemDefault": "System Default",
"darkMode": "Dark Mode",
"lightMode": "Light Mode",
"textSize": "Text Size",
"system": "System",
"small": "Small",
"medium": "Medium",
"large": "Large",
"language": "Language",
"german": "German",
"english": "English"
},
"personalData": {
"name": "Name",
"maxMustermann": "Max Mustermann",
"changePassword": "Change Password",
"twoFactor": "Two-Factor Authentication",
"off": "Off"
},
"accountManagement": {
"email": "Email"
},
"help": {
"faq": "FAQ",
"sendFeedback": "Send Feedback"
},
"legal": {
"privacy": "Privacy",
"termsOfService": "Terms of Service"
} }
} }
} }

View File

@@ -4,9 +4,9 @@
/// To regenerate, run: `dart run slang` /// To regenerate, run: `dart run slang`
/// ///
/// Locales: 2 /// Locales: 2
/// Strings: 28 (14 per locale) /// Strings: 104 (52 per locale)
/// ///
/// Built on 2025-09-27 at 09:55 UTC /// Built on 2025-09-27 at 10:12 UTC
// coverage:ignore-file // coverage:ignore-file
// ignore_for_file: type=lint, unused_import // ignore_for_file: type=lint, unused_import

View File

@@ -38,6 +38,9 @@ class TranslationsDe implements Translations {
// Translations // Translations
@override String hello({required Object name}) => 'Hallo ${name}'; @override String hello({required Object name}) => 'Hallo ${name}';
@override late final _TranslationsLoginDe login = _TranslationsLoginDe._(_root); @override late final _TranslationsLoginDe login = _TranslationsLoginDe._(_root);
@override late final _TranslationsDashboardDe dashboard = _TranslationsDashboardDe._(_root);
@override late final _TranslationsBudgetDe budget = _TranslationsBudgetDe._(_root);
@override late final _TranslationsAppDe app = _TranslationsAppDe._(_root);
@override late final _TranslationsSettingsDe settings = _TranslationsSettingsDe._(_root); @override late final _TranslationsSettingsDe settings = _TranslationsSettingsDe._(_root);
} }
@@ -48,9 +51,52 @@ class _TranslationsLoginDe implements TranslationsLoginEn {
final TranslationsDe _root; // ignore: unused_field final TranslationsDe _root; // ignore: unused_field
// Translations // Translations
@override String get title => 'Login';
@override String get pleaseSignIn => 'Bitte melden Sie sich an';
@override String get signingIn => 'Melde Sie an…';
@override String get success => 'Login erfolgreich'; @override String get success => 'Login erfolgreich';
} }
// Path: dashboard
class _TranslationsDashboardDe implements TranslationsDashboardEn {
_TranslationsDashboardDe._(this._root);
final TranslationsDe _root; // ignore: unused_field
// Translations
@override String get welcome => 'Dashboard Willkommen bei Finlog';
}
// Path: budget
class _TranslationsBudgetDe implements TranslationsBudgetEn {
_TranslationsBudgetDe._(this._root);
final TranslationsDe _root; // ignore: unused_field
// Translations
@override String get title => 'Budgets';
}
// Path: app
class _TranslationsAppDe implements TranslationsAppEn {
_TranslationsAppDe._(this._root);
final TranslationsDe _root; // ignore: unused_field
// Translations
@override String get navigationSettings => 'Einstellungen';
@override String get navigationDashboard => 'Dashboard';
@override String get navigationBudgets => 'Budgets';
@override String get navigationInventory => 'Inventar';
@override String get navigationReports => 'Berichte';
@override String get tooltipMenu => 'Menü';
@override String get tooltipNotifications => 'Benachrichtigungen';
@override String get tooltipUserSettings => 'Benutzer-Einstellungen';
@override String get tooltipCollapseRail => 'Leiste verkleinern';
@override String get tooltipExpandRail => 'Leiste erweitern';
@override String get drawerSettings => 'Einstellungen';
}
// Path: settings // Path: settings
class _TranslationsSettingsDe implements TranslationsSettingsEn { class _TranslationsSettingsDe implements TranslationsSettingsEn {
_TranslationsSettingsDe._(this._root); _TranslationsSettingsDe._(this._root);
@@ -62,6 +108,11 @@ class _TranslationsSettingsDe implements TranslationsSettingsEn {
@override late final _TranslationsSettingsSectionsDe sections = _TranslationsSettingsSectionsDe._(_root); @override late final _TranslationsSettingsSectionsDe sections = _TranslationsSettingsSectionsDe._(_root);
@override late final _TranslationsSettingsItemsDe items = _TranslationsSettingsItemsDe._(_root); @override late final _TranslationsSettingsItemsDe items = _TranslationsSettingsItemsDe._(_root);
@override late final _TranslationsSettingsMessagesDe messages = _TranslationsSettingsMessagesDe._(_root); @override late final _TranslationsSettingsMessagesDe messages = _TranslationsSettingsMessagesDe._(_root);
@override late final _TranslationsSettingsAppDe app = _TranslationsSettingsAppDe._(_root);
@override late final _TranslationsSettingsPersonalDataDe personalData = _TranslationsSettingsPersonalDataDe._(_root);
@override late final _TranslationsSettingsAccountManagementDe accountManagement = _TranslationsSettingsAccountManagementDe._(_root);
@override late final _TranslationsSettingsHelpDe help = _TranslationsSettingsHelpDe._(_root);
@override late final _TranslationsSettingsLegalDe legal = _TranslationsSettingsLegalDe._(_root);
} }
// Path: settings.sections // Path: settings.sections
@@ -102,13 +153,96 @@ class _TranslationsSettingsMessagesDe implements TranslationsSettingsMessagesEn
@override String get logoutNotImplemented => 'Logout… (noch nicht implementiert)'; @override String get logoutNotImplemented => 'Logout… (noch nicht implementiert)';
} }
// Path: settings.app
class _TranslationsSettingsAppDe implements TranslationsSettingsAppEn {
_TranslationsSettingsAppDe._(this._root);
final TranslationsDe _root; // ignore: unused_field
// Translations
@override String get systemBackground => 'System-Hintergrundfarbe';
@override String get systemDefault => 'Systemstandard';
@override String get darkMode => 'Dark Mode';
@override String get lightMode => 'Light Mode';
@override String get textSize => 'Textgröße';
@override String get system => 'System';
@override String get small => 'Klein';
@override String get medium => 'Mittel';
@override String get large => 'Groß';
@override String get language => 'Sprache';
@override String get german => 'Deutsch';
@override String get english => 'Englisch';
}
// Path: settings.personalData
class _TranslationsSettingsPersonalDataDe implements TranslationsSettingsPersonalDataEn {
_TranslationsSettingsPersonalDataDe._(this._root);
final TranslationsDe _root; // ignore: unused_field
// Translations
@override String get name => 'Name';
@override String get maxMustermann => 'Max Mustermann';
@override String get changePassword => 'Passwort ändern';
@override String get twoFactor => '2-Faktor-Authentifizierung';
@override String get off => 'Aus';
}
// Path: settings.accountManagement
class _TranslationsSettingsAccountManagementDe implements TranslationsSettingsAccountManagementEn {
_TranslationsSettingsAccountManagementDe._(this._root);
final TranslationsDe _root; // ignore: unused_field
// Translations
@override String get email => 'E-Mail';
}
// Path: settings.help
class _TranslationsSettingsHelpDe implements TranslationsSettingsHelpEn {
_TranslationsSettingsHelpDe._(this._root);
final TranslationsDe _root; // ignore: unused_field
// Translations
@override String get faq => 'FAQ';
@override String get sendFeedback => 'Feedback senden';
}
// Path: settings.legal
class _TranslationsSettingsLegalDe implements TranslationsSettingsLegalEn {
_TranslationsSettingsLegalDe._(this._root);
final TranslationsDe _root; // ignore: unused_field
// Translations
@override String get privacy => 'Datenschutz';
@override String get termsOfService => 'Nutzungsbedingungen';
}
/// Flat map(s) containing all translations. /// Flat map(s) containing all translations.
/// Only for edge cases! For simple maps, use the map function of this library. /// Only for edge cases! For simple maps, use the map function of this library.
extension on TranslationsDe { extension on TranslationsDe {
dynamic _flatMapFunction(String path) { dynamic _flatMapFunction(String path) {
switch (path) { switch (path) {
case 'hello': return ({required Object name}) => 'Hallo ${name}'; case 'hello': return ({required Object name}) => 'Hallo ${name}';
case 'login.title': return 'Login';
case 'login.pleaseSignIn': return 'Bitte melden Sie sich an';
case 'login.signingIn': return 'Melde Sie an…';
case 'login.success': return 'Login erfolgreich'; case 'login.success': return 'Login erfolgreich';
case 'dashboard.welcome': return 'Dashboard Willkommen bei Finlog';
case 'budget.title': return 'Budgets';
case 'app.navigationSettings': return 'Einstellungen';
case 'app.navigationDashboard': return 'Dashboard';
case 'app.navigationBudgets': return 'Budgets';
case 'app.navigationInventory': return 'Inventar';
case 'app.navigationReports': return 'Berichte';
case 'app.tooltipMenu': return 'Menü';
case 'app.tooltipNotifications': return 'Benachrichtigungen';
case 'app.tooltipUserSettings': return 'Benutzer-Einstellungen';
case 'app.tooltipCollapseRail': return 'Leiste verkleinern';
case 'app.tooltipExpandRail': return 'Leiste erweitern';
case 'app.drawerSettings': return 'Einstellungen';
case 'settings.title': return 'Einstellungen'; case 'settings.title': return 'Einstellungen';
case 'settings.sections.account': return 'Konto & Daten'; case 'settings.sections.account': return 'Konto & Daten';
case 'settings.sections.app': return 'App'; case 'settings.sections.app': return 'App';
@@ -121,6 +255,28 @@ extension on TranslationsDe {
case 'settings.items.legalPrivacy': return 'Rechtliches & Datenschutz'; case 'settings.items.legalPrivacy': return 'Rechtliches & Datenschutz';
case 'settings.items.logout': return 'Abmelden'; case 'settings.items.logout': return 'Abmelden';
case 'settings.messages.logoutNotImplemented': return 'Logout… (noch nicht implementiert)'; case 'settings.messages.logoutNotImplemented': return 'Logout… (noch nicht implementiert)';
case 'settings.app.systemBackground': return 'System-Hintergrundfarbe';
case 'settings.app.systemDefault': return 'Systemstandard';
case 'settings.app.darkMode': return 'Dark Mode';
case 'settings.app.lightMode': return 'Light Mode';
case 'settings.app.textSize': return 'Textgröße';
case 'settings.app.system': return 'System';
case 'settings.app.small': return 'Klein';
case 'settings.app.medium': return 'Mittel';
case 'settings.app.large': return 'Groß';
case 'settings.app.language': return 'Sprache';
case 'settings.app.german': return 'Deutsch';
case 'settings.app.english': return 'Englisch';
case 'settings.personalData.name': return 'Name';
case 'settings.personalData.maxMustermann': return 'Max Mustermann';
case 'settings.personalData.changePassword': return 'Passwort ändern';
case 'settings.personalData.twoFactor': return '2-Faktor-Authentifizierung';
case 'settings.personalData.off': return 'Aus';
case 'settings.accountManagement.email': return 'E-Mail';
case 'settings.help.faq': return 'FAQ';
case 'settings.help.sendFeedback': return 'Feedback senden';
case 'settings.legal.privacy': return 'Datenschutz';
case 'settings.legal.termsOfService': return 'Nutzungsbedingungen';
default: return null; default: return null;
} }
} }

View File

@@ -44,6 +44,9 @@ class Translations implements BaseTranslations<AppLocale, Translations> {
String hello({required Object name}) => 'Hello ${name}'; String hello({required Object name}) => 'Hello ${name}';
late final TranslationsLoginEn login = TranslationsLoginEn._(_root); late final TranslationsLoginEn login = TranslationsLoginEn._(_root);
late final TranslationsDashboardEn dashboard = TranslationsDashboardEn._(_root);
late final TranslationsBudgetEn budget = TranslationsBudgetEn._(_root);
late final TranslationsAppEn app = TranslationsAppEn._(_root);
late final TranslationsSettingsEn settings = TranslationsSettingsEn._(_root); late final TranslationsSettingsEn settings = TranslationsSettingsEn._(_root);
} }
@@ -55,10 +58,85 @@ class TranslationsLoginEn {
// Translations // Translations
/// en: 'Login'
String get title => 'Login';
/// en: 'Please sign in'
String get pleaseSignIn => 'Please sign in';
/// en: 'Signing you in…'
String get signingIn => 'Signing you in…';
/// en: 'Logged in successfully' /// en: 'Logged in successfully'
String get success => 'Logged in successfully'; String get success => 'Logged in successfully';
} }
// Path: dashboard
class TranslationsDashboardEn {
TranslationsDashboardEn._(this._root);
final Translations _root; // ignore: unused_field
// Translations
/// en: 'Dashboard Welcome to Finlog'
String get welcome => 'Dashboard Welcome to Finlog';
}
// Path: budget
class TranslationsBudgetEn {
TranslationsBudgetEn._(this._root);
final Translations _root; // ignore: unused_field
// Translations
/// en: 'Budgets'
String get title => 'Budgets';
}
// Path: app
class TranslationsAppEn {
TranslationsAppEn._(this._root);
final Translations _root; // ignore: unused_field
// Translations
/// en: 'Settings'
String get navigationSettings => 'Settings';
/// en: 'Dashboard'
String get navigationDashboard => 'Dashboard';
/// en: 'Budgets'
String get navigationBudgets => 'Budgets';
/// en: 'Inventory'
String get navigationInventory => 'Inventory';
/// en: 'Reports'
String get navigationReports => 'Reports';
/// en: 'Menu'
String get tooltipMenu => 'Menu';
/// en: 'Notifications'
String get tooltipNotifications => 'Notifications';
/// en: 'User Settings'
String get tooltipUserSettings => 'User Settings';
/// en: 'Collapse Rail'
String get tooltipCollapseRail => 'Collapse Rail';
/// en: 'Expand Rail'
String get tooltipExpandRail => 'Expand Rail';
/// en: 'Settings'
String get drawerSettings => 'Settings';
}
// Path: settings // Path: settings
class TranslationsSettingsEn { class TranslationsSettingsEn {
TranslationsSettingsEn._(this._root); TranslationsSettingsEn._(this._root);
@@ -73,6 +151,11 @@ class TranslationsSettingsEn {
late final TranslationsSettingsSectionsEn sections = TranslationsSettingsSectionsEn._(_root); late final TranslationsSettingsSectionsEn sections = TranslationsSettingsSectionsEn._(_root);
late final TranslationsSettingsItemsEn items = TranslationsSettingsItemsEn._(_root); late final TranslationsSettingsItemsEn items = TranslationsSettingsItemsEn._(_root);
late final TranslationsSettingsMessagesEn messages = TranslationsSettingsMessagesEn._(_root); late final TranslationsSettingsMessagesEn messages = TranslationsSettingsMessagesEn._(_root);
late final TranslationsSettingsAppEn app = TranslationsSettingsAppEn._(_root);
late final TranslationsSettingsPersonalDataEn personalData = TranslationsSettingsPersonalDataEn._(_root);
late final TranslationsSettingsAccountManagementEn accountManagement = TranslationsSettingsAccountManagementEn._(_root);
late final TranslationsSettingsHelpEn help = TranslationsSettingsHelpEn._(_root);
late final TranslationsSettingsLegalEn legal = TranslationsSettingsLegalEn._(_root);
} }
// Path: settings.sections // Path: settings.sections
@@ -135,13 +218,140 @@ class TranslationsSettingsMessagesEn {
String get logoutNotImplemented => 'Logout… (not implemented yet)'; String get logoutNotImplemented => 'Logout… (not implemented yet)';
} }
// Path: settings.app
class TranslationsSettingsAppEn {
TranslationsSettingsAppEn._(this._root);
final Translations _root; // ignore: unused_field
// Translations
/// en: 'System Background Color'
String get systemBackground => 'System Background Color';
/// en: 'System Default'
String get systemDefault => 'System Default';
/// en: 'Dark Mode'
String get darkMode => 'Dark Mode';
/// en: 'Light Mode'
String get lightMode => 'Light Mode';
/// en: 'Text Size'
String get textSize => 'Text Size';
/// en: 'System'
String get system => 'System';
/// en: 'Small'
String get small => 'Small';
/// en: 'Medium'
String get medium => 'Medium';
/// en: 'Large'
String get large => 'Large';
/// en: 'Language'
String get language => 'Language';
/// en: 'German'
String get german => 'German';
/// en: 'English'
String get english => 'English';
}
// Path: settings.personalData
class TranslationsSettingsPersonalDataEn {
TranslationsSettingsPersonalDataEn._(this._root);
final Translations _root; // ignore: unused_field
// Translations
/// en: 'Name'
String get name => 'Name';
/// en: 'Max Mustermann'
String get maxMustermann => 'Max Mustermann';
/// en: 'Change Password'
String get changePassword => 'Change Password';
/// en: 'Two-Factor Authentication'
String get twoFactor => 'Two-Factor Authentication';
/// en: 'Off'
String get off => 'Off';
}
// Path: settings.accountManagement
class TranslationsSettingsAccountManagementEn {
TranslationsSettingsAccountManagementEn._(this._root);
final Translations _root; // ignore: unused_field
// Translations
/// en: 'Email'
String get email => 'Email';
}
// Path: settings.help
class TranslationsSettingsHelpEn {
TranslationsSettingsHelpEn._(this._root);
final Translations _root; // ignore: unused_field
// Translations
/// en: 'FAQ'
String get faq => 'FAQ';
/// en: 'Send Feedback'
String get sendFeedback => 'Send Feedback';
}
// Path: settings.legal
class TranslationsSettingsLegalEn {
TranslationsSettingsLegalEn._(this._root);
final Translations _root; // ignore: unused_field
// Translations
/// en: 'Privacy'
String get privacy => 'Privacy';
/// en: 'Terms of Service'
String get termsOfService => 'Terms of Service';
}
/// Flat map(s) containing all translations. /// Flat map(s) containing all translations.
/// Only for edge cases! For simple maps, use the map function of this library. /// Only for edge cases! For simple maps, use the map function of this library.
extension on Translations { extension on Translations {
dynamic _flatMapFunction(String path) { dynamic _flatMapFunction(String path) {
switch (path) { switch (path) {
case 'hello': return ({required Object name}) => 'Hello ${name}'; case 'hello': return ({required Object name}) => 'Hello ${name}';
case 'login.title': return 'Login';
case 'login.pleaseSignIn': return 'Please sign in';
case 'login.signingIn': return 'Signing you in…';
case 'login.success': return 'Logged in successfully'; case 'login.success': return 'Logged in successfully';
case 'dashboard.welcome': return 'Dashboard Welcome to Finlog';
case 'budget.title': return 'Budgets';
case 'app.navigationSettings': return 'Settings';
case 'app.navigationDashboard': return 'Dashboard';
case 'app.navigationBudgets': return 'Budgets';
case 'app.navigationInventory': return 'Inventory';
case 'app.navigationReports': return 'Reports';
case 'app.tooltipMenu': return 'Menu';
case 'app.tooltipNotifications': return 'Notifications';
case 'app.tooltipUserSettings': return 'User Settings';
case 'app.tooltipCollapseRail': return 'Collapse Rail';
case 'app.tooltipExpandRail': return 'Expand Rail';
case 'app.drawerSettings': return 'Settings';
case 'settings.title': return 'Settings'; case 'settings.title': return 'Settings';
case 'settings.sections.account': return 'Account & Data'; case 'settings.sections.account': return 'Account & Data';
case 'settings.sections.app': return 'App'; case 'settings.sections.app': return 'App';
@@ -154,6 +364,28 @@ extension on Translations {
case 'settings.items.legalPrivacy': return 'Legal & Privacy'; case 'settings.items.legalPrivacy': return 'Legal & Privacy';
case 'settings.items.logout': return 'Sign out'; case 'settings.items.logout': return 'Sign out';
case 'settings.messages.logoutNotImplemented': return 'Logout… (not implemented yet)'; case 'settings.messages.logoutNotImplemented': return 'Logout… (not implemented yet)';
case 'settings.app.systemBackground': return 'System Background Color';
case 'settings.app.systemDefault': return 'System Default';
case 'settings.app.darkMode': return 'Dark Mode';
case 'settings.app.lightMode': return 'Light Mode';
case 'settings.app.textSize': return 'Text Size';
case 'settings.app.system': return 'System';
case 'settings.app.small': return 'Small';
case 'settings.app.medium': return 'Medium';
case 'settings.app.large': return 'Large';
case 'settings.app.language': return 'Language';
case 'settings.app.german': return 'German';
case 'settings.app.english': return 'English';
case 'settings.personalData.name': return 'Name';
case 'settings.personalData.maxMustermann': return 'Max Mustermann';
case 'settings.personalData.changePassword': return 'Change Password';
case 'settings.personalData.twoFactor': return 'Two-Factor Authentication';
case 'settings.personalData.off': return 'Off';
case 'settings.accountManagement.email': return 'Email';
case 'settings.help.faq': return 'FAQ';
case 'settings.help.sendFeedback': return 'Send Feedback';
case 'settings.legal.privacy': return 'Privacy';
case 'settings.legal.termsOfService': return 'Terms of Service';
default: return null; default: return null;
} }
} }

View File

@@ -1,3 +1,4 @@
import 'package:app/core/i18n/translations.g.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
@@ -23,53 +24,55 @@ class _AppShellState extends State<AppShell> {
// --- NAV ITEMS ------------------------------------------------------------- // --- NAV ITEMS -------------------------------------------------------------
final _items = List<({IconData icon, IconData? selectedIcon, String label, String route})> _getItems(BuildContext context) {
const < final t = Translations.of(context);
({IconData icon, IconData? selectedIcon, String label, String route}) return [
>[ (
( icon: Icons.dashboard_outlined,
icon: Icons.dashboard_outlined, selectedIcon: Icons.dashboard,
selectedIcon: Icons.dashboard, label: t.app.navigationDashboard,
label: 'Dashboard', route: '/home',
route: '/home', ),
), (
( icon: Icons.account_balance_wallet_outlined,
icon: Icons.account_balance_wallet_outlined, selectedIcon: Icons.account_balance_wallet,
selectedIcon: Icons.account_balance_wallet, label: t.app.navigationBudgets,
label: 'Budgets', route: '/budget',
route: '/budget', ),
), (
( icon: Icons.inventory_2_outlined,
icon: Icons.inventory_2_outlined, selectedIcon: Icons.inventory_2,
selectedIcon: Icons.inventory_2, label: t.app.navigationInventory,
label: 'Inventar', route: '/inventory',
route: '/inventory', ),
), (
( icon: Icons.bar_chart_outlined,
icon: Icons.bar_chart_outlined, selectedIcon: Icons.bar_chart,
selectedIcon: Icons.bar_chart, label: t.app.navigationReports,
label: 'Reports', route: '/reports',
route: '/reports', ),
), (
(
icon: Icons.settings, icon: Icons.settings,
selectedIcon: Icons.settings_outlined, selectedIcon: Icons.settings_outlined,
label: 'Settings', label: t.app.navigationSettings,
route: '/settings', route: '/settings',
), ),
]; ];
}
int _indexForPath(String p) { int _indexForPath(String p, List<({IconData icon, IconData? selectedIcon, String label, String route})> items) {
for (var i = 0; i < _items.length; i++) { for (var i = 0; i < items.length; i++) {
if (p.startsWith(_items[i].route)) return i; if (p.startsWith(items[i].route)) return i;
} }
return 0; return 0;
} }
void _goForIndex(BuildContext ctx, int i) => ctx.go(_items[i].route); void _goForIndex(BuildContext ctx, int i, List<({IconData icon, IconData? selectedIcon, String label, String route})> items) => ctx.go(items[i].route);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final t = Translations.of(context);
final items = _getItems(context);
final width = MediaQuery.of(context).size.width; final width = MediaQuery.of(context).size.width;
final isRail = width >= _railBreakpoint; final isRail = width >= _railBreakpoint;
final currentPath = GoRouterState.of(context).matchedLocation; final currentPath = GoRouterState.of(context).matchedLocation;
@@ -89,17 +92,17 @@ class _AppShellState extends State<AppShell> {
builder: (ctx) => IconButton( builder: (ctx) => IconButton(
icon: const Icon(Icons.menu), icon: const Icon(Icons.menu),
onPressed: () => Scaffold.of(ctx).openDrawer(), onPressed: () => Scaffold.of(ctx).openDrawer(),
tooltip: 'Menü', tooltip: t.app.tooltipMenu,
), ),
), ),
actions: [ actions: [
IconButton( IconButton(
tooltip: 'Benachrichtigungen', tooltip: t.app.tooltipNotifications,
onPressed: () {}, onPressed: () {},
icon: const Icon(Icons.notifications_none), icon: const Icon(Icons.notifications_none),
), ),
IconButton( IconButton(
tooltip: 'Benutzer-Einstellungen', tooltip: t.app.tooltipUserSettings,
onPressed: () => context.push('/settings'), onPressed: () => context.push('/settings'),
icon: const Icon(Icons.account_circle_outlined), icon: const Icon(Icons.account_circle_outlined),
), ),
@@ -108,16 +111,16 @@ class _AppShellState extends State<AppShell> {
if (!isRail) { if (!isRail) {
// ------------------- MOBILE: Drawer ------------------- // ------------------- MOBILE: Drawer -------------------
final selectedIndex = _indexForPath(currentPath); final selectedIndex = _indexForPath(currentPath, items);
return Scaffold( return Scaffold(
appBar: appBar, appBar: appBar,
drawer: _AppDrawer(items: _items, selectedIndex: selectedIndex), drawer: _AppDrawer(items: items, selectedIndex: selectedIndex),
body: SafeArea(child: widget.child), body: SafeArea(child: widget.child),
); );
} }
// ------------------- TABLET/DESKTOP: NavigationRail ------------------- // ------------------- TABLET/DESKTOP: NavigationRail -------------------
final selected = _indexForPath(currentPath); final selected = _indexForPath(currentPath, items);
final scheme = Theme.of(context).colorScheme; final scheme = Theme.of(context).colorScheme;
return Scaffold( return Scaffold(
@@ -143,13 +146,13 @@ class _AppShellState extends State<AppShell> {
child: NavigationRail( child: NavigationRail(
extended: _railExtended, extended: _railExtended,
selectedIndex: selected, selectedIndex: selected,
onDestinationSelected: (i) => _goForIndex(context, i), onDestinationSelected: (i) => _goForIndex(context, i, items),
// leading: const Padding( // leading: const Padding(
// padding: EdgeInsets.only(top: 8), // padding: EdgeInsets.only(top: 8),
// child: _LogoHeader(), // child: _LogoHeader(),
// ), // ),
destinations: [ destinations: [
for (final it in _items) for (final it in items)
NavigationRailDestination( NavigationRailDestination(
icon: Icon(it.icon), icon: Icon(it.icon),
selectedIcon: Icon(it.selectedIcon ?? it.icon), selectedIcon: Icon(it.selectedIcon ?? it.icon),
@@ -164,8 +167,8 @@ class _AppShellState extends State<AppShell> {
// toggle lives at the very bottom so layout doesn't jump // toggle lives at the very bottom so layout doesn't jump
IconButton( IconButton(
tooltip: _railExtended tooltip: _railExtended
? 'Leiste verkleinern' ? t.app.tooltipCollapseRail
: 'Leiste erweitern', : t.app.tooltipExpandRail,
onPressed: () => onPressed: () =>
setState(() => _railExtended = !_railExtended), setState(() => _railExtended = !_railExtended),
icon: Icon( icon: Icon(
@@ -234,7 +237,7 @@ class _AppDrawer extends StatelessWidget {
const Divider(), const Divider(),
ListTile( ListTile(
leading: const Icon(Icons.settings_outlined), leading: const Icon(Icons.settings_outlined),
title: const Text('Einstellungen'), title: Text(Translations.of(context).app.drawerSettings),
onTap: () { onTap: () {
Navigator.of(context).maybePop(); Navigator.of(context).maybePop();
context.go('/settings'); context.go('/settings');

View File

@@ -1,4 +1,5 @@
import 'package:app/core/app/router.dart'; import 'package:app/core/app/router.dart';
import 'package:app/core/i18n/translations.g.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
@@ -28,9 +29,10 @@ class _LoginPageState extends State<LoginPage> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final t = Translations.of(context);
final theme = Theme.of(context); final theme = Theme.of(context);
return Scaffold( return Scaffold(
appBar: AppBar(title: const Text('Login')), appBar: AppBar(title: Text(t.login.title)),
body: Center( body: Center(
child: ConstrainedBox( child: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 360), constraints: const BoxConstraints(maxWidth: 360),
@@ -39,7 +41,7 @@ class _LoginPageState extends State<LoginPage> {
children: [ children: [
const FlutterLogo(size: 64), const FlutterLogo(size: 64),
const SizedBox(height: 24), const SizedBox(height: 24),
Text('Please sign in', style: theme.textTheme.titleMedium), Text(t.login.pleaseSignIn, style: theme.textTheme.titleMedium),
const SizedBox(height: 24), const SizedBox(height: 24),
SizedBox( SizedBox(
width: double.infinity, width: double.infinity,
@@ -56,7 +58,7 @@ class _LoginPageState extends State<LoginPage> {
height: 20, height: 20,
child: CircularProgressIndicator(strokeWidth: 2), child: CircularProgressIndicator(strokeWidth: 2),
) )
: const Text('Login', key: ValueKey('text')), : Text(t.login.title, key: const ValueKey('text')),
), ),
), ),
), ),
@@ -67,7 +69,7 @@ class _LoginPageState extends State<LoginPage> {
? Padding( ? Padding(
padding: const EdgeInsets.only(top: 4), padding: const EdgeInsets.only(top: 4),
child: Text( child: Text(
'Signing you in…', t.login.signingIn,
style: theme.textTheme.bodySmall, style: theme.textTheme.bodySmall,
), ),
) )

View File

@@ -1,3 +1,4 @@
import 'package:app/core/i18n/translations.g.dart';
import 'package:app/core/ui/controller/locale_controller.dart'; import 'package:app/core/ui/controller/locale_controller.dart';
import 'package:app/core/ui/controller/scale_controller.dart'; import 'package:app/core/ui/controller/scale_controller.dart';
import 'package:app/core/ui/controller/theme.dart'; import 'package:app/core/ui/controller/theme.dart';
@@ -61,15 +62,16 @@ class _SystemBackgroundSection extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final t = Translations.of(context);
final vm = context.watch<AppSettingsViewModel>(); final vm = context.watch<AppSettingsViewModel>();
final selected = vm.themeMode; final selected = vm.themeMode;
return Column( return Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
const Text( Text(
'System-Hintergrundfarbe', t.settings.app.systemBackground,
style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600), style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w600),
), ),
const SizedBox(height: 10), const SizedBox(height: 10),
@@ -94,10 +96,10 @@ class _SystemBackgroundSection extends StatelessWidget {
}, },
borderRadius: BorderRadius.circular(24), borderRadius: BorderRadius.circular(24),
constraints: const BoxConstraints(minHeight: 44, minWidth: 140), constraints: const BoxConstraints(minHeight: 44, minWidth: 140),
children: const [ children: [
_SegItem(icon: Icons.phone_iphone, label: 'Systemstandard'), _SegItem(icon: Icons.phone_iphone, label: t.settings.app.systemDefault),
_SegItem(emoji: '🌙', label: 'Dark Mode'), _SegItem(emoji: '🌙', label: t.settings.app.darkMode),
_SegItem(emoji: '☀️', label: 'White Mode'), _SegItem(emoji: '☀️', label: t.settings.app.lightMode),
], ],
), ),
], ],
@@ -134,13 +136,14 @@ class _TextScaleSection extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final t = Translations.of(context);
final vm = context.watch<AppSettingsViewModel>(); final vm = context.watch<AppSettingsViewModel>();
final selected = vm.textScale; final selected = vm.textScale;
return Column( return Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
const Text('Textgröße', style: TextStyle(fontWeight: FontWeight.w600)), Text(t.settings.app.textSize, style: const TextStyle(fontWeight: FontWeight.w600)),
const SizedBox(height: 8), const SizedBox(height: 8),
ToggleButtons( ToggleButtons(
isSelected: [ isSelected: [
@@ -167,11 +170,11 @@ class _TextScaleSection extends StatelessWidget {
}, },
borderRadius: BorderRadius.circular(24), borderRadius: BorderRadius.circular(24),
constraints: const BoxConstraints(minHeight: 44, minWidth: 120), constraints: const BoxConstraints(minHeight: 44, minWidth: 120),
children: const [ children: [
_SegItem(icon: Icons.phone_android, label: 'System'), _SegItem(icon: Icons.phone_android, label: t.settings.app.system),
_SegItem(icon: Icons.text_fields, label: 'Klein'), _SegItem(icon: Icons.text_fields, label: t.settings.app.small),
_SegItem(icon: Icons.text_fields, label: 'Mittel'), _SegItem(icon: Icons.text_fields, label: t.settings.app.medium),
_SegItem(icon: Icons.text_fields, label: 'Groß'), _SegItem(icon: Icons.text_fields, label: t.settings.app.large),
], ],
), ),
], ],
@@ -184,25 +187,26 @@ class _LanguageSection extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final t = Translations.of(context);
final vm = context.watch<AppSettingsViewModel>(); final vm = context.watch<AppSettingsViewModel>();
final scheme = Theme.of(context).colorScheme; final scheme = Theme.of(context).colorScheme;
return Column( return Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text('Sprache', style: Theme.of(context).textTheme.titleMedium), Text(t.settings.app.language, style: Theme.of(context).textTheme.titleMedium),
const SizedBox(height: 8), const SizedBox(height: 8),
DropdownButtonFormField<LanguagePref>( DropdownButtonFormField<LanguagePref>(
initialValue: vm.language, initialValue: vm.language,
onChanged: (v) => vm.setLanguage(v ?? LanguagePref.en), onChanged: (v) => vm.setLanguage(v ?? LanguagePref.en),
items: const [ items: [
DropdownMenuItem( DropdownMenuItem(
value: LanguagePref.system, value: LanguagePref.system,
child: Text('Systemstandard'), child: Text(t.settings.app.systemDefault),
), ),
DropdownMenuItem(value: LanguagePref.de, child: Text('Deutsch')), DropdownMenuItem(value: LanguagePref.de, child: Text(t.settings.app.german)),
DropdownMenuItem(value: LanguagePref.en, child: Text('Englisch')), DropdownMenuItem(value: LanguagePref.en, child: Text(t.settings.app.english)),
], ],
decoration: InputDecoration( decoration: InputDecoration(
isDense: true, isDense: true,