import 'package:core/dependency/injector.dart'; import 'package:core/models/common.dart'; import 'package:core/services/index.dart'; import 'package:core/utils/localization.utils.dart'; import 'package:core/utils/string.extensions.dart'; import 'package:flutter/widgets.dart'; import 'package:get/get.dart' hide Rx; import 'package:rxdart/rxdart.dart'; import 'package:rxdart_ext/rxdart_ext.dart'; import '../proxy/volo/abp/asp-net-core/mvc/index.dart'; import 'service.base.dart'; class LocalizationService extends ServiceBase implements TranslationService { LocalizationService(super._injector); SessionService get _sessionService => resolve(); ConfigStateService get _configStateService => resolve(); EnvironmentService get _environmentService => resolve(); String? get currentLang => _sessionService.currentLanguage; static LocalizationService get to => Injector.instance.get(); final Subject _localization$ = BehaviorSubject(); final BehaviorSubject>> _localizations$ = BehaviorSubject>>.seeded({}); @override void onInit() { super.onInit(); _listenToSetLanguage(); _initLocalizationValues(); } void _initLocalizationValues() { // var legacyResources$ = configStateService.sliceConfig((config) => config.localization?.values); // var remoteLocalizations$ = configStateService.sliceConfig((config) => config.localization?.resources); var currentLanguage$ = _sessionService.getLanguage$(); var localizations$ = _configStateService.getLocalization$(); // 本地化 Rx.combineLatest2(currentLanguage$, localizations$, (currentLang, localizations) { if (currentLang == null || localizations == null) return null; if (currentLang.isNullOrWhiteSpace() || localizations.resources == null) return null; var remote = combineLegacyandNewResources(localizations.values, localizations.resources!); if (remote == null) return null; List resourceList = []; remote.forEach((resourceName, resources) { Map texts = {}; resources.forEach((key, value) { if (value == null) return; texts.putIfAbsent(key, () => value); }); resourceList.add(LocalizationResource(resourceName, texts)); }); return Localization(currentLang, resourceList); }).whereNotNull() .listen((localization) => _localization$.add(localization)); _localization$ .where((localization) { var environment = _environmentService.getEnvironment(); return environment.localization.useLocalResources == false; }) .listen((localization) async { Map> localizations = {}; Map cultureMap = {}; for (var resource in localization.resources) { resource.texts.forEach((key, value) { cultureMap.putIfAbsent(key, () => value); }); } localizations.putIfAbsent(localization.culture, () => cultureMap); Get.clearTranslations(); Get.addTranslations(localizations); await Get.updateLocale(Locale.fromSubtags(languageCode: localization.culture)); _localizations$.add(localizations); }); } void _listenToSetLanguage() { var lanuage$ = _sessionService.onLanguageChange$(); var localization$ = _configStateService.getLocalization$(); Rx.combineLatest2(lanuage$, localization$, (lang, localization) { if (localization?.currentCulture?.cultureName == null) return null; if (lang == localization?.currentCulture?.cultureName) return null; return lang; }).whereNotNull() .listen((_) => _configStateService.refreshAppState()); } Stream localize(String resourceName, String key, {String? defaultValue}) { return _configStateService.getLocalization$() .map((localization) => LocalizationUtils.createLocalizer(localization)) .map((localize) => localize(resourceName, key, defaultValue ?? key)); } String localizeSync(String resourceName, String key, {String? defaultValue}) { var localization = _configStateService.getLocalization(); return LocalizationUtils.createLocalizer(localization)(resourceName, key, defaultValue ?? key); } Stream localizeWithFallback( List resourceNames, List keys, String defaultValue, ) { return _configStateService.getLocalization$() .map((localization) => LocalizationUtils.createLocalizerWithFallback(localization!)) .map((localizeWithFallback) => localizeWithFallback(resourceNames, keys, defaultValue), ); } String localizeWithFallbackSync( List resourceNames, List keys, String defaultValue, ) { var localization = _configStateService.getLocalization(); return LocalizationUtils.createLocalizerWithFallback(localization!)(resourceNames, keys, defaultValue); } @override Map> getResources() { return _localizations$.value; } Map? getResource(String resourceName) { return _localizations$.value[resourceName]; } Stream?> getResource$(String resourceName) { return _localizations$.map((res) => res[resourceName]); } ApplicationLocalizationResourceDto recursivelyMergeBaseResources(String baseResourceName, Map resource) { var item = resource[baseResourceName]; if (item == null) { return ApplicationLocalizationResourceDto(); } item.texts ??= {}; if (item.baseResources?.isEmpty == true) { return item; } for (var baseResource in item.baseResources!) { var baseItem = recursivelyMergeBaseResources(baseResource, resource); baseItem.texts ??= {}; item.texts!.addAll(baseItem.texts!); } return item; } Map mergeResourcesWithBaseResource(Map resource) { Map entities = {}; for (var key in resource.keys) { var newValue = recursivelyMergeBaseResources(key, resource); entities.putIfAbsent(key, () => newValue); } return entities; } Map>? combineLegacyandNewResources( Map>? legacy, Map resource, ) { Map>? legacyMerged = legacy; var mergedResource = mergeResourcesWithBaseResource(resource); mergedResource.forEach((key, value) { legacyMerged?.putIfAbsent(key, () => value.texts!); }); return legacyMerged; } }