From f0cb2707c4adfedbb27e306b4d8fe65c35e02874 Mon Sep 17 00:00:00 2001 From: Felipe Date: Fri, 9 Jan 2026 17:25:01 -0300 Subject: [PATCH] chore: localization alternative --- .../translations/en-US.json | 11 +- fastlane/Fastfile | 17 +- integration_test/common/general_helpers.dart | 2 + integration_test/test/signin_test.dart | 39 ++-- ios/Podfile.lock | 24 +-- .../xcshareddata/xcschemes/dev.xcscheme | 2 + ios/Runner/AppDelegate.swift | 2 +- l10n.yaml | 3 - lib/gen/locale_keys.g.dart | 19 ++ lib/l10n/app_localizations.dart | 200 ------------------ lib/l10n/app_localizations_en.dart | 49 ----- lib/main.dart | 11 +- lib/ui/extensions/context_extensions.dart | 6 - lib/ui/main/main_screen.dart | 19 +- lib/ui/resources.dart | 17 -- lib/ui/section/section_router.dart | 22 +- lib/ui/signin/signin_screen.dart | 20 +- lib/ui/welcome/welcome_screen.dart | 7 +- pubspec.lock | 16 ++ pubspec.yaml | 4 +- scripts/clean_up.sh | 5 +- scripts/generate_localization_keys.sh | 4 + 22 files changed, 139 insertions(+), 360 deletions(-) rename lib/l10n/intl_en.arb => assets/translations/en-US.json (69%) delete mode 100644 l10n.yaml create mode 100644 lib/gen/locale_keys.g.dart delete mode 100644 lib/l10n/app_localizations.dart delete mode 100644 lib/l10n/app_localizations_en.dart delete mode 100644 lib/ui/extensions/context_extensions.dart delete mode 100644 lib/ui/resources.dart create mode 100755 scripts/generate_localization_keys.sh diff --git a/lib/l10n/intl_en.arb b/assets/translations/en-US.json similarity index 69% rename from lib/l10n/intl_en.arb rename to assets/translations/en-US.json index f0116023..e3a1acee 100644 --- a/lib/l10n/intl_en.arb +++ b/assets/translations/en-US.json @@ -1,14 +1,5 @@ { "error": "Error: {text}", - "@error": { - "description": "Gives the user an error explanation", - "placeholders": { - "text": { - "type": "String", - "example": "Something went wrong try again" - } - } - }, "error_button_ok": "Ok", "error_button_retry": "Retry", "error_no_internet_connection_error_description": "You have no internet connection", @@ -20,4 +11,4 @@ "password": "Password", "sign_in": "Sign In", "xmartlabs_projects": "Xmartlabs' projects" -} \ No newline at end of file +} diff --git a/fastlane/Fastfile b/fastlane/Fastfile index c96d37bd..2c68c561 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -52,24 +52,20 @@ lane :fetch_dependencies do flutter_command(command: "pub get --suppress-analytics", subdirectory: "design_system/design_system_gallery") end -desc "**Generate flutter generated code**" +desc "**Generate flutter generated code (includes build_runner and locale keys)**" lane :build_autogenerated_code do flutter_command(command: "pub run build_runner build --delete-conflicting-outputs") + generate_locale_keys end desc "**Lint: Check code format**" lane :lint_format do - sh_on_root(command: "find lib -name '*.dart' ! -path 'lib/l10n/*' -print0 | xargs -0 fvm dart format -o none --set-exit-if-changed") + sh_on_root(command: "find lib -name '*.dart' ! -path 'lib/gen/*' -print0 | xargs -0 fvm dart format -o none --set-exit-if-changed") end -desc "**Lint: Check code format**" -lane :lint_check_language_sorting do - current_content = sh_on_root(command: "cat lib/l10n/intl_en.arb") - sh_on_root(command: "arb_utils sort lib/l10n/intl_en.arb") - new_content = sh_on_root(command: "cat lib/l10n/intl_en.arb") - unless current_content == new_content - UI.user_error!("Language file is not sorted") - end +desc "**Generate locale keys from translations**" +lane :generate_locale_keys do + flutter_command(command: "pub run easy_localization:generate -S assets/translations -O lib/gen -f keys -o locale_keys.g.dart") end desc "**Lint: Analyze code**" @@ -93,7 +89,6 @@ end desc "**Run linters**" lane :lints do lint_format - lint_check_language_sorting lint_analyze lint_code_metrics end diff --git a/integration_test/common/general_helpers.dart b/integration_test/common/general_helpers.dart index 4da09982..409b8043 100644 --- a/integration_test/common/general_helpers.dart +++ b/integration_test/common/general_helpers.dart @@ -1,3 +1,4 @@ +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_template/core/di/di_provider.dart'; import 'package:flutter_template/core/source/auth_remote_source.dart'; @@ -11,6 +12,7 @@ Future commonSetup({ required MockAuthRemoteSource mockAuthRemoteSource, required MockProjectRemoteSource mockProjectRemoteSource, }) async { + await EasyLocalization.ensureInitialized(); await app.initSdks(); DiProvider.instance.unregister(); diff --git a/integration_test/test/signin_test.dart b/integration_test/test/signin_test.dart index 69037982..1004ee96 100644 --- a/integration_test/test/signin_test.dart +++ b/integration_test/test/signin_test.dart @@ -1,11 +1,11 @@ +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:flutter_template/core/common/network_exceptions.dart'; import 'package:flutter_template/core/di/di_provider.dart'; import 'package:flutter_template/core/model/service/auth_models.dart'; import 'package:flutter_template/core/model/user.dart'; +import 'package:flutter_template/gen/locale_keys.g.dart'; import 'package:flutter_template/main.dart' as app; -import 'package:flutter_template/ui/extensions/context_extensions.dart'; -import 'package:flutter_template/ui/signin/signin_screen.dart'; import 'package:flutter_template/ui/welcome/welcome_screen.dart'; import 'package:integration_test/integration_test.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -42,20 +42,26 @@ void main() { ), ); - await tester.pumpWidget(const app.MyApp()); + await tester.pumpWidget( + EasyLocalization( + supportedLocales: const [Locale('en', 'US')], + path: 'assets/translations', + fallbackLocale: const Locale('en', 'US'), + child: const app.MyApp(), + ), + ); await tester.pumpAndSettle(); - final context = tester.contextOfType(); await tester.enterText( - find.widgetWithText(TextField, context.localizations.mail), + find.widgetWithText(TextField, LocaleKeys.mail.tr()), email, ); await tester.enterText( - find.widgetWithText(TextField, context.localizations.password), + find.widgetWithText(TextField, LocaleKeys.password.tr()), password, ); await tester.tap( - find.widgetWithText(TextButton, context.localizations.sign_in), + find.widgetWithText(TextButton, LocaleKeys.sign_in.tr()), ); await tester.pumpAndSettle(); expect(find.byType(AlertDialog), findsOneWidget); @@ -72,20 +78,27 @@ void main() { when(() => mockProjectRemoteSource.getProjects()) .thenAnswer((_) => Future.value([])); - await tester.pumpWidget(const app.MyApp()); + await tester.pumpWidget( + EasyLocalization( + supportedLocales: const [Locale('en', 'US')], + path: 'assets/translations', + fallbackLocale: const Locale('en', 'US'), + child: const app.MyApp(), + ), + ); await tester.pumpAndSettle(); - final context = tester.contextOfType(); await tester.enterText( - find.widgetWithText(TextField, context.localizations.mail), + find.widgetWithText(TextField, LocaleKeys.mail.tr()), email, ); await tester.enterText( - find.widgetWithText(TextField, context.localizations.password), + find.widgetWithText(TextField, LocaleKeys.password.tr()), password, ); - await tester - .tap(find.widgetWithText(TextButton, context.localizations.sign_in)); + await tester.tap( + find.widgetWithText(TextButton, LocaleKeys.sign_in.tr()), + ); await tester.pumpAndSettle(); expect(find.byType(WelcomeScreen), findsOneWidget); }); diff --git a/ios/Podfile.lock b/ios/Podfile.lock index e944c14c..7ea60df7 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -4,8 +4,6 @@ PODS: - Flutter - flutter_secure_storage (6.0.0): - Flutter - - flutter_web_browser (0.17.1): - - Flutter - integration_test (0.0.1): - Flutter - path_provider_foundation (0.0.1): @@ -17,16 +15,18 @@ PODS: - sqflite (0.0.3): - Flutter - FlutterMacOS + - url_launcher_ios (0.0.1): + - Flutter DEPENDENCIES: - Flutter (from `Flutter`) - flutter_native_splash (from `.symlinks/plugins/flutter_native_splash/ios`) - flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`) - - flutter_web_browser (from `.symlinks/plugins/flutter_web_browser/ios`) - integration_test (from `.symlinks/plugins/integration_test/ios`) - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`) - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`) - sqflite (from `.symlinks/plugins/sqflite/darwin`) + - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`) EXTERNAL SOURCES: Flutter: @@ -35,8 +35,6 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/flutter_native_splash/ios" flutter_secure_storage: :path: ".symlinks/plugins/flutter_secure_storage/ios" - flutter_web_browser: - :path: ".symlinks/plugins/flutter_web_browser/ios" integration_test: :path: ".symlinks/plugins/integration_test/ios" path_provider_foundation: @@ -45,16 +43,18 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/shared_preferences_foundation/darwin" sqflite: :path: ".symlinks/plugins/sqflite/darwin" + url_launcher_ios: + :path: ".symlinks/plugins/url_launcher_ios/ios" SPEC CHECKSUMS: Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 - flutter_native_splash: 35ddbc7228eafcb3969dcc5f1fbbe27c1145a4f0 - flutter_secure_storage: 1ed9476fba7e7a782b22888f956cce43e2c62f13 - flutter_web_browser: 8fe4d18e7b1328ab3fbec6e67029d6996c2335d9 - integration_test: d5929033778cc4991a187e4e1a85396fa4f59b3a - path_provider_foundation: 080d55be775b7414fd5a5ef3ac137b97b097e564 - shared_preferences_foundation: 9e1978ff2562383bd5676f64ec4e9aa8fa06a6f7 - sqflite: c35dad70033b8862124f8337cc994a809fcd9fa3 + flutter_native_splash: edf599c81f74d093a4daf8e17bd7a018854bc778 + flutter_secure_storage: d33dac7ae2ea08509be337e775f6b59f1ff45f12 + integration_test: 252f60fa39af5e17c3aa9899d35d908a0721b573 + path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46 + shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78 + sqflite: 673a0e54cc04b7d6dba8d24fb8095b31c3a99eec + url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe PODFILE CHECKSUM: 4e8f8b2be68aeea4c0d5beb6ff1e79fface1d048 diff --git a/ios/Runner.xcodeproj/xcshareddata/xcschemes/dev.xcscheme b/ios/Runner.xcodeproj/xcshareddata/xcschemes/dev.xcscheme index 663ebac6..77faf54a 100644 --- a/ios/Runner.xcodeproj/xcshareddata/xcschemes/dev.xcscheme +++ b/ios/Runner.xcodeproj/xcshareddata/xcschemes/dev.xcscheme @@ -10,6 +10,7 @@ buildConfiguration = "Debug-dev" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit" shouldUseLaunchSchemeArgsEnv = "YES"> (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')]; - - /// Gives the user an error explanation - /// - /// In en, this message translates to: - /// **'Error: {text}'** - String error(String text); - - /// No description provided for @error_button_ok. - /// - /// In en, this message translates to: - /// **'Ok'** - String get error_button_ok; - - /// No description provided for @error_button_retry. - /// - /// In en, this message translates to: - /// **'Retry'** - String get error_button_retry; - - /// No description provided for @error_no_internet_connection_error_description. - /// - /// In en, this message translates to: - /// **'You have no internet connection'** - String get error_no_internet_connection_error_description; - - /// No description provided for @error_no_internet_connection_error_title. - /// - /// In en, this message translates to: - /// **'Error'** - String get error_no_internet_connection_error_title; - - /// No description provided for @error_unknown_error_description. - /// - /// In en, this message translates to: - /// **'Something went wrong!'** - String get error_unknown_error_description; - - /// No description provided for @error_unknown_error_title. - /// - /// In en, this message translates to: - /// **'Ops!'** - String get error_unknown_error_title; - - /// No description provided for @log_out. - /// - /// In en, this message translates to: - /// **'Log out'** - String get log_out; - - /// No description provided for @mail. - /// - /// In en, this message translates to: - /// **'Mail'** - String get mail; - - /// No description provided for @password. - /// - /// In en, this message translates to: - /// **'Password'** - String get password; - - /// No description provided for @sign_in. - /// - /// In en, this message translates to: - /// **'Sign In'** - String get sign_in; - - /// No description provided for @xmartlabs_projects. - /// - /// In en, this message translates to: - /// **'Xmartlabs\' projects'** - String get xmartlabs_projects; -} - -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/lib/l10n/app_localizations_en.dart b/lib/l10n/app_localizations_en.dart deleted file mode 100644 index 6a32ff9f..00000000 --- a/lib/l10n/app_localizations_en.dart +++ /dev/null @@ -1,49 +0,0 @@ -// 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 error(String text) { - return 'Error: $text'; - } - - @override - String get error_button_ok => 'Ok'; - - @override - String get error_button_retry => 'Retry'; - - @override - String get error_no_internet_connection_error_description => - 'You have no internet connection'; - - @override - String get error_no_internet_connection_error_title => 'Error'; - - @override - String get error_unknown_error_description => 'Something went wrong!'; - - @override - String get error_unknown_error_title => 'Ops!'; - - @override - String get log_out => 'Log out'; - - @override - String get mail => 'Mail'; - - @override - String get password => 'Password'; - - @override - String get sign_in => 'Sign In'; - - @override - String get xmartlabs_projects => 'Xmartlabs\' projects'; -} diff --git a/lib/main.dart b/lib/main.dart index caa4aa82..097d94c9 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,5 +1,6 @@ import 'dart:async'; +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:flutter_native_splash/flutter_native_splash.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; @@ -14,8 +15,16 @@ Future main() async { () async { final widgetsBinding = WidgetsFlutterBinding.ensureInitialized(); FlutterNativeSplash.preserve(widgetsBinding: widgetsBinding); + await EasyLocalization.ensureInitialized(); await initSdks(); - runApp(const MyApp()); + runApp( + EasyLocalization( + supportedLocales: const [Locale('en', 'US')], + path: 'assets/translations', + fallbackLocale: const Locale('en', 'US'), + child: const MyApp(), + ), + ); FlutterNativeSplash.remove(); }, (exception, stackTrace) => diff --git a/lib/ui/extensions/context_extensions.dart b/lib/ui/extensions/context_extensions.dart deleted file mode 100644 index e34c9b7c..00000000 --- a/lib/ui/extensions/context_extensions.dart +++ /dev/null @@ -1,6 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_template/l10n/app_localizations.dart'; - -extension ContextExtensions on BuildContext { - AppLocalizations get localizations => AppLocalizations.of(this)!; -} diff --git a/lib/ui/main/main_screen.dart b/lib/ui/main/main_screen.dart index f374a331..c393bdd2 100644 --- a/lib/ui/main/main_screen.dart +++ b/lib/ui/main/main_screen.dart @@ -1,9 +1,7 @@ import 'package:design_system/theme/app_theme.dart'; +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:flutter_template/core/di/di_provider.dart'; -import 'package:flutter_template/l10n/app_localizations.dart'; -import 'package:flutter_template/ui/resources.dart'; import 'package:flutter_template/ui/router/app_router.dart'; class MainScreen extends StatelessWidget { @@ -16,17 +14,10 @@ class MainScreen extends StatelessWidget { theme: AppTheme.provideAppTheme(context), routerConfig: router.config(reevaluateListenable: router.authReevaluateListenable), - localizationsDelegates: const [ - AppLocalizations.delegate, - GlobalMaterialLocalizations.delegate, - GlobalWidgetsLocalizations.delegate, - GlobalCupertinoLocalizations.delegate, - ], - supportedLocales: AppLocalizations.supportedLocales, - builder: (context, child) { - Resources.setup(context); - return child!; - }, + localizationsDelegates: context.localizationDelegates, + supportedLocales: context.supportedLocales, + locale: context.locale, + builder: (context, child) => child!, ); } } diff --git a/lib/ui/resources.dart b/lib/ui/resources.dart deleted file mode 100644 index ce299efa..00000000 --- a/lib/ui/resources.dart +++ /dev/null @@ -1,17 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_template/l10n/app_localizations.dart'; -import 'package:flutter_template/ui/extensions/context_extensions.dart'; - -abstract class Resources { - /// Returns the app localizations - /// It should be used only in spacial cases when the context is not available - /// It's recommended to use `context.localizations` extension method. - /// This property doesn't notify the widget if the language changes, - /// so if the language changes or the localization are load async, - /// the app language will not be consistent. - static late AppLocalizations localizations; - - static void setup(BuildContext appContext) { - localizations = appContext.localizations; - } -} diff --git a/lib/ui/section/section_router.dart b/lib/ui/section/section_router.dart index 5bb9914c..ceb35fbb 100644 --- a/lib/ui/section/section_router.dart +++ b/lib/ui/section/section_router.dart @@ -1,7 +1,8 @@ import 'package:auto_route/auto_route.dart'; +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_template/ui/extensions/context_extensions.dart'; +import 'package:flutter_template/gen/locale_keys.g.dart'; import 'package:flutter_template/ui/section/error_handler/global_event_handler_cubit.dart'; @RoutePage() @@ -31,9 +32,9 @@ class SectionRouter extends StatelessWidget { GlobalEventHandlerState event, ) => event.when( - idle: () => {}, + idle: () => null, error: (errorType) => _showError(errorType, context), - loading: () => {}, + loading: () => null, ); void _showError( @@ -43,15 +44,14 @@ class SectionRouter extends StatelessWidget { switch (errorType) { UnknownError(retry: final retry) => _showDialog( context, - context.localizations.error_unknown_error_title, - context.localizations.error_unknown_error_description, + LocaleKeys.error_unknown_error_title.tr(), + LocaleKeys.error_unknown_error_description.tr(), retry, ), InternetError(retry: final retry) => _showDialog( context, - context.localizations.error_no_internet_connection_error_title, - context - .localizations.error_no_internet_connection_error_description, + LocaleKeys.error_no_internet_connection_error_title.tr(), + LocaleKeys.error_no_internet_connection_error_description.tr(), retry, ), GeneralError( @@ -72,18 +72,18 @@ class SectionRouter extends StatelessWidget { context: context, builder: (context) => AlertDialog( title: Text( - title ?? context.localizations.error_unknown_error_title, + title ?? LocaleKeys.error_unknown_error_title.tr(), ), content: Text(description), actions: [ if (retry != null) TextButton( onPressed: () => Navigator.pop(context), - child: Text(context.localizations.error_button_retry), + child: Text(LocaleKeys.error_button_retry.tr()), ), TextButton( onPressed: () => Navigator.pop(context), - child: Text(context.localizations.error_button_ok), + child: Text(LocaleKeys.error_button_ok.tr()), ), ], ), diff --git a/lib/ui/signin/signin_screen.dart b/lib/ui/signin/signin_screen.dart index 3396fc75..96a23604 100644 --- a/lib/ui/signin/signin_screen.dart +++ b/lib/ui/signin/signin_screen.dart @@ -1,7 +1,8 @@ import 'package:auto_route/auto_route.dart'; +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_template/ui/extensions/context_extensions.dart'; +import 'package:flutter_template/gen/locale_keys.g.dart'; import 'package:flutter_template/ui/section/error_handler/global_event_handler_cubit.dart'; import 'package:flutter_template/ui/signin/signin_cubit.dart'; @@ -24,7 +25,9 @@ class _SignInContentScreen extends StatelessWidget { BlocBuilder( builder: (context, state) => Scaffold( appBar: AppBar( - title: Text(context.localizations.sign_in), + title: Text( + LocaleKeys.sign_in_test.tr(), + ), ), body: Column( mainAxisSize: MainAxisSize.min, @@ -32,14 +35,17 @@ class _SignInContentScreen extends StatelessWidget { Expanded(child: _SignInForm()), if (context.read().state.error.isNotEmpty) Text( - context.localizations - .error(context.read().state.error), + LocaleKeys.error.tr( + namedArgs: { + 'text': context.read().state.error, + }, + ), ), Padding( padding: const EdgeInsets.symmetric(vertical: 30.0), child: TextButton( onPressed: () => context.read().signIn(), - child: Text(context.localizations.sign_in), + child: Text(LocaleKeys.sign_in.tr()), ), ), ], @@ -85,7 +91,7 @@ class _SignInFormState extends State<_SignInForm> { onChanged: (String text) => _signInCubit.changeEmail(text), decoration: InputDecoration( border: const OutlineInputBorder(), - labelText: context.localizations.mail, + labelText: LocaleKeys.mail.tr(), ), ), ), @@ -98,7 +104,7 @@ class _SignInFormState extends State<_SignInForm> { _signInCubit.changePassword(password), decoration: InputDecoration( border: const OutlineInputBorder(), - labelText: context.localizations.password, + labelText: LocaleKeys.password.tr(), ), ), ), diff --git a/lib/ui/welcome/welcome_screen.dart b/lib/ui/welcome/welcome_screen.dart index 364c597f..4c8acef4 100644 --- a/lib/ui/welcome/welcome_screen.dart +++ b/lib/ui/welcome/welcome_screen.dart @@ -2,10 +2,11 @@ import 'dart:async'; import 'package:auto_route/auto_route.dart'; import 'package:design_system/design_system.dart'; +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_template/core/model/project.dart'; -import 'package:flutter_template/ui/extensions/context_extensions.dart'; +import 'package:flutter_template/gen/locale_keys.g.dart'; import 'package:flutter_template/ui/section/error_handler/global_event_handler_cubit.dart'; import 'package:flutter_template/ui/welcome/welcome_cubit.dart'; import 'package:url_launcher/url_launcher.dart'; @@ -29,7 +30,7 @@ class _WelcomeContentScreen extends StatelessWidget { builder: (context, state) => Scaffold( appBar: AppBar( title: Text( - context.localizations.xmartlabs_projects, + LocaleKeys.xmartlabs_projects.tr(), style: context.theme.textStyles.titleMedium ?.copyWith(color: context.theme.colorScheme.onPrimary), ), @@ -42,7 +43,7 @@ class _WelcomeContentScreen extends StatelessWidget { ), onPressed: () => context.read().logOut(), child: Text( - context.localizations.log_out, + LocaleKeys.log_out.tr(), style: context.theme.textStyles.bodyMedium ?.copyWith(color: context.theme.colorScheme.onPrimary), ), diff --git a/pubspec.lock b/pubspec.lock index 98aa55d3..8c0a9c0a 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -312,6 +312,22 @@ packages: url: "https://pub.dev" source: hosted version: "5.4.3+1" + easy_localization: + dependency: "direct main" + description: + name: easy_localization + sha256: "2ccdf9db8fe4d9c5a75c122e6275674508fd0f0d49c827354967b8afcc56bbed" + url: "https://pub.dev" + source: hosted + version: "3.0.8" + easy_logger: + dependency: transitive + description: + name: easy_logger + sha256: c764a6e024846f33405a2342caf91c62e357c24b02c04dbc712ef232bf30ffb7 + url: "https://pub.dev" + source: hosted + version: "0.0.2" equatable: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index 586766e3..b7b94090 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -21,6 +21,7 @@ dependencies: cupertino_icons: 1.0.8 dartx: 1.2.0 dio: 5.4.3+1 + easy_localization: 3.0.8 equatable: 2.0.5 flutter_bloc: 8.1.5 flutter_dotenv: 5.1.0 @@ -71,10 +72,10 @@ dependency_overrides: pub_updater: 0.4.0 flutter: - generate: true uses-material-design: true assets: - assets/ + - assets/translations/ - environments/ flutter_gen: @@ -82,6 +83,7 @@ flutter_gen: exclude: - environments/* - assets/README.md + - assets/translations/* # Run command: flutter pub run flutter_launcher_icons:main flutter_launcher_icons: diff --git a/scripts/clean_up.sh b/scripts/clean_up.sh index fbb1b135..c0564e2b 100755 --- a/scripts/clean_up.sh +++ b/scripts/clean_up.sh @@ -6,4 +6,7 @@ echo ':: flutter pub get ::' fvm flutter pub get echo ':: flutter pub run build_runner build --delete-conflicting-outputs ::' -fvm flutter pub run build_runner build --delete-conflicting-outputs +fvm dart run build_runner build --delete-conflicting-outputs + +echo ':: flutter pub run easy_localization:generate -f keys -o locale_keys.g.dart ::' +fvm flutter pub run easy_localization:generate -S assets/translations -O lib/gen -f keys -o locale_keys.g.dart diff --git a/scripts/generate_localization_keys.sh b/scripts/generate_localization_keys.sh new file mode 100755 index 00000000..dd5949f1 --- /dev/null +++ b/scripts/generate_localization_keys.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +echo ':: Generate localization keys ::' +fvm flutter pub run easy_localization:generate -S assets/translations -O lib/gen -f keys -o locale_keys.g.dart