From 7c9081b9812ba34bd1d1154a2ef9ccb53fa18bda Mon Sep 17 00:00:00 2001 From: Hugo Pointcheval Date: Wed, 1 Mar 2023 17:27:19 +0100 Subject: [PATCH] feat(i18n)!: rename few files + add clearer documentation --- packages/wyatt_i18n/README.md | 127 ++++++++++++-- packages/wyatt_i18n/example/lib/main.dart | 54 +++--- .../wyatt_i18n/lib/src/core/enums/format.dart | 7 +- .../lib/src/core/utils/intl_utils.dart | 24 +++ .../core/utils/{ => parsers}/arb_parser.dart | 2 +- .../utils/{ => parsers}/i18n_file_parser.dart | 8 +- .../core/utils/{ => parsers}/icu_parser.dart | 8 +- .../core/utils/{ => parsers}/json_parser.dart | 0 .../src/core/utils/parsers/locale_parser.dart | 50 ++++++ .../core/utils/{ => parsers}/yaml_parser.dart | 0 .../wyatt_i18n/lib/src/core/utils/utils.dart | 10 +- packages/wyatt_i18n/lib/src/data/data.dart | 6 +- ...dart => assets_i18n_data_source_impl.dart} | 90 +++++++--- ...art => network_i18n_data_source_impl.dart} | 74 +++++++-- .../repositories/i18n_repository_impl.dart | 157 ------------------ .../domain/data_sources/i18n_data_source.dart | 53 +++++- .../wyatt_i18n/lib/src/domain/domain.dart | 2 - .../lib/src/domain/entities/i18n.dart | 117 +++++++++---- .../lib/src/domain/entities/i18n_file.dart | 54 ------ .../domain/repositories/i18n_repository.dart | 77 --------- .../lib/src/presentation/i18n_delegate.dart | 15 +- packages/wyatt_i18n/test/wyatt_i18n_test.dart | 4 +- 22 files changed, 510 insertions(+), 429 deletions(-) create mode 100644 packages/wyatt_i18n/lib/src/core/utils/intl_utils.dart rename packages/wyatt_i18n/lib/src/core/utils/{ => parsers}/arb_parser.dart (95%) rename packages/wyatt_i18n/lib/src/core/utils/{ => parsers}/i18n_file_parser.dart (95%) rename packages/wyatt_i18n/lib/src/core/utils/{ => parsers}/icu_parser.dart (96%) rename packages/wyatt_i18n/lib/src/core/utils/{ => parsers}/json_parser.dart (100%) create mode 100644 packages/wyatt_i18n/lib/src/core/utils/parsers/locale_parser.dart rename packages/wyatt_i18n/lib/src/core/utils/{ => parsers}/yaml_parser.dart (100%) rename packages/wyatt_i18n/lib/src/data/data_sources/{assets_file_data_source_impl.dart => assets_i18n_data_source_impl.dart} (57%) rename packages/wyatt_i18n/lib/src/data/data_sources/{network_data_source_impl.dart => network_i18n_data_source_impl.dart} (65%) delete mode 100644 packages/wyatt_i18n/lib/src/data/repositories/i18n_repository_impl.dart delete mode 100644 packages/wyatt_i18n/lib/src/domain/entities/i18n_file.dart delete mode 100644 packages/wyatt_i18n/lib/src/domain/repositories/i18n_repository.dart diff --git a/packages/wyatt_i18n/README.md b/packages/wyatt_i18n/README.md index c3d48592..09cd8b61 100644 --- a/packages/wyatt_i18n/README.md +++ b/packages/wyatt_i18n/README.md @@ -23,28 +23,131 @@ SDK: Flutter

