import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:universal_io/io.dart';
import '../../flutter_localization.dart';
import '../../flutter_localization_platform_interface.dart';
import '../model/ensure_initialized_exception.dart';
import '../utility/preference_util.dart';
import 'flutter_localization_delegate.dart';
import 'flutter_localization_translator.dart';
typedef TranslatorCallback = void Function(Locale?);
class FlutterLocalization {
FlutterLocalization._instance();
static final FlutterLocalization instance = FlutterLocalization._instance();
FlutterLocalizationDelegate _delegate = FlutterLocalizationDelegate(null);
List<Locale> _supportedLocales = [];
Locale? _currentLocale;
bool _localeFromPreferences = false;
TranslatorCallback? onTranslatedLanguage;
final Map<String, String?> _fontFamily = {};
Future<void> ensureInitialized() async {
try {
final locale = await PreferenceUtil.getLocale();
_localeFromPreferences = locale != null;
_currentLocale = locale ?? await _platformLocale();
} catch (e) {
_currentLocale = const Locale('en');
}
}
Future<Locale> _platformLocale() async {
try {
final localeName = kIsWeb
? await FlutterLocalizationPlatform.instance.getPlatformLocale()
: Platform.localeName;
if (localeName.isEmpty) {
return const Locale('en');
}
final locale = localeName.split(RegExp(r'[-_]'));
final languageCode = locale.first;
if (languageCode.isEmpty) {
return const Locale('en');
}
return Locale.fromSubtags(
languageCode: languageCode,
countryCode: locale.length > 1 ? locale.last : null,
scriptCode: locale.length > 2 ? locale[1] : null,
);
} catch (e) {
return const Locale('en');
}
}
void init({
required String initLanguageCode,
LocalizationSource source = LocalizationSource.map,
List<MapLocale>? mapLocales,
List<JsonLocale>? jsonLocales,
}) {
if (_currentLocale == null) throw const EnsureInitializeException();
_supportedLocales.clear();
_fontFamily.clear();
switch (source) {
case LocalizationSource.map:
_configureFromMapLocales(mapLocales ?? <MapLocale>[]);
case LocalizationSource.jsonAsset:
_configureFromJsonLocales(jsonLocales ?? <JsonLocale>[]);
}
if (!_localeFromPreferences) {
_currentLocale = _generateLocale(initLanguageCode);
}
_reload();
}
void _configureFromMapLocales(List<MapLocale> locales) {
if (locales.isEmpty) {
throw ArgumentError(
'mapLocales must be provided when using LocalizationSource.map',
);
}
FlutterLocalizationTranslator.instance.mapLocales(locales);
_supportedLocales = locales.map((e) => e.locale).toList();
for (final locale in locales) {
_fontFamily.putIfAbsent(locale.languageCode, () => locale.fontFamily);
}
}
void _configureFromJsonLocales(List<JsonLocale> locales) {
if (locales.isEmpty) {
throw ArgumentError(
'jsonLocales must be provided when using LocalizationSource.jsonAsset',
);
}
FlutterLocalizationTranslator.instance.jsonLocales(locales);
_supportedLocales = locales.map((e) => e.locale).toList();
for (final locale in locales) {
_fontFamily.putIfAbsent(locale.languageCode, () => locale.fontFamily);
}
}
Locale _generateLocale(String languageCode) {
final countryCode = _getCountryCode(languageCode);
final scriptCode = _getScriptCode(languageCode);
return Locale.fromSubtags(
languageCode: languageCode,
countryCode: countryCode,
scriptCode: scriptCode,
);
}
void translate(
String languageCode, {
bool save = true,
}) {
if (languageCode == _currentLocale?.languageCode) return;
_currentLocale = _generateLocale(languageCode);
if (save) PreferenceUtil.setLocale(_currentLocale);
_reload();
}
void _reload() {
_delegate = FlutterLocalizationDelegate(_currentLocale);
onTranslatedLanguage?.call(_currentLocale);
}
String? _getCountryCode(String languageCode) {
return _supportedLocales
.singleWhere((element) => element.languageCode == languageCode)
.countryCode;
}
String? _getScriptCode(String languageCode) {
return _supportedLocales
.singleWhere((element) => element.languageCode == languageCode)
.scriptCode;
}
String getLanguageName({String? languageCode}) =>
FlutterLocalizationTranslator.instance
.getName(languageCode ?? _currentLocale!.languageCode);
List<String> get supportedLanguageCodes =>
_supportedLocales.map((e) => e.languageCode).toList();
Locale? get currentLocale => _currentLocale;
Iterable<Locale> get supportedLocales => _supportedLocales;
Iterable<LocalizationsDelegate<dynamic>> get localizationsDelegates => [
_delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
];
String? get fontFamily => _fontFamily[_currentLocale?.languageCode];
}