From 0ff158134c9bca8c3e179cbae9ba0c501c641392 Mon Sep 17 00:00:00 2001 From: oskar Date: Fri, 9 Jan 2026 17:35:26 +0100 Subject: [PATCH] iteration with extr strs --- front001/mosenioring/l10n.yaml | 4 + front001/mosenioring/lib/l10n/app_en.arb | 12 ++ .../lib/l10n/app_localizations.dart | 182 ++++++++++++++++++ .../lib/l10n/app_localizations_en.dart | 37 ++++ front001/mosenioring/lib/src/app/app.dart | 12 +- .../mosenioring/lib/src/di/providers.dart | 5 +- .../auth/presentation/auth_controller.dart | 11 +- .../auth/presentation/login_page.dart | 22 ++- .../features/home/presentation/home_page.dart | 11 +- .../flutter/generated_plugin_registrant.cc | 4 + .../linux/flutter/generated_plugins.cmake | 1 + .../Flutter/GeneratedPluginRegistrant.swift | 4 + front001/mosenioring/pubspec.yaml | 10 +- front001/mosenioring/test/widget_test.dart | 25 +-- .../flutter/generated_plugin_registrant.cc | 3 + .../windows/flutter/generated_plugins.cmake | 1 + 16 files changed, 309 insertions(+), 35 deletions(-) create mode 100644 front001/mosenioring/l10n.yaml create mode 100644 front001/mosenioring/lib/l10n/app_en.arb create mode 100644 front001/mosenioring/lib/l10n/app_localizations.dart create mode 100644 front001/mosenioring/lib/l10n/app_localizations_en.dart diff --git a/front001/mosenioring/l10n.yaml b/front001/mosenioring/l10n.yaml new file mode 100644 index 0000000..1437ccc --- /dev/null +++ b/front001/mosenioring/l10n.yaml @@ -0,0 +1,4 @@ +arb-dir: lib/l10n +template-arb-file: app_en.arb +output-localization-file: app_localizations.dart +output-class: AppLocalizations diff --git a/front001/mosenioring/lib/l10n/app_en.arb b/front001/mosenioring/lib/l10n/app_en.arb new file mode 100644 index 0000000..5dc8705 --- /dev/null +++ b/front001/mosenioring/lib/l10n/app_en.arb @@ -0,0 +1,12 @@ +{ + "@@locale": "en", + "appTitle": "Mosenioring", + "signInTitle": "Sign in", + "welcomeTitle": "Welcome", + "emailLabel": "Email", + "passwordLabel": "Password", + "loginButton": "Login", + "loginRequired": "Email and password are required.", + "signedInMessage": "You are signed in.", + "logoutTooltip": "Logout" +} diff --git a/front001/mosenioring/lib/l10n/app_localizations.dart b/front001/mosenioring/lib/l10n/app_localizations.dart new file mode 100644 index 0000000..d2795f9 --- /dev/null +++ b/front001/mosenioring/lib/l10n/app_localizations.dart @@ -0,0 +1,182 @@ +import 'dart:async'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/widgets.dart'; +import 'package:flutter_localizations/flutter_localizations.dart'; +import 'package:intl/intl.dart' as intl; + +import 'app_localizations_en.dart'; + +// ignore_for_file: type=lint + +/// Callers can lookup localized strings with an instance of AppLocalizations +/// returned by `AppLocalizations.of(context)`. +/// +/// Applications need to include `AppLocalizations.delegate()` in their app's +/// `localizationDelegates` list, and the locales they support in the app's +/// `supportedLocales` list. For example: +/// +/// ```dart +/// import 'l10n/app_localizations.dart'; +/// +/// return MaterialApp( +/// localizationsDelegates: AppLocalizations.localizationsDelegates, +/// supportedLocales: AppLocalizations.supportedLocales, +/// home: MyApplicationHome(), +/// ); +/// ``` +/// +/// ## Update pubspec.yaml +/// +/// Please make sure to update your pubspec.yaml to include the following +/// packages: +/// +/// ```yaml +/// dependencies: +/// # Internationalization support. +/// flutter_localizations: +/// sdk: flutter +/// intl: any # Use the pinned version from flutter_localizations +/// +/// # Rest of dependencies +/// ``` +/// +/// ## iOS Applications +/// +/// iOS applications define key application metadata, including supported +/// locales, in an Info.plist file that is built into the application bundle. +/// To configure the locales supported by your app, you’ll need to edit this +/// file. +/// +/// First, open your project’s ios/Runner.xcworkspace Xcode workspace file. +/// Then, in the Project Navigator, open the Info.plist file under the Runner +/// project’s Runner folder. +/// +/// Next, select the Information Property List item, select Add Item from the +/// Editor menu, then select Localizations from the pop-up menu. +/// +/// Select and expand the newly-created Localizations item then, for each +/// locale your application supports, add a new item and select the locale +/// you wish to add from the pop-up menu in the Value field. This list should +/// be consistent with the languages listed in the AppLocalizations.supportedLocales +/// property. +abstract class AppLocalizations { + AppLocalizations(String locale) + : localeName = intl.Intl.canonicalizedLocale(locale.toString()); + + final String localeName; + + static AppLocalizations? of(BuildContext context) { + return Localizations.of(context, AppLocalizations); + } + + static const LocalizationsDelegate delegate = + _AppLocalizationsDelegate(); + + /// A list of this localizations delegate along with the default localizations + /// delegates. + /// + /// Returns a list of localizations delegates containing this delegate along with + /// GlobalMaterialLocalizations.delegate, GlobalCupertinoLocalizations.delegate, + /// and GlobalWidgetsLocalizations.delegate. + /// + /// Additional delegates can be added by appending to this list in + /// MaterialApp. This list does not have to be used at all if a custom list + /// of delegates is preferred or required. + static const List> localizationsDelegates = + >[ + delegate, + GlobalMaterialLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + ]; + + /// A list of this localizations delegate's supported locales. + static const List supportedLocales = [Locale('en')]; + + /// No description provided for @appTitle. + /// + /// In en, this message translates to: + /// **'Mosenioring'** + String get appTitle; + + /// No description provided for @signInTitle. + /// + /// In en, this message translates to: + /// **'Sign in'** + String get signInTitle; + + /// No description provided for @welcomeTitle. + /// + /// In en, this message translates to: + /// **'Welcome'** + String get welcomeTitle; + + /// No description provided for @emailLabel. + /// + /// In en, this message translates to: + /// **'Email'** + String get emailLabel; + + /// No description provided for @passwordLabel. + /// + /// In en, this message translates to: + /// **'Password'** + String get passwordLabel; + + /// No description provided for @loginButton. + /// + /// In en, this message translates to: + /// **'Login'** + String get loginButton; + + /// No description provided for @loginRequired. + /// + /// In en, this message translates to: + /// **'Email and password are required.'** + String get loginRequired; + + /// No description provided for @signedInMessage. + /// + /// In en, this message translates to: + /// **'You are signed in.'** + String get signedInMessage; + + /// No description provided for @logoutTooltip. + /// + /// In en, this message translates to: + /// **'Logout'** + String get logoutTooltip; +} + +class _AppLocalizationsDelegate + extends LocalizationsDelegate { + const _AppLocalizationsDelegate(); + + @override + Future load(Locale locale) { + return SynchronousFuture(lookupAppLocalizations(locale)); + } + + @override + bool isSupported(Locale locale) => + ['en'].contains(locale.languageCode); + + @override + bool shouldReload(_AppLocalizationsDelegate old) => false; +} + +AppLocalizations lookupAppLocalizations(Locale locale) { + // Lookup logic when only language code is specified. + switch (locale.languageCode) { + case 'en': + return AppLocalizationsEn(); + } + + throw FlutterError( + 'AppLocalizations.delegate failed to load unsupported locale "$locale". This is likely ' + 'an issue with the localizations generation tool. Please file an issue ' + 'on GitHub with a reproducible sample app and the gen-l10n configuration ' + 'that was used.', + ); +} diff --git a/front001/mosenioring/lib/l10n/app_localizations_en.dart b/front001/mosenioring/lib/l10n/app_localizations_en.dart new file mode 100644 index 0000000..07c84a3 --- /dev/null +++ b/front001/mosenioring/lib/l10n/app_localizations_en.dart @@ -0,0 +1,37 @@ +// ignore: unused_import +import 'package:intl/intl.dart' as intl; +import 'app_localizations.dart'; + +// ignore_for_file: type=lint + +/// The translations for English (`en`). +class AppLocalizationsEn extends AppLocalizations { + AppLocalizationsEn([String locale = 'en']) : super(locale); + + @override + String get appTitle => 'Mosenioring'; + + @override + String get signInTitle => 'Sign in'; + + @override + String get welcomeTitle => 'Welcome'; + + @override + String get emailLabel => 'Email'; + + @override + String get passwordLabel => 'Password'; + + @override + String get loginButton => 'Login'; + + @override + String get loginRequired => 'Email and password are required.'; + + @override + String get signedInMessage => 'You are signed in.'; + + @override + String get logoutTooltip => 'Logout'; +} diff --git a/front001/mosenioring/lib/src/app/app.dart b/front001/mosenioring/lib/src/app/app.dart index 7d24eaa..869b1d1 100644 --- a/front001/mosenioring/lib/src/app/app.dart +++ b/front001/mosenioring/lib/src/app/app.dart @@ -1,5 +1,7 @@ import 'package:flutter/material.dart'; +import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:mosenioring/l10n/app_localizations.dart'; import 'router.dart'; @@ -11,11 +13,19 @@ class App extends ConsumerWidget { final router = ref.watch(appRouterProvider); return MaterialApp.router( - title: 'Mosenioring', + onGenerateTitle: (context) => + AppLocalizations.of(context)?.appTitle ?? 'Mosenioring', theme: ThemeData( colorScheme: ColorScheme.fromSeed(seedColor: Colors.teal), useMaterial3: true, ), + supportedLocales: AppLocalizations.supportedLocales, + localizationsDelegates: const [ + AppLocalizations.delegate, + GlobalMaterialLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, + ], routerConfig: router, ); } diff --git a/front001/mosenioring/lib/src/di/providers.dart b/front001/mosenioring/lib/src/di/providers.dart index ad680cc..f0888dd 100644 --- a/front001/mosenioring/lib/src/di/providers.dart +++ b/front001/mosenioring/lib/src/di/providers.dart @@ -65,7 +65,6 @@ final authRepositoryProvider = Provider((ref) { ); }); -final authControllerProvider = - StateNotifierProvider((ref) { - return AuthController(ref.watch(authRepositoryProvider)); +final authControllerProvider = NotifierProvider(() { + return AuthController(); }); diff --git a/front001/mosenioring/lib/src/features/auth/presentation/auth_controller.dart b/front001/mosenioring/lib/src/features/auth/presentation/auth_controller.dart index 4256814..3ca21f3 100644 --- a/front001/mosenioring/lib/src/features/auth/presentation/auth_controller.dart +++ b/front001/mosenioring/lib/src/features/auth/presentation/auth_controller.dart @@ -1,14 +1,17 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; +import '../../../di/providers.dart'; import '../domain/auth_repository.dart'; import 'auth_state.dart'; -class AuthController extends StateNotifier { - AuthController(this._repository) : super(const AuthState()) { - _bootstrap(); +class AuthController extends Notifier { + @override + AuthState build() { + Future.microtask(_bootstrap); + return const AuthState(); } - final AuthRepository _repository; + AuthRepository get _repository => ref.watch(authRepositoryProvider); Future _bootstrap() async { state = state.copyWith(isLoading: true, errorMessage: null); diff --git a/front001/mosenioring/lib/src/features/auth/presentation/login_page.dart b/front001/mosenioring/lib/src/features/auth/presentation/login_page.dart index 88525d7..57c98f5 100644 --- a/front001/mosenioring/lib/src/features/auth/presentation/login_page.dart +++ b/front001/mosenioring/lib/src/features/auth/presentation/login_page.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:mosenioring/l10n/app_localizations.dart'; import '../../../di/providers.dart'; @@ -24,10 +25,11 @@ class _LoginPageState extends ConsumerState { Future _submit() async { final email = _emailController.text.trim(); final password = _passwordController.text; + final l10n = AppLocalizations.of(context)!; if (email.isEmpty || password.isEmpty) { ScaffoldMessenger.of(context).showSnackBar( - const SnackBar(content: Text('Email and password are required.')), + SnackBar(content: Text(l10n.loginRequired)), ); return; } @@ -40,9 +42,10 @@ class _LoginPageState extends ConsumerState { @override Widget build(BuildContext context) { final authState = ref.watch(authControllerProvider); + final l10n = AppLocalizations.of(context)!; return Scaffold( - appBar: AppBar(title: const Text('Sign in')), + appBar: AppBar(title: Text(l10n.signInTitle)), body: Center( child: ConstrainedBox( constraints: const BoxConstraints(maxWidth: 420), @@ -51,18 +54,27 @@ class _LoginPageState extends ConsumerState { child: Column( mainAxisSize: MainAxisSize.min, children: [ + Text( + l10n.welcomeTitle, + style: const TextStyle(fontSize: 24, fontWeight: FontWeight.w600), + ), + const SizedBox(height: 12), TextField( controller: _emailController, keyboardType: TextInputType.emailAddress, autofillHints: const [AutofillHints.username], - decoration: const InputDecoration(labelText: 'Email'), + decoration: InputDecoration( + labelText: l10n.emailLabel, + ), ), const SizedBox(height: 16), TextField( controller: _passwordController, obscureText: true, autofillHints: const [AutofillHints.password], - decoration: const InputDecoration(labelText: 'Password'), + decoration: InputDecoration( + labelText: l10n.passwordLabel, + ), ), const SizedBox(height: 24), SizedBox( @@ -75,7 +87,7 @@ class _LoginPageState extends ConsumerState { height: 20, child: CircularProgressIndicator(strokeWidth: 2), ) - : const Text('Login'), + : Text(l10n.loginButton), ), ), if (authState.errorMessage != null) ...[ diff --git a/front001/mosenioring/lib/src/features/home/presentation/home_page.dart b/front001/mosenioring/lib/src/features/home/presentation/home_page.dart index ea67922..c7cedf6 100644 --- a/front001/mosenioring/lib/src/features/home/presentation/home_page.dart +++ b/front001/mosenioring/lib/src/features/home/presentation/home_page.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:mosenioring/l10n/app_localizations.dart'; import '../../../di/providers.dart'; @@ -8,21 +9,23 @@ class HomePage extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { + final l10n = AppLocalizations.of(context)!; + return Scaffold( appBar: AppBar( - title: const Text('Mosenioring'), + title: Text(l10n.appTitle), actions: [ IconButton( onPressed: () { ref.read(authControllerProvider.notifier).logout(); }, icon: const Icon(Icons.logout), - tooltip: 'Logout', + tooltip: l10n.logoutTooltip, ), ], ), - body: const Center( - child: Text('You are signed in.'), + body: Center( + child: Text(l10n.signedInMessage), ), ); } diff --git a/front001/mosenioring/linux/flutter/generated_plugin_registrant.cc b/front001/mosenioring/linux/flutter/generated_plugin_registrant.cc index e71a16d..d0e7f79 100644 --- a/front001/mosenioring/linux/flutter/generated_plugin_registrant.cc +++ b/front001/mosenioring/linux/flutter/generated_plugin_registrant.cc @@ -6,6 +6,10 @@ #include "generated_plugin_registrant.h" +#include void fl_register_plugins(FlPluginRegistry* registry) { + g_autoptr(FlPluginRegistrar) flutter_secure_storage_linux_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterSecureStorageLinuxPlugin"); + flutter_secure_storage_linux_plugin_register_with_registrar(flutter_secure_storage_linux_registrar); } diff --git a/front001/mosenioring/linux/flutter/generated_plugins.cmake b/front001/mosenioring/linux/flutter/generated_plugins.cmake index 2e1de87..b29e9ba 100644 --- a/front001/mosenioring/linux/flutter/generated_plugins.cmake +++ b/front001/mosenioring/linux/flutter/generated_plugins.cmake @@ -3,6 +3,7 @@ # list(APPEND FLUTTER_PLUGIN_LIST + flutter_secure_storage_linux ) list(APPEND FLUTTER_FFI_PLUGIN_LIST diff --git a/front001/mosenioring/macos/Flutter/GeneratedPluginRegistrant.swift b/front001/mosenioring/macos/Flutter/GeneratedPluginRegistrant.swift index cccf817..61d01d0 100644 --- a/front001/mosenioring/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/front001/mosenioring/macos/Flutter/GeneratedPluginRegistrant.swift @@ -5,6 +5,10 @@ import FlutterMacOS import Foundation +import flutter_secure_storage_darwin +import path_provider_foundation func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + FlutterSecureStorageDarwinPlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStorageDarwinPlugin")) + PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) } diff --git a/front001/mosenioring/pubspec.yaml b/front001/mosenioring/pubspec.yaml index e02368a..648c747 100644 --- a/front001/mosenioring/pubspec.yaml +++ b/front001/mosenioring/pubspec.yaml @@ -30,14 +30,17 @@ environment: dependencies: flutter: sdk: flutter + flutter_localizations: + sdk: flutter # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^1.0.8 dio: ^5.7.0 - flutter_riverpod: ^2.5.1 - flutter_secure_storage: ^9.2.2 - go_router: ^14.2.0 + flutter_riverpod: ^3.1.0 + flutter_secure_storage: ^10.0.0 + go_router: ^17.0.1 + intl: ^0.20.2 dev_dependencies: flutter_test: @@ -55,6 +58,7 @@ dev_dependencies: # The following section is specific to Flutter packages. flutter: + generate: true # The following line ensures that the Material Icons font is # included with your application, so that you can use the icons in diff --git a/front001/mosenioring/test/widget_test.dart b/front001/mosenioring/test/widget_test.dart index 50a7788..751bfed 100644 --- a/front001/mosenioring/test/widget_test.dart +++ b/front001/mosenioring/test/widget_test.dart @@ -5,26 +5,21 @@ // gestures. You can also use WidgetTester to find child widgets in the widget // tree, read text, and verify that the values of widget properties are correct. -import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:mosenioring/main.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:mosenioring/src/app/app.dart'; void main() { - testWidgets('Counter increments smoke test', (WidgetTester tester) async { + testWidgets('App smoke test', (WidgetTester tester) async { // Build our app and trigger a frame. - await tester.pumpWidget(const MyApp()); + await tester.pumpWidget(const ProviderScope(child: App())); + await tester.pump(); // Start _bootstrap + await tester.pump(const Duration(milliseconds: 100)); // Allow some time - // Verify that our counter starts at 0. - expect(find.text('0'), findsOneWidget); - expect(find.text('1'), findsNothing); - - // Tap the '+' icon and trigger a frame. - await tester.tap(find.byIcon(Icons.add)); - await tester.pump(); - - // Verify that our counter has incremented. - expect(find.text('0'), findsNothing); - expect(find.text('1'), findsOneWidget); + // Verify that login page is shown. + expect(find.text('Sign in'), findsOneWidget); + expect(find.text('Email'), findsOneWidget); + expect(find.text('Password'), findsOneWidget); }); } diff --git a/front001/mosenioring/windows/flutter/generated_plugin_registrant.cc b/front001/mosenioring/windows/flutter/generated_plugin_registrant.cc index 8b6d468..0c50753 100644 --- a/front001/mosenioring/windows/flutter/generated_plugin_registrant.cc +++ b/front001/mosenioring/windows/flutter/generated_plugin_registrant.cc @@ -6,6 +6,9 @@ #include "generated_plugin_registrant.h" +#include void RegisterPlugins(flutter::PluginRegistry* registry) { + FlutterSecureStorageWindowsPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("FlutterSecureStorageWindowsPlugin")); } diff --git a/front001/mosenioring/windows/flutter/generated_plugins.cmake b/front001/mosenioring/windows/flutter/generated_plugins.cmake index b93c4c3..4fc759c 100644 --- a/front001/mosenioring/windows/flutter/generated_plugins.cmake +++ b/front001/mosenioring/windows/flutter/generated_plugins.cmake @@ -3,6 +3,7 @@ # list(APPEND FLUTTER_PLUGIN_LIST + flutter_secure_storage_windows ) list(APPEND FLUTTER_FFI_PLUGIN_LIST