-A package by wyatt studio +This package aims to facilitate and improve the internationalization of your applications. It allows, among other things, to load translation files on the fly during the execution of the application. ## Features -TODO: List what your package can do. Maybe include images, gifs, or videos. +* Load translation files + + [x] Load translation files from assets + + [x] Load translation files from the network + + [ ] Load translation files from the file system + + [ ] Load translation files from multiple sources -## Getting started +* Supports multiple formats + + [x] Supports JSON format + + [x] Supports YAML format + + [x] Supports ARB formats + + [ ] Supports CSV format + + [x] Supports custom formats parsers (see Parser class) -TODO: List prerequisites and provide or point to information on how to -start using the package. +* Usage + + [x] Detects the current locale + + [x] Act as a LocalizationDelegate + + [x] Act as a DataSource (in the sense of the Wyatt Architecture) + +* Other + + [ ] Generate translation constants from fallback translation files ## Usage -TODO: Include short and useful examples for package users. Add longer examples -to `/example` folder. +You can use this package as a LocalizationDelegate or as a DataSource. + +### As a LocalizationDelegate + +It is recommended to use this package as a LocalizationDelegate. This allows you to use the `context.i18n` method to translate your strings. It follows the standard of the `flutter_localizations` package and you can use it in the MaterialApp widget as follows: ```dart -const like = 'sample'; +class MyApp extends StatelessWidget { + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'Flutter Demo', + theme: ThemeData( + primarySwatch: Colors.blue, + ), + localizationsDelegates: [ + I18nDelegate( + dataSource: NetworkI18nDataSourceImpl( + baseUri: 'https://i18n.wyatt-studio.fr/apps/flutter_demo', + ), + localeTransformer: (locale) => locale.languageCode, + ), + GlobalMaterialLocalizations.delegate, + GlobalWidgetsLocalizations.delegate + ], + supportedLocales: [ + Locale('en'), + Locale('fr'), + ], + home: MyHomePage(title: 'Flutter Demo Home Page'), + ); + } +} ``` -## Additional information +And in your widgets: -TODO: Tell users more about the package: where to find more information, how to -contribute to the package, how to file issues, what response they can expect -from the package authors, and more. +```dart +Text(context.i18n('youHavePushed', {'count': 42})), +// => 'You have pushed the button this many times: 42' in English +// => 'Vous avez appuyé sur le bouton ce nombre de fois: 42' in French +``` + +### As a DataSource + +This gives you more control over the internationalization of your application. You can handle the loading of the translation files yourself. + +For example, if you want to create a Repository that will handle the loading of the translation files, you can do it. + +Provide DataSource with GetIt: + +```dart +// Initialize i18n +final I18nDataSource i18nDataSource = + await AssetsI18nDataSourceImpl.withSystemLocale( + basePath: 'l10n', + baseName: 'intl', + localeTransformer: (locale) => locale.languageCode, +); + +// Initialize real sources/services +GetIt.I.registerLazySingleton( + () => i18nDataSource, +); +``` + +Create a Repository: + +```dart +class I18nRepository { + I18nRepository({ + required this.dataSource, + }); + + final I18nDataSource dataSource; + + Future load() async { + final i18n = await dataSource.load(); + return i18n; + } +} +``` + +And use it in your cubit: + +```dart +class MyCubit extends Cubit { + MyCubit({ + required this.i18nRepository, + }) : super(MyState()); + + final I18nRepository i18nRepository; + + Future loadI18n() async { + final i18n = await i18nRepository.load(); + emit(state.copyWith(i18n: i18n)); + } +} +``` + +> Note: you should create a cache system to avoid reloading the translation files every time. diff --git a/packages/wyatt_i18n/example/lib/main.dart b/packages/wyatt_i18n/example/lib/main.dart index d5b685b0..fa13f3f7 100644 --- a/packages/wyatt_i18n/example/lib/main.dart +++ b/packages/wyatt_i18n/example/lib/main.dart @@ -29,36 +29,32 @@ class App extends StatelessWidget { static const String title = 'Wyatt i18n Example'; @override - Widget build(BuildContext context) { - final I18nDataSource dataSource = NetworkDataSourceImpl( - baseUri: Uri.parse( - 'https://git.wyatt-studio.fr/Wyatt-FOSS/wyatt-packages/raw/commit/75f561a19e0484e67e511dbf29601ec5f58544aa/packages/wyatt_i18n/example/assets/', - ), - ); - - final I18nRepository repository = - I18nRepositoryImpl(dataSource: dataSource); - - return MaterialApp( - title: title, - theme: ThemeData( - primarySwatch: Colors.blue, - ), - localizationsDelegates: [ - I18nDelegate(repository: repository), - GlobalMaterialLocalizations.delegate, - GlobalWidgetsLocalizations.delegate - ], - home: Scaffold( - appBar: AppBar( - title: const Text(title), + Widget build(BuildContext context) => MaterialApp( + title: title, + theme: ThemeData( + primarySwatch: Colors.blue, ), - body: Builder( - builder: (ctx) => Center( - child: Text(ctx.i18n('youHavePushed', {'count': 654})), + localizationsDelegates: [ + I18nDelegate( + dataSource: NetworkI18nDataSourceImpl( + baseUri: Uri.parse( + 'https://git.wyatt-studio.fr/Wyatt-FOSS/wyatt-packages/raw/commit/75f561a19e0484e67e511dbf29601ec5f58544aa/packages/wyatt_i18n/example/assets/', + ), + localeTransformer: (locale) => locale.languageCode, + ), + ), + GlobalMaterialLocalizations.delegate, + GlobalWidgetsLocalizations.delegate + ], + home: Scaffold( + appBar: AppBar( + title: const Text(title), + ), + body: Builder( + builder: (ctx) => Center( + child: Text(ctx.i18n('youHavePushed', {'count': 654})), + ), ), ), - ), - ); - } + ); } diff --git a/packages/wyatt_i18n/lib/src/core/enums/format.dart b/packages/wyatt_i18n/lib/src/core/enums/format.dart index 8b043174..f428e25d 100644 --- a/packages/wyatt_i18n/lib/src/core/enums/format.dart +++ b/packages/wyatt_i18n/lib/src/core/enums/format.dart @@ -14,11 +14,10 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -import 'package:wyatt_i18n/src/core/utils/arb_parser.dart'; -import 'package:wyatt_i18n/src/core/utils/json_parser.dart'; import 'package:wyatt_i18n/src/core/utils/parser.dart'; - -import 'package:wyatt_i18n/src/core/utils/yaml_parser.dart'; +import 'package:wyatt_i18n/src/core/utils/parsers/arb_parser.dart'; +import 'package:wyatt_i18n/src/core/utils/parsers/json_parser.dart'; +import 'package:wyatt_i18n/src/core/utils/parsers/yaml_parser.dart'; /// Enum for i18n file formats and extensions. /// diff --git a/packages/wyatt_i18n/lib/src/core/utils/intl_utils.dart b/packages/wyatt_i18n/lib/src/core/utils/intl_utils.dart new file mode 100644 index 00000000..1721168d --- /dev/null +++ b/packages/wyatt_i18n/lib/src/core/utils/intl_utils.dart @@ -0,0 +1,24 @@ +// Copyright (C) 2023 WYATT GROUP +// Please see the AUTHORS file for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +import 'package:intl/intl_standalone.dart' + if (dart.library.html) 'package:intl/intl_browser.dart'; + +/// Utility class for internationalization. +abstract class IntlUtils { + /// Returns system locale. + static Future getSystemLocale() => findSystemLocale(); +} diff --git a/packages/wyatt_i18n/lib/src/core/utils/arb_parser.dart b/packages/wyatt_i18n/lib/src/core/utils/parsers/arb_parser.dart similarity index 95% rename from packages/wyatt_i18n/lib/src/core/utils/arb_parser.dart rename to packages/wyatt_i18n/lib/src/core/utils/parsers/arb_parser.dart index 1a064a70..2ec303b6 100644 --- a/packages/wyatt_i18n/lib/src/core/utils/arb_parser.dart +++ b/packages/wyatt_i18n/lib/src/core/utils/parsers/arb_parser.dart @@ -14,8 +14,8 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -import 'package:wyatt_i18n/src/core/utils/json_parser.dart'; import 'package:wyatt_i18n/src/core/utils/parser.dart'; +import 'package:wyatt_i18n/src/core/utils/parsers/json_parser.dart'; /// {@template arb_parser} /// A class that parses a given input of type [String] into a given output diff --git a/packages/wyatt_i18n/lib/src/core/utils/i18n_file_parser.dart b/packages/wyatt_i18n/lib/src/core/utils/parsers/i18n_file_parser.dart similarity index 95% rename from packages/wyatt_i18n/lib/src/core/utils/i18n_file_parser.dart rename to packages/wyatt_i18n/lib/src/core/utils/parsers/i18n_file_parser.dart index 3314c581..3cc371e1 100644 --- a/packages/wyatt_i18n/lib/src/core/utils/i18n_file_parser.dart +++ b/packages/wyatt_i18n/lib/src/core/utils/parsers/i18n_file_parser.dart @@ -20,8 +20,8 @@ import 'package:wyatt_i18n/src/domain/entities/tokens.dart'; import 'package:wyatt_i18n/wyatt_i18n.dart'; /// {@template i18n_file_parser} -/// This class is responsible for parsing the [I18nFile] and returning the -/// translated string. +/// This class is responsible for parsing the [I18n] and returning the +/// translated string using the [arguments] provided and the [IcuParser]. /// {@endtemplate} class I18nFileParser extends Parser { /// {@macro i18n_file_parser} @@ -30,8 +30,8 @@ class I18nFileParser extends Parser { this.arguments = const {}, }) : super(); - /// The [I18nFile] to be parsed. - final I18nFile i18n; + /// The [I18n] to be parsed. + final I18n i18n; /// The arguments to be used in the translation. final Map arguments; diff --git a/packages/wyatt_i18n/lib/src/core/utils/icu_parser.dart b/packages/wyatt_i18n/lib/src/core/utils/parsers/icu_parser.dart similarity index 96% rename from packages/wyatt_i18n/lib/src/core/utils/icu_parser.dart rename to packages/wyatt_i18n/lib/src/core/utils/parsers/icu_parser.dart index 0eb6d84a..192e752b 100644 --- a/packages/wyatt_i18n/lib/src/core/utils/icu_parser.dart +++ b/packages/wyatt_i18n/lib/src/core/utils/parsers/icu_parser.dart @@ -17,6 +17,7 @@ // ignore_for_file: avoid_dynamic_calls, inference_failure_on_untyped_parameter import 'package:petitparser/petitparser.dart' hide Token; +import 'package:wyatt_i18n/src/core/utils/parser.dart' as wyatt; import 'package:wyatt_i18n/src/domain/entities/tokens.dart'; /// {@template icu_parser} @@ -24,7 +25,7 @@ import 'package:wyatt_i18n/src/domain/entities/tokens.dart'; /// See https://unicode-org.github.io/icu/userguide/format_parse/messages/ /// for the syntax. /// {@endtemplate} -class IcuParser { +class IcuParser extends wyatt.Parser?> { /// {@macro icu_parser} IcuParser() { // There is a cycle here, so we need the explicit @@ -167,12 +168,13 @@ class IcuParser { Parser get parameter => (openCurly & id & closeCurly) .map((result) => Argument(result[1] as String)); - List? parse(String message) { + @override + List? parse(String input) { final parsed = (compound | pluralOrGenderOrSelect | simpleText | empty) .map( (result) => List.from(result is List ? result : [result]), ) - .parse(message); + .parse(input); return parsed.isSuccess ? parsed.value : null; } diff --git a/packages/wyatt_i18n/lib/src/core/utils/json_parser.dart b/packages/wyatt_i18n/lib/src/core/utils/parsers/json_parser.dart similarity index 100% rename from packages/wyatt_i18n/lib/src/core/utils/json_parser.dart rename to packages/wyatt_i18n/lib/src/core/utils/parsers/json_parser.dart diff --git a/packages/wyatt_i18n/lib/src/core/utils/parsers/locale_parser.dart b/packages/wyatt_i18n/lib/src/core/utils/parsers/locale_parser.dart new file mode 100644 index 00000000..03192204 --- /dev/null +++ b/packages/wyatt_i18n/lib/src/core/utils/parsers/locale_parser.dart @@ -0,0 +1,50 @@ +// Copyright (C) 2023 WYATT GROUP +// Please see the AUTHORS file for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +import 'package:flutter/widgets.dart'; +import 'package:wyatt_i18n/wyatt_i18n.dart'; + +/// {@template locale_parser} +/// A parser that parses a [String] into a [Locale]. +/// Example: +/// ```dart +/// final parser = LocaleParser(); +/// final locale = parser.parse('en-US'); +/// // locale.languageCode == 'en' +/// // locale.countryCode == 'US' +/// ``` +/// {@endtemplate} +class LocaleParser extends Parser { + /// {@macro locale_parser} + const LocaleParser() : super(); + + @override + Locale parse(String input) { + final languageParts = RegExp('([a-z]*)[_-]?([a-z|A-Z]*)').firstMatch(input); + final languageCode = languageParts?.group(1); + final countryCode = languageParts?.group(2); + if (languageCode == null) { + throw ParserException("Can't parse locale tag '$input'", null); + } + + return Locale(languageCode, countryCode); + } + + /// Serializes the given [locale] into a [String]. + String serialize(Locale locale) => locale.countryCode == null + ? locale.languageCode + : '${locale.languageCode}_${locale.countryCode}'; +} diff --git a/packages/wyatt_i18n/lib/src/core/utils/yaml_parser.dart b/packages/wyatt_i18n/lib/src/core/utils/parsers/yaml_parser.dart similarity index 100% rename from packages/wyatt_i18n/lib/src/core/utils/yaml_parser.dart rename to packages/wyatt_i18n/lib/src/core/utils/parsers/yaml_parser.dart diff --git a/packages/wyatt_i18n/lib/src/core/utils/utils.dart b/packages/wyatt_i18n/lib/src/core/utils/utils.dart index d822ac44..000da567 100644 --- a/packages/wyatt_i18n/lib/src/core/utils/utils.dart +++ b/packages/wyatt_i18n/lib/src/core/utils/utils.dart @@ -14,9 +14,9 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -export 'arb_parser.dart'; -export 'i18n_file_parser.dart'; -export 'icu_parser.dart'; -export 'json_parser.dart'; export 'parser.dart'; -export 'yaml_parser.dart'; +export 'parsers/arb_parser.dart'; +export 'parsers/i18n_file_parser.dart'; +export 'parsers/icu_parser.dart'; +export 'parsers/json_parser.dart'; +export 'parsers/yaml_parser.dart'; diff --git a/packages/wyatt_i18n/lib/src/data/data.dart b/packages/wyatt_i18n/lib/src/data/data.dart index 876c86da..9fdb7a4a 100644 --- a/packages/wyatt_i18n/lib/src/data/data.dart +++ b/packages/wyatt_i18n/lib/src/data/data.dart @@ -14,6 +14,6 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -export 'data_sources/assets_file_data_source_impl.dart'; -export 'data_sources/network_data_source_impl.dart'; -export 'repositories/i18n_repository_impl.dart'; +export '../domain/entities/i18n.dart'; +export 'data_sources/assets_i18n_data_source_impl.dart'; +export 'data_sources/network_i18n_data_source_impl.dart'; diff --git a/packages/wyatt_i18n/lib/src/data/data_sources/assets_file_data_source_impl.dart b/packages/wyatt_i18n/lib/src/data/data_sources/assets_i18n_data_source_impl.dart similarity index 57% rename from packages/wyatt_i18n/lib/src/data/data_sources/assets_file_data_source_impl.dart rename to packages/wyatt_i18n/lib/src/data/data_sources/assets_i18n_data_source_impl.dart index 5cfecc4e..008d8dc9 100644 --- a/packages/wyatt_i18n/lib/src/data/data_sources/assets_file_data_source_impl.dart +++ b/packages/wyatt_i18n/lib/src/data/data_sources/assets_i18n_data_source_impl.dart @@ -15,31 +15,61 @@ // along with this program. If not, see . import 'package:flutter/services.dart' show AssetBundle, rootBundle; -import 'package:wyatt_i18n/src/core/enums/format.dart'; -import 'package:wyatt_i18n/src/core/exceptions/exceptions.dart'; +import 'package:flutter/widgets.dart'; import 'package:wyatt_i18n/src/core/utils/assets_utils.dart'; -import 'package:wyatt_i18n/src/domain/data_sources/i18n_data_source.dart'; +import 'package:wyatt_i18n/src/core/utils/intl_utils.dart'; +import 'package:wyatt_i18n/src/core/utils/parsers/locale_parser.dart'; +import 'package:wyatt_i18n/wyatt_i18n.dart'; -/// {@template assets_file_data_source_impl} +/// {@template assets_i18n_data_source_impl} /// Implementation of [I18nDataSource] that loads i18n files from the assets. /// /// The [basePath] is the folder where the i18n files are located. /// The [baseName] is the name of the i18n files without the extension. /// The [format] is the format of the i18n files. +/// The [defaultLocale] is the default locale to use when the locale is not +/// specified. +/// The [separator] is the separator to use when the locale is specified. /// /// For example, if the i18n files are located in the `assets/l10n/` and are /// named `i18n.en.arb`, `i18n.fr.arb`, etc., then the [basePath] /// is `l10n` and the [baseName] is `i18n` and the [format] is [Format.arb]. /// {@endtemplate} -class AssetsFileDataSourceImpl extends I18nDataSource { - /// {@macro assets_file_data_source_impl} - AssetsFileDataSourceImpl({ +class AssetsI18nDataSourceImpl extends I18nDataSource { + /// {@macro assets_i18n_data_source_impl} + AssetsI18nDataSourceImpl({ this.basePath = '', this.baseName = 'i18n', super.format = Format.arb, - super.defaultLocale = 'en', + super.defaultLocale = const Locale('en'), + super.separator = '.', + super.localeTransformer, }) : super(); + /// Creates a new instance of [AssetsI18nDataSourceImpl] with the system + /// locale as the default locale. + /// So, the [defaultLocale] is the system locale and you don't need to + /// specify it when you load the i18n file. + // ignore: long-parameter-list + static Future withSystemLocale({ + String basePath = '', + String baseName = 'i18n', + Format format = Format.arb, + String separator = '.', + LocaleTransformer? localeTransformer, + }) async { + final locale = await IntlUtils.getSystemLocale(); + + return AssetsI18nDataSourceImpl( + basePath: basePath, + baseName: baseName, + format: format, + defaultLocale: const LocaleParser().parse(locale), + separator: separator, + localeTransformer: localeTransformer, + ); + } + /// The folder where the i18n files are located. /// /// Note: The path is relative to the `assets` folder. So, `assets/l10n/` @@ -53,15 +83,20 @@ class AssetsFileDataSourceImpl extends I18nDataSource { final AssetBundle assetBundle = rootBundle; /// Tries to load the i18n file from the given [locale]. - Future _tryLoad(String? locale) async { + Future _tryLoad(Locale? locale) async { String? content; final ext = format.name; - final path = AssetsUtils.cleanPath( - locale == null - ? '$basePath/$baseName.$defaultLocale.$ext' - : '$basePath/$baseName.$locale.$ext', - ); + final defaultLocaleString = localeTransformer?.call(defaultLocale) ?? + const LocaleParser().serialize(defaultLocale); + + final localeString = locale == null + ? defaultLocaleString + : localeTransformer?.call(locale) ?? + const LocaleParser().serialize(locale); + + final path = + AssetsUtils.cleanPath('$basePath/$baseName.$localeString.$ext'); try { if (await AssetsUtils.assetExists(path)) { @@ -76,7 +111,7 @@ class AssetsFileDataSourceImpl extends I18nDataSource { if (content == null && locale != null) { try { final fallbackPath = AssetsUtils.cleanPath( - '$basePath/$baseName.$defaultLocale.$ext', + '$basePath/$baseName.$defaultLocaleString.$ext', ); content = await assetBundle.loadString(fallbackPath); } catch (_) { @@ -90,12 +125,19 @@ class AssetsFileDataSourceImpl extends I18nDataSource { throw SourceNotFoundException(path, format: format); } - return content; + /// Parse the i18n file. + final parsedData = format.parser.parse(content); + + return I18n( + unparsedData: content, + data: parsedData, + locale: locale, + ); } /// Tries to load the i18n file from a given [uri]. /// This method is used when the [uri] is not null. - Future _tryLoadUri(Uri uri) async { + Future _tryLoadUri(Uri uri) async { String? content; try { content = await assetBundle.loadString(uri.toString()); @@ -103,7 +145,13 @@ class AssetsFileDataSourceImpl extends I18nDataSource { throw SourceNotFoundException(uri.toString(), format: format); } - return content; + /// Parse the i18n file. + final parsedData = format.parser.parse(content); + + return I18n( + unparsedData: content, + data: parsedData, + ); } /// Loads the i18n file from Assets folder. @@ -111,7 +159,8 @@ class AssetsFileDataSourceImpl extends I18nDataSource { /// The i18n file must be in [basePath], named [baseName] + /// `..` and must be specified in the `pubspec.yaml` file. @override - Future load({required String? locale}) async => _tryLoad(locale); + Future load({Locale? locale}) async => + super.currentI18nFile = await _tryLoad(locale); /// Loads the i18n file from Assets folder. /// @@ -119,5 +168,6 @@ class AssetsFileDataSourceImpl extends I18nDataSource { /// from the given [uri]. In this case, the [basePath] and the /// [baseName] are ignored. And there is no fallback. @override - Future loadFrom(Uri uri) async => _tryLoadUri(uri); + Future loadFrom(Uri uri) async => + super.currentI18nFile = await _tryLoadUri(uri); } diff --git a/packages/wyatt_i18n/lib/src/data/data_sources/network_data_source_impl.dart b/packages/wyatt_i18n/lib/src/data/data_sources/network_i18n_data_source_impl.dart similarity index 65% rename from packages/wyatt_i18n/lib/src/data/data_sources/network_data_source_impl.dart rename to packages/wyatt_i18n/lib/src/data/data_sources/network_i18n_data_source_impl.dart index 559ecfad..64bd3de4 100644 --- a/packages/wyatt_i18n/lib/src/data/data_sources/network_data_source_impl.dart +++ b/packages/wyatt_i18n/lib/src/data/data_sources/network_i18n_data_source_impl.dart @@ -15,19 +15,26 @@ // along with this program. If not, see . import 'package:flutter/services.dart'; +import 'package:flutter/widgets.dart'; import 'package:http/http.dart' as http; import 'package:wyatt_i18n/src/core/enums/format.dart'; import 'package:wyatt_i18n/src/core/exceptions/exceptions.dart'; import 'package:wyatt_i18n/src/core/utils/assets_utils.dart'; +import 'package:wyatt_i18n/src/core/utils/intl_utils.dart'; +import 'package:wyatt_i18n/src/core/utils/parsers/locale_parser.dart'; import 'package:wyatt_i18n/src/domain/data_sources/i18n_data_source.dart'; +import 'package:wyatt_i18n/src/domain/entities/i18n.dart'; -/// {@template network_data_source_impl} +/// {@template network_i18n_data_source_impl} /// Implementation of [I18nDataSource] that loads i18n files from the network. /// /// The [baseUri] is the base uri where the i18n files are located. /// The [baseName] is the name of the i18n files without the extension. /// The [fallbackAssetPath] is the path to the fallback i18n files. /// The [format] is the format of the i18n files. +/// The [defaultLocale] is the default locale to use when the locale is not +/// specified. +/// The [separator] is the separator to use when the locale is specified. /// /// For example, if the i18n files are located at `https://example.com/i18n/` /// and are named `i18n.en.arb`, `i18n.fr.arb`, etc., then the [baseUri] is @@ -42,16 +49,42 @@ import 'package:wyatt_i18n/src/domain/data_sources/i18n_data_source.dart'; /// and are named `i18n.arb`, `i18n.en.arb`, `i18n.fr.arb`, etc., then the /// [fallbackAssetPath] is `l10n`. /// {@endtemplate} -class NetworkDataSourceImpl extends I18nDataSource { - /// {@macro network_data_source_impl} - const NetworkDataSourceImpl({ +class NetworkI18nDataSourceImpl extends I18nDataSource { + /// {@macro network_i18n_data_source_impl} + NetworkI18nDataSourceImpl({ required this.baseUri, this.baseName = 'i18n', this.fallbackAssetPath = '', super.format = Format.arb, - super.defaultLocale = 'en', + super.defaultLocale = const Locale('en'), + super.separator = '.', + super.localeTransformer, }) : super(); + /// Creates a new instance of [NetworkI18nDataSourceImpl] with the system + /// locale as the default locale. + /// So, the [defaultLocale] is the system locale and you don't need to + /// specify it when you load the i18n file. + // ignore: long-parameter-list + static Future withSystemLocale({ + required Uri baseUri, + String baseName = 'i18n', + Format format = Format.arb, + String separator = '.', + LocaleTransformer? localeTransformer, + }) async { + final locale = await IntlUtils.getSystemLocale(); + + return NetworkI18nDataSourceImpl( + baseUri: baseUri, + baseName: baseName, + format: format, + defaultLocale: const LocaleParser().parse(locale), + separator: separator, + localeTransformer: localeTransformer, + ); + } + /// The base uri where the i18n files are located. final Uri baseUri; @@ -68,18 +101,26 @@ class NetworkDataSourceImpl extends I18nDataSource { final String fallbackAssetPath; /// Tries to load the i18n file from the given [locale]. - Future _tryLoad(String? locale, {Uri? overrideUri}) async { + Future _tryLoad(Locale? locale, {Uri? overrideUri}) async { String? content; final ext = format.name; + final defaultLocaleString = localeTransformer?.call(defaultLocale) ?? + const LocaleParser().serialize(defaultLocale); + + final localeString = locale == null + ? defaultLocaleString + : localeTransformer?.call(locale) ?? + const LocaleParser().serialize(locale); + /// If the locale is null, then we try to load the default i18n file from /// the fallback asset path. /// Otherwise, we try to load the i18n file for the given locale from the /// base uri. final path = AssetsUtils.cleanPath( locale == null - ? '$fallbackAssetPath/$baseName.$defaultLocale.$ext' - : overrideUri?.toString() ?? '$baseUri/$baseName.$locale.$ext', + ? '$fallbackAssetPath/$baseName.$defaultLocaleString.$ext' + : overrideUri?.toString() ?? '$baseUri/$baseName.$localeString.$ext', ); if (locale == null) { @@ -108,7 +149,7 @@ class NetworkDataSourceImpl extends I18nDataSource { if (content == null && locale != null) { try { final fallbackPath = AssetsUtils.cleanPath( - '$fallbackAssetPath/$baseName.$defaultLocale.$ext', + '$fallbackAssetPath/$baseName.$defaultLocaleString.$ext', ); content = await rootBundle.loadString(fallbackPath); } catch (_) { @@ -122,7 +163,14 @@ class NetworkDataSourceImpl extends I18nDataSource { throw SourceNotFoundException(path, format: format); } - return content; + /// Parse the i18n file. + final parsedData = format.parser.parse(content); + + return I18n( + unparsedData: content, + data: parsedData, + locale: locale, + ); } /// Loads the i18n file for the given [locale]. @@ -130,12 +178,14 @@ class NetworkDataSourceImpl extends I18nDataSource { /// If the [locale] is null, then the default i18n file is loaded from the /// fallback asset path. @override - Future load({required String? locale}) async => _tryLoad(locale); + Future load({Locale? locale}) async => + super.currentI18nFile = await _tryLoad(locale); /// Loads the i18n file from the given [uri]. /// /// If the fetch fails, then the fallback i18n files are loaded from the /// [fallbackAssetPath] in the root bundle. @override - Future loadFrom(Uri uri) async => _tryLoad(null, overrideUri: uri); + Future loadFrom(Uri uri) async => + super.currentI18nFile = await _tryLoad(null, overrideUri: uri); } diff --git a/packages/wyatt_i18n/lib/src/data/repositories/i18n_repository_impl.dart b/packages/wyatt_i18n/lib/src/data/repositories/i18n_repository_impl.dart deleted file mode 100644 index d3d9b9d2..00000000 --- a/packages/wyatt_i18n/lib/src/data/repositories/i18n_repository_impl.dart +++ /dev/null @@ -1,157 +0,0 @@ -// Copyright (C) 2023 WYATT GROUP -// Please see the AUTHORS file for details. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -import 'package:wyatt_architecture/wyatt_architecture.dart'; -import 'package:wyatt_i18n/wyatt_i18n.dart'; -import 'package:wyatt_type_utils/wyatt_type_utils.dart' hide Option; - -/// {@template i18n_repository_impl} -/// The default implementation of [I18nRepository]. -/// {@endtemplate} -class I18nRepositoryImpl extends I18nRepository { - /// {@macro i18n_repository_impl} - I18nRepositoryImpl({ - required this.dataSource, - }) : super(); - - /// The data source used to load the i18n file. - final I18nDataSource dataSource; - - /// The current i18n instance. - I18nFile _i18n = const I18nFile.empty(); - - @override - I18nFile get i18n => _i18n; - - Future _parse( - String content, - Parser> parser, { - String? locale, - bool strict = false, - }) async { - final parsed = parser.parse(content); - - String parsedLocale; - - /// Checks if the locale is present in the parsed data. - /// If not, throws an exception. - /// If yes, sets the locale to the parsed locale. - if (parsed.containsKey('@@locale')) { - if (strict) { - /// Checks if the parsed locale is a string. - if (parsed['@@locale'] is! String) { - throw NoLocaleException(); - } - - /// Checks if the parsed locale is the same as the given locale. - if (locale != null && parsed['@@locale'] as String != locale) { - throw InvalidLocaleException(locale, parsed['@@locale'] as String); - } - } - parsedLocale = parsed['@@locale'] as String; - } else { - if (strict) { - /// Throws an exception if the locale is not present in the parsed data. - throw NoLocaleException(); - } else { - /// Sets the locale to the given locale. - /// If the given locale is null, sets the locale to 'null'. - /// This is done to prevent the locale from being null. - /// It should never be null. - parsedLocale = locale ?? 'null'; - } - } - - return I18nFile( - locale: parsedLocale, - unparsedData: content, - data: parsed, - ); - } - - @override - FutureOrResult load({ - required String? locale, - bool strict = false, - Parser>? parser, - }) async => - await Result.tryCatchAsync( - () async { - final content = await dataSource.load(locale: locale); - - return _i18n = await _parse( - content, - parser ?? dataSource.format.parser, - locale: locale, - strict: strict, - ); - }, - (error) => error, - ); - - @override - FutureOrResult loadFrom( - Uri uri, { - Parser>? parser, - }) async => - await Result.tryCatchAsync( - () async { - final content = await dataSource.loadFrom(uri); - - /// Strict is always true when loading from a uri. Because - /// the locale is not given and can't be inferred. - return _i18n = await _parse( - content, - parser ?? dataSource.format.parser, - strict: true, - ); - }, - (error) => error, - ); - - @override - Result get( - String key, [ - Map arguments = const {}, - ]) { - final I18nFileParser parser = - I18nFileParser(i18n: _i18n, arguments: arguments); - - if (_i18n.containsKey(key)) { - return Result.tryCatch( - () => parser.parse(_i18n[key] as String, key: key), - (error) => error, - ); - } else { - throw KeyNotFoundException(key, arguments); - } - } - - @override - Result getI18n() => - Result.conditional(!_i18n.isEmpty, _i18n, NotLoadedException()); - - @override - Result getLocale() => - Result.conditional(!_i18n.isEmpty, _i18n.locale, NotLoadedException()); - - @override - Result setI18n(I18nFile i18n) { - _i18n = i18n; - - return const Ok(null); - } -} diff --git a/packages/wyatt_i18n/lib/src/domain/data_sources/i18n_data_source.dart b/packages/wyatt_i18n/lib/src/domain/data_sources/i18n_data_source.dart index 942e35b9..95531071 100644 --- a/packages/wyatt_i18n/lib/src/domain/data_sources/i18n_data_source.dart +++ b/packages/wyatt_i18n/lib/src/domain/data_sources/i18n_data_source.dart @@ -14,8 +14,11 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +import 'package:flutter/widgets.dart'; import 'package:wyatt_architecture/wyatt_architecture.dart'; -import 'package:wyatt_i18n/src/core/enums/format.dart'; +import 'package:wyatt_i18n/wyatt_i18n.dart'; + +typedef LocaleTransformer = String Function(Locale); /// {@template i18n_data_source} /// Base class for i18n data sources. @@ -26,24 +29,62 @@ import 'package:wyatt_i18n/src/core/enums/format.dart'; /// {@endtemplate} abstract class I18nDataSource extends BaseDataSource { /// {@macro i18n_data_source} - const I18nDataSource({ + I18nDataSource({ this.format = Format.arb, - this.defaultLocale = 'en', + this.defaultLocale = const Locale('en'), + this.separator = '.', + this.localeTransformer, }); /// The format of the i18n file. final Format format; /// The default locale. - final String defaultLocale; + /// This is used when the locale is `null` in the [load] method. + Locale defaultLocale; + + /// The separator used in file name. + /// This is used to separate the baseName and the locale. + /// + /// For example, if the baseName is `i18n` and the locale is `en`, if the + /// separator is `_`, the path will be `i18n_en`. + final String separator; + + /// The list of parsers used to parse the i18n file. + /// This is used to parse the i18n file into a [Map]. + // final List>> decoders; + + /// The current i18n file loaded from the source. + /// This is used to avoid loading the same file twice. + I18n? currentI18nFile; + + /// Function used to transform the [Locale] into a string. + /// This is used to get the file name from the [Locale]. + /// + /// For example, if the [Locale] is `en_US`, the file name will be `en_us`. + /// + /// But maybe you want to use `en-us` instead. In this case, you can use + /// ```dart + /// localeTransformer: (locale) => locale.toString().replaceAll('_', '-'), + /// ``` + /// to replace the `_` with `-`. + /// + /// If you want to use `en` instead of `en_US`, you can use + /// ```dart + /// localeTransformer: (locale) => locale.languageCode, + /// ``` + /// + /// If this is `null`, the default transformer will be used. + /// The default transformer will use the LocaleParser serializer method. + LocaleTransformer? localeTransformer; /// Loads the i18n file from the source. /// If [locale] is not `null`, it will load the file with the given [locale]. /// Otherwise, it will load the file from the default location. - Future load({required String? locale}); + Future load({Locale? locale}); /// Loads the i18n file from the source. /// /// This method is used to load the i18n file from the given [Uri]. - Future loadFrom(Uri uri); + Future loadFrom(Uri uri); } diff --git a/packages/wyatt_i18n/lib/src/domain/domain.dart b/packages/wyatt_i18n/lib/src/domain/domain.dart index d6f3a7f2..7a92cdf3 100644 --- a/packages/wyatt_i18n/lib/src/domain/domain.dart +++ b/packages/wyatt_i18n/lib/src/domain/domain.dart @@ -16,5 +16,3 @@ export 'data_sources/i18n_data_source.dart'; export 'entities/i18n.dart'; -export 'entities/i18n_file.dart'; -export 'repositories/i18n_repository.dart'; diff --git a/packages/wyatt_i18n/lib/src/domain/entities/i18n.dart b/packages/wyatt_i18n/lib/src/domain/entities/i18n.dart index 42280fc7..dde2bcd7 100644 --- a/packages/wyatt_i18n/lib/src/domain/entities/i18n.dart +++ b/packages/wyatt_i18n/lib/src/domain/entities/i18n.dart @@ -14,47 +14,104 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +import 'package:equatable/equatable.dart'; +import 'package:flutter/widgets.dart'; import 'package:wyatt_architecture/wyatt_architecture.dart'; -import 'package:wyatt_i18n/wyatt_i18n.dart'; +import 'package:wyatt_i18n/src/core/exceptions/exceptions.dart'; +import 'package:wyatt_i18n/src/core/utils/parsers/i18n_file_parser.dart'; +import 'package:wyatt_i18n/src/core/utils/parsers/locale_parser.dart'; -/// {@template i18n} -/// This class is used to store the translations of the application. -/// This entity is used by the I18nDelegate and Flutter's Localizations -/// widget to provide the translations to the application. +/// {@template i18n_file} +/// Data structure for i18n files. /// {@endtemplate} -class I18n extends Entity { - /// {@macro i18n} - I18n({ - required this.i18nRepository, +class I18n extends Entity with EquatableMixin { + /// {@macro i18n_file} + const I18n({ + required this.unparsedData, + required this.data, + this.locale, }) : super(); - final I18nRepository i18nRepository; + /// Creates an empty i18n file. + const I18n.empty() : this(unparsedData: '', data: const {}); - /// Get the translation of the given [key]. - /// If the [key] is not found, the [key] itself is returned. - /// If the [key] is found, the translation is returned. - /// If [args] is not null, the translation is formatted with the - /// given arguments. - String get(String key, [Map? args]) { - final result = i18nRepository.get(key, args ?? const {}); + /// The locale of the i18n file. + final Locale? locale; - return result.fold( - (value) => value, - (error) => key, - ); + /// The unparsed data of the i18n file. + final String unparsedData; + + /// The data of the i18n file. + final Map data; + + /// Gets the locale of the i18n file. + /// If [strict] is true, it will throw an exception if the locale is not + /// present in the i18n file. + /// If [strict] is false, it will return the locale of the i18n file if it is + /// present, otherwise it will return the locale passed to the constructor. + Locale getLocale({bool strict = false}) { + if (strict) { + if (data.containsKey('@@locale')) { + if (data['@@locale'] is! String) { + throw NoLocaleException(); + } + + return const LocaleParser().parse(data['@@locale'] as String); + } else { + throw NoLocaleException(); + } + } else { + if (data.containsKey('@@locale')) { + if (data['@@locale'] is! String) { + if (locale != null) { + return locale!; + } else { + throw NoLocaleException(); + } + } + + return const LocaleParser().parse(data['@@locale'] as String); + } else { + if (locale != null) { + return locale!; + } else { + throw NoLocaleException(); + } + } + } } - /// Get the translation of the given [key]. - /// - /// Note: arguments are not supported. - String operator [](String key) => get(key); + /// Gets the value of a key. + /// If the key is not present in the i18n file, it will throw a + /// [KeyNotFoundException]. + /// If the key is present in the i18n file, it will return the value of the + /// key parsed with ICU message format. + String get(String key, [Map? args]) { + final arguments = args ?? const {}; + + /// The parser used to parse the i18n file with ICU message format. + final I18nFileParser parser = + I18nFileParser(i18n: this, arguments: arguments); + + if (containsKey(key)) { + return parser.parse(this[key] as String, key: key); + } else { + throw KeyNotFoundException(key, arguments); + } + } /// Get the translation of the given [key]. String call(String key, [Map? args]) => get(key, args); - /// Load the translations from the given [locale]. - /// If the [locale] is not found, the default locale is loaded. - Future load(String locale) async { - await i18nRepository.load(locale: locale); - } + /// Checks if the i18n file contains a key. + bool containsKey(String key) => data.containsKey(key); + + /// Gets the value of a key. + dynamic operator [](String key) => data[key]; + + /// Checks if the i18n file is empty. + bool get isEmpty => data.isEmpty && unparsedData.isEmpty && locale == null; + + @override + List get props => [locale, unparsedData, data]; } diff --git a/packages/wyatt_i18n/lib/src/domain/entities/i18n_file.dart b/packages/wyatt_i18n/lib/src/domain/entities/i18n_file.dart deleted file mode 100644 index a24f863b..00000000 --- a/packages/wyatt_i18n/lib/src/domain/entities/i18n_file.dart +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright (C) 2023 WYATT GROUP -// Please see the AUTHORS file for details. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -import 'package:equatable/equatable.dart'; -import 'package:wyatt_architecture/wyatt_architecture.dart'; - -/// {@template i18n_file} -/// Data structure for i18n files. -/// {@endtemplate} -class I18nFile extends Entity with EquatableMixin { - /// {@macro i18n_file} - const I18nFile({ - required this.locale, - required this.unparsedData, - required this.data, - }) : super(); - - /// Creates an empty i18n file. - const I18nFile.empty() : this(locale: '', unparsedData: '', data: const {}); - - /// The locale of the i18n file. - final String locale; - - /// The unparsed data of the i18n file. - final String unparsedData; - - /// The data of the i18n file. - final Map data; - - /// Checks if the i18n file contains a key. - bool containsKey(String key) => data.containsKey(key); - - /// Gets the value of a key. - dynamic operator [](String key) => data[key]; - - /// Checks if the i18n file is empty. - bool get isEmpty => data.isEmpty && unparsedData.isEmpty && locale.isEmpty; - - @override - List get props => [locale, unparsedData, data]; -} diff --git a/packages/wyatt_i18n/lib/src/domain/repositories/i18n_repository.dart b/packages/wyatt_i18n/lib/src/domain/repositories/i18n_repository.dart deleted file mode 100644 index e398f390..00000000 --- a/packages/wyatt_i18n/lib/src/domain/repositories/i18n_repository.dart +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright (C) 2023 WYATT GROUP -// Please see the AUTHORS file for details. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -import 'package:wyatt_architecture/wyatt_architecture.dart'; -import 'package:wyatt_i18n/src/core/utils/parser.dart'; -import 'package:wyatt_i18n/src/domain/entities/i18n_file.dart'; -import 'package:wyatt_type_utils/wyatt_type_utils.dart'; - -/// {@template i18n_repository} -/// Base class for i18n repositories. -/// -/// This class is used to manage i18n files. -/// {@endtemplate} -abstract class I18nRepository extends BaseRepository { - /// {@macro i18n_repository} - const I18nRepository() : super(); - - /// The current i18n file. - I18nFile get i18n; - - /// Loads the i18n file from the source. - /// If [strict] is `true`, it will throw an NoLocaleException if the - /// `@@locale` key is not found in the i18n file, otherwise it will - /// set the locale to the given [locale]. - /// If [parser] is not `null`, it will use the given parser to parse - /// the i18n file. Otherwise, it will use the default parser for the format. - FutureOrResult load({ - required String? locale, - bool strict = false, - Parser>? parser, - }); - - /// Loads the i18n file from the given [uri]. - /// If [parser] is not `null`, it will use the given parser to parse - /// the i18n file. Otherwise, it will use the default parser for the format. - FutureOrResult loadFrom( - Uri uri, { - Parser>? parser, - }); - - /// Gets the translation for the given [key]. - /// - /// If [arguments] is not `null`, it will replace the placeholders in the - /// translation with the given arguments. - Result get( - String key, [ - Map arguments = const {}, - ]); - - /// Sets the current i18n instance. - /// - /// This method is used to set the current i18n instance. - Result setI18n(I18nFile i18n); - - /// Gets the current i18n instance. - /// - /// This method is used to get the current i18n instance. - Result getI18n(); - - /// Gets the current locale. - /// - /// This method is used to get the current locale. - Result getLocale(); -} diff --git a/packages/wyatt_i18n/lib/src/presentation/i18n_delegate.dart b/packages/wyatt_i18n/lib/src/presentation/i18n_delegate.dart index 8eae1508..8df4aa64 100644 --- a/packages/wyatt_i18n/lib/src/presentation/i18n_delegate.dart +++ b/packages/wyatt_i18n/lib/src/presentation/i18n_delegate.dart @@ -18,21 +18,21 @@ import 'package:flutter/cupertino.dart'; import 'package:wyatt_i18n/wyatt_i18n.dart'; /// {@template i18n_delegate} -/// A [LocalizationsDelegate] that loads the [I18n] instance. +/// A [LocalizationsDelegate] that loads the [I18n] instance from +/// the [I18nDataSource]. /// {@endtemplate} class I18nDelegate extends LocalizationsDelegate { /// {@macro i18n_delegate} I18nDelegate({ - required this.repository, + required this.dataSource, }); - /// The [I18nRepository] that will be used to load the i18n file. - final I18nRepository repository; + final I18nDataSource dataSource; /// The current locale. Locale? currentLocale; - /// Since we are using the [I18nRepository] to load the i18n file, + /// Since we are using the [I18nDataSource] to load the i18n file, /// we don't need to check if the locale is supported. /// We can just return `true`. @override @@ -40,9 +40,10 @@ class I18nDelegate extends LocalizationsDelegate { @override Future load(Locale locale) async { - await repository.load(locale: locale.languageCode); + currentLocale = locale; + final i18n = await dataSource.load(locale: locale); - return I18n(i18nRepository: repository); + return i18n; } @override diff --git a/packages/wyatt_i18n/test/wyatt_i18n_test.dart b/packages/wyatt_i18n/test/wyatt_i18n_test.dart index a1b0e305..7fc032cc 100644 --- a/packages/wyatt_i18n/test/wyatt_i18n_test.dart +++ b/packages/wyatt_i18n/test/wyatt_i18n_test.dart @@ -18,6 +18,4 @@ /// And update any widgets that are listening to the i18n map. /// /// A top level BLoC is used to provide the i18n map to the app. -void main() { - -} +void main() {}