feat(i18n): add default locale

This commit is contained in:
Hugo Pointcheval 2023-02-28 11:22:04 +01:00
parent cbbde8db85
commit 17ece11170
Signed by: hugo
GPG Key ID: 3AAC487E131E00BC
3 changed files with 73 additions and 26 deletions

View File

@ -0,0 +1,40 @@
// 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 <https://www.gnu.org/licenses/>.
import 'dart:convert';
import 'package:flutter/services.dart';
class AssetsUtils {
/// Checks if the given [assetPath] is a local asset.
///
/// In fact, this method loads the asset and checks if it is null.
static Future<bool> assetExists(String assetPath) async {
final encoded = utf8.encoder.convert(
Uri(path: Uri.encodeFull(assetPath)).path,
);
final asset = await ServicesBinding.instance.defaultBinaryMessenger
.send('flutter/assets', encoded.buffer.asByteData());
return asset != null;
}
/// Cleans the given [path] by removing all the consecutive slashes.
/// For example, `///a///b///c///` becomes `/a/b/c/`.
/// It also removes the first slash if it exists.
static String cleanPath(String path) =>
path.replaceAll(RegExp(r'\/+'), '/').replaceFirst(RegExp(r'^\/'), '');
}

View File

@ -14,11 +14,10 @@
// 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 'dart:convert';
import 'package:flutter/services.dart' show ServicesBinding, rootBundle;
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:wyatt_i18n/src/core/utils/assets_utils.dart';
import 'package:wyatt_i18n/src/domain/data_sources/i18n_data_source.dart';
/// {@template assets_file_data_source_impl}
@ -34,10 +33,11 @@ import 'package:wyatt_i18n/src/domain/data_sources/i18n_data_source.dart';
/// {@endtemplate}
class AssetsFileDataSourceImpl extends I18nDataSource {
/// {@macro assets_file_data_source_impl}
const AssetsFileDataSourceImpl({
AssetsFileDataSourceImpl({
this.basePath = 'assets',
this.baseName = 'i18n',
super.format = Format.arb,
super.defaultLocale = 'en',
}) : super();
/// The folder where the i18n files are located.
@ -46,29 +46,23 @@ class AssetsFileDataSourceImpl extends I18nDataSource {
/// The name of the i18n files without the extension.
final String baseName;
/// Checks if the given [assetPath] is a local asset.
///
/// In fact, this method loads the asset and checks if it is null.
Future<bool> assetExists(String assetPath) async {
final encoded = utf8.encoder.convert(
Uri(path: Uri.encodeFull(assetPath)).path,
);
final asset = await ServicesBinding.instance.defaultBinaryMessenger
.send('flutter/assets', encoded.buffer.asByteData());
return asset != null;
}
/// The asset bundle used to load the i18n files.
final AssetBundle assetBundle = rootBundle;
/// Tries to load the i18n file from the given [locale].
Future<String> _tryLoad(String? locale) async {
String? content;
final ext = format.name;
final path = locale == null
? '$basePath/$baseName.$ext'
: '$basePath/$baseName.$locale.$ext';
final path = AssetsUtils.cleanPath(
locale == null
? '$basePath/$baseName.$defaultLocale.$ext'
: '$basePath/$baseName.$locale.$ext',
);
try {
if (await assetExists(path)) {
content = await rootBundle.loadString(path);
if (await AssetsUtils.assetExists(path)) {
content = await assetBundle.loadString(path);
}
} catch (_) {
content = null;
@ -78,7 +72,10 @@ class AssetsFileDataSourceImpl extends I18nDataSource {
/// default i18n file.
if (content == null && locale != null) {
try {
content = await rootBundle.loadString('$basePath/$baseName.$ext');
final fallbackPath = AssetsUtils.cleanPath(
'$basePath/$baseName.$defaultLocale.$ext',
);
content = await assetBundle.loadString(fallbackPath);
} catch (_) {
throw SourceNotFoundException(path, format: format);
}
@ -98,7 +95,7 @@ class AssetsFileDataSourceImpl extends I18nDataSource {
Future<String> _tryLoadUri(Uri uri) async {
String? content;
try {
content = await rootBundle.loadString(uri.toString());
content = await assetBundle.loadString(uri.toString());
} catch (_) {
throw SourceNotFoundException(uri.toString(), format: format);
}

View File

@ -17,23 +17,33 @@
import 'package:wyatt_architecture/wyatt_architecture.dart';
import 'package:wyatt_i18n/src/core/enums/format.dart';
/// {@template i18n_data_source}
/// Base class for i18n data sources.
///
///
/// This class is used to load i18n files from a source.
/// It returns a [Future] that resolves to a [String] containing the i18n file
/// contents.
/// {@endtemplate}
abstract class I18nDataSource extends BaseDataSource {
const I18nDataSource({this.format = Format.arb});
/// {@macro i18n_data_source}
const I18nDataSource({
this.format = Format.arb,
this.defaultLocale = 'en',
});
/// The format of the i18n file.
final Format format;
/// The default locale.
final String defaultLocale;
/// 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<String> load({required String? locale});
/// Loads the i18n file from the source.
///
///
/// This method is used to load the i18n file from the given [Uri].
Future<String> loadFrom(Uri uri);
}