feat(i18n): implements network data source

This commit is contained in:
Hugo Pointcheval 2023-02-28 11:22:23 +01:00
parent 17ece11170
commit 0f8f9abcf4
Signed by: hugo
GPG Key ID: 3AAC487E131E00BC
2 changed files with 108 additions and 11 deletions

View File

@ -14,21 +14,117 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:flutter/services.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/domain/data_sources/i18n_data_source.dart';
/// {@template network_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.
///
/// 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
/// `https://example.com/i18n/` and the [baseName] is `i18n` and the
/// [format] is [Format.arb].
///
/// If the i18n file for the given locale is not found or if the fetch fails,
/// then the fallback i18n files are loaded from the [fallbackAssetPath] in
/// the root bundle.
///
/// For example, if the fallback i18n files are located in the `assets` and are
/// named `i18n.arb`, `i18n.en.arb`, `i18n.fr.arb`, etc., then the
/// [fallbackAssetPath] is `assets`.
/// {@endtemplate}
class NetworkDataSourceImpl extends I18nDataSource {
const NetworkDataSourceImpl({super.format = Format.arb}) : super();
@override
Future<String> load({required String? locale}) {
// TODO(wyatt): implement load
throw UnimplementedError();
/// {@macro network_data_source_impl}
const NetworkDataSourceImpl({
required this.baseUri,
this.baseName = 'i18n',
this.fallbackAssetPath = 'assets',
super.format = Format.arb,
super.defaultLocale = 'en',
}) : super();
/// The base uri where the i18n files are located.
final Uri baseUri;
/// The name of the i18n files without the extension.
final String baseName;
/// The path to the fallback i18n files.
///
/// This is used when the i18n file for the given locale is not found or
/// if the fetch fails.
final String fallbackAssetPath;
/// Tries to load the i18n file from the given [locale].
Future<String> _tryLoad(String? locale, {Uri? overrideUri}) async {
String? content;
final ext = format.name;
/// 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',
);
if (locale == null) {
/// Load the default i18n file from the fallback asset path.
try {
if (await AssetsUtils.assetExists(path)) {
content = await rootBundle.loadString(path);
}
} catch (_) {
content = null;
}
} else {
/// Load the i18n file for the given locale from the base uri.
try {
final response = await http.get(Uri.parse(path));
if (response.statusCode >= 200 && response.statusCode < 300) {
content = response.body;
}
} catch (_) {
content = null;
}
}
/// If the i18n file is not found, then we try to load the
/// default i18n file.
if (content == null && locale != null) {
try {
final fallbackPath = AssetsUtils.cleanPath(
'$fallbackAssetPath/$baseName.$defaultLocale.$ext',
);
content = await rootBundle.loadString(fallbackPath);
} catch (_) {
throw SourceNotFoundException(path, format: format);
}
}
/// If the default i18n file is not found, then we throw an exception.
/// This case should happen only if the locale is null.
if (content == null) {
throw SourceNotFoundException(path, format: format);
}
return content;
}
@override
Future<String> loadFrom(Uri uri) {
// TODO(wyatt): implement loadFrom
throw UnimplementedError();
}
Future<String> load({required String? locale}) async => _tryLoad(locale);
@override
Future<String> loadFrom(Uri uri) async => _tryLoad(null, overrideUri: uri);
}

View File

@ -9,6 +9,7 @@ environment:
dependencies:
collection: ^1.17.0
flutter: {sdk: flutter}
http: ^0.13.5
intl: ^0.18.0
path: ^1.8.0
petitparser: ^5.1.0