diff --git a/assets/text/Cambios.md b/assets/text/Cambios.md index ad178ff..b996ff3 100644 --- a/assets/text/Cambios.md +++ b/assets/text/Cambios.md @@ -1,3 +1,7 @@ +## Version 2.1.10 +- Ahora se puede cambiar el idioma en las opciones (Inglés, Francés o Español) +- Nueva funcionalidad para agregar amiibos en caja o abiertos y cuantos (experimental) + ## Version 2.1.61 - Zelda edición Tears of the Kingdom agregado - Ganondorf posando suavemente edición Tears of the Kingdom agregado diff --git a/assets/text/Changelog.md b/assets/text/Changelog.md index 8ca7377..9fd0d5e 100644 --- a/assets/text/Changelog.md +++ b/assets/text/Changelog.md @@ -1,3 +1,7 @@ +## Version 2.1.10 +- Now you can change the language of the app from settings (English, French or Spanish) +- New feature to add owned amiibos as boxed / unboxed (experimental) + ## Version 2.1.61 - Zelda y Ganondorf (mid-suavemente) from Tears of the Kingdom added diff --git a/assets/text/Journal_des_Modifications.md b/assets/text/Journal_des_Modifications.md index edfbc36..d1ae374 100644 --- a/assets/text/Journal_des_Modifications.md +++ b/assets/text/Journal_des_Modifications.md @@ -1,3 +1,7 @@ +## Version 2.1.10 +- Vous pouvez désormais changer la langue de l'application depuis les paramètres (Anglais, Français ou Espagnol) +- Nouvelle fonctionnalité pour ajouter des amiibos possédés en boîte/sans boîte (expérimental) + ## Version 2.1.61 - Ajout des figurines Zelda et Ganondorf (au milieu suavemente) de la série Tears of the Kingdom diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index 8c3f43b..5de1860 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -85,6 +85,10 @@ "actionText": "Share", "donate": "Donate", "lockTooltip": "{choice, select, true {Locked} false {Unlocked} other {Unknown}}", + "language": "Language", + "languageSubtitle": "Choose a language or use it from the system", + "system": "System", + "localization": "{choice, select, en {English} es {Spanish} fr {French} other {Unknown}}", "statTooltip": "{choice, select, true {Percentage} false {Fraction} other {Unknown}}", "switch_platform": "Switch", "wiiu_platform": "WiiU", diff --git a/lib/l10n/intl_es.arb b/lib/l10n/intl_es.arb index abee654..a029096 100644 --- a/lib/l10n/intl_es.arb +++ b/lib/l10n/intl_es.arb @@ -85,6 +85,10 @@ "actionText": "Compartir", "donate": "Donaciones", "lockTooltip": "{choice, select, true {Bloqueado} false {Desbloqueado} other {Desconocido}}", + "language": "Idioma", + "languageSubtitle": "Escoja un idioma o use el del sistema", + "system": "Sistema", + "localization": "{choice, select, en {Inglés} es {Español} fr {Francés} other {Desconocido}}", "statTooltip": "{choice, select, true {Porcentaje} false {Fracción} other {Unknown}}", "switch_platform": "Switch", "wiiu_platform": "WiiU", diff --git a/lib/l10n/intl_fr.arb b/lib/l10n/intl_fr.arb index 7c7ce07..fdbde56 100644 --- a/lib/l10n/intl_fr.arb +++ b/lib/l10n/intl_fr.arb @@ -85,6 +85,10 @@ "actionText": "Partager", "donate": "Faire un don", "lockTooltip": "{choice, select, true {Fermé à clé} false {Déverrouillé} other {inconnue}}", + "language": "Langue", + "languageSubtitle": "Choisissez une langue ou utilisez-la depuis le système", + "system": "Système", + "localization": "{choice, select, en {Anglais} es {Espagnol} fr {Français} other {Inconnu}}", "statTooltip": "{choice, select, true {Pourcentage} false {Fraction} other {inconnue}}", "switch_platform": "Switch", "wiiu_platform": "WiiU", diff --git a/lib/main.dart b/lib/main.dart index 9beed68..12467d0 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -3,6 +3,7 @@ import 'dart:async'; import 'package:amiibo_network/data/remote_config/model/default_remote_config.dart'; import 'package:amiibo_network/firebase_options.dart'; import 'package:amiibo_network/riverpod/game_provider.dart'; +import 'package:amiibo_network/riverpod/preferences_provider.dart'; import 'package:amiibo_network/riverpod/provider_observer.dart'; import 'package:amiibo_network/riverpod/router_provider.dart'; import 'package:amiibo_network/service/info_package.dart'; @@ -63,9 +64,8 @@ Future main() async { await remoteConfig.ensureInitialized(); await remoteConfig.setConfigSettings(RemoteConfigSettings( fetchTimeout: const Duration(minutes: 1), - minimumFetchInterval: kDebugMode - ? const Duration(minutes: 5) - : const Duration(days: 1), + minimumFetchInterval: + kDebugMode ? const Duration(minutes: 5) : const Duration(days: 1), )); await remoteConfig.setDefaults(const DefaultRemoteConfig().toJson()); if (remoteConfig.lastFetchStatus == RemoteConfigFetchStatus.success) { @@ -135,6 +135,7 @@ class AmiiboNetwork extends ConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { final ThemeProvider themeMode = ref.watch(themeProvider); final router = ref.watch(routerProvider); + final Locale? locale = ref.watch(localeProvider); return MaterialApp.router( localizationsDelegates: [ S.delegate, @@ -144,6 +145,7 @@ class AmiiboNetwork extends ConsumerWidget { ], supportedLocales: S.delegate.supportedLocales, debugShowCheckedModeBanner: false, + locale: locale, theme: themeMode.light, darkTheme: themeMode.dark, themeMode: themeMode.preferredTheme, diff --git a/lib/model/preferences.dart b/lib/model/preferences.dart index 1e00a77..b48a1a0 100644 --- a/lib/model/preferences.dart +++ b/lib/model/preferences.dart @@ -9,6 +9,7 @@ class Preferences with _$Preferences { required bool usePercentage, required bool useGrid, required bool ownTypes, + String? languageCode, HiddenType? ignored, }) = _Preferences; } diff --git a/lib/model/result.dart b/lib/model/result.dart new file mode 100644 index 0000000..ecb7e54 --- /dev/null +++ b/lib/model/result.dart @@ -0,0 +1,9 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; + +part 'result.freezed.dart'; + +@freezed +class Result with _$Result { + + const factory Result(T data) = _Result; +} diff --git a/lib/repository/theme_repository.dart b/lib/repository/theme_repository.dart index 75d2d3d..27b2cd1 100644 --- a/lib/repository/theme_repository.dart +++ b/lib/repository/theme_repository.dart @@ -1615,7 +1615,7 @@ class AmiiboTheme3 implements AmiiboTheme { _textTheme.bodyLarge, ), overlayColor: WidgetStateProperty.all(overlay), - visualDensity: const VisualDensity(vertical: 2.5), + visualDensity: const VisualDensity(vertical: 0), ), ), diff --git a/lib/riverpod/preferences_provider.dart b/lib/riverpod/preferences_provider.dart index 479c057..24945b1 100644 --- a/lib/riverpod/preferences_provider.dart +++ b/lib/riverpod/preferences_provider.dart @@ -1,3 +1,5 @@ +import 'dart:ui'; + import 'package:amiibo_network/enum/hidden_types.dart'; import 'package:amiibo_network/model/preferences.dart'; import 'package:amiibo_network/riverpod/repository_provider.dart'; @@ -14,11 +16,24 @@ final hiddenCategoryProvider = Provider( final ownTypesCategoryProvider = Provider( (ref) { final ownedCategories = ref.watch(remoteOwnedCategoryProvider); - return ownedCategories && ref.watch(personalProvider.select((value) => value.ownTypes)); + return ownedCategories && + ref.watch(personalProvider.select((value) => value.ownTypes)); }, name: 'OwnerCategoriesFlagProvider', ); +final localeProvider = Provider( + (ref) { + final languageCode = + ref.watch(personalProvider.select((value) => value.languageCode)); + if (languageCode == null || languageCode.isEmpty) { + return null; + } + return Locale.fromSubtags(languageCode: languageCode); + }, + name: 'LocaleProvider', +); + final personalProvider = StateNotifierProvider( (ref) { @@ -26,6 +41,7 @@ final personalProvider = final percent = sharedProvider.getBool(sharedStatMode) ?? false; final grid = sharedProvider.getBool(sharedGridMode) ?? true; final ignored = sharedProvider.getInt(sharedIgnored) ?? 0; + final languageCode = sharedProvider.getString(sharedLanguageCode); final ownType = sharedProvider.getBool(sharedOwnType) ?? false; final HiddenType? categoryIgnored; switch (ignored) { @@ -45,6 +61,7 @@ final personalProvider = useGrid: grid, ownTypes: ownType, ignored: categoryIgnored, + languageCode: languageCode, ); return UserPreferencessNotifier(initial, ref); }, @@ -58,6 +75,18 @@ class UserPreferencessNotifier extends StateNotifier { bool get isPercentage => state.usePercentage; + Future forceLocale(String? newLanguageCode) async { + if (newLanguageCode != state.languageCode) { + final SharedPreferences preferences = ref.read(preferencesProvider); + if (newLanguageCode == null) { + await preferences.remove(sharedLanguageCode); + } else { + await preferences.setString(sharedLanguageCode, newLanguageCode); + } + state = state.copyWith(languageCode: newLanguageCode); + } + } + Future toggleOwnType(bool newValue) async { if (newValue != state.ownTypes) { final SharedPreferences preferences = ref.read(preferencesProvider); diff --git a/lib/screen/settings_screen.dart b/lib/screen/settings_screen.dart index 2bc825c..1ae8e76 100644 --- a/lib/screen/settings_screen.dart +++ b/lib/screen/settings_screen.dart @@ -1,4 +1,5 @@ import 'package:amiibo_network/enum/hidden_types.dart'; +import 'package:amiibo_network/model/result.dart'; import 'package:amiibo_network/resources/resources.dart'; import 'package:amiibo_network/riverpod/preferences_provider.dart'; import 'package:amiibo_network/riverpod/query_provider.dart'; @@ -7,6 +8,7 @@ import 'package:amiibo_network/riverpod/service_provider.dart'; import 'package:amiibo_network/riverpod/theme_provider.dart'; import 'package:amiibo_network/enum/amiibo_category_enum.dart'; import 'package:amiibo_network/utils/format_color_on_theme.dart'; +import 'package:amiibo_network/widget/locale_selection_dialog.dart'; import 'package:firebase_crashlytics/firebase_crashlytics.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; @@ -63,6 +65,23 @@ class SettingsPage extends ConsumerWidget { icon: const Icon(Icons.color_lens), onTap: () => ThemeButton.dialog(context), ), + _CardSettings( + title: translate.language, + subtitle: translate.languageSubtitle, + icon: const Icon(Icons.language), + onTap: () async { + final localeResult = await showDialog>( + context: context, + builder: (context) => const LocaleDialog(), + ); + if (localeResult == null) { + return; + } + ref + .read(personalProvider.notifier) + .forceLocale(localeResult.data); + }, + ), _CardSettings( title: translate.changelog, subtitle: translate.changelogSubtitle, diff --git a/lib/utils/preferences_constants.dart b/lib/utils/preferences_constants.dart index 14c82f6..59d9098 100644 --- a/lib/utils/preferences_constants.dart +++ b/lib/utils/preferences_constants.dart @@ -34,5 +34,7 @@ const String sharedGridMode = 'gridMode'; const String sharedIgnored = 'ignoredType'; /// if owned system should be enable (shows boxed/unboxed owned amiibos or just owned as a whole) const String sharedOwnType = 'ownType'; +/// locale index from S.delegate.supportedLocales or null +const String sharedLanguageCode = 'localeIndex'; /// Version of the App -const int versionApp = 58; +const int versionApp = 85; diff --git a/lib/widget/locale_selection_dialog.dart b/lib/widget/locale_selection_dialog.dart new file mode 100644 index 0000000..c4a2ed4 --- /dev/null +++ b/lib/widget/locale_selection_dialog.dart @@ -0,0 +1,52 @@ +import 'package:amiibo_network/generated/l10n.dart'; +import 'package:amiibo_network/model/result.dart'; +import 'package:amiibo_network/riverpod/preferences_provider.dart'; +import 'package:amiibo_network/utils/string_extensions.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; + +class LocaleDialog extends HookConsumerWidget { + const LocaleDialog({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final localizations = MaterialLocalizations.of(context); + final S translate = S.of(context); + final selectedLocale = ref.watch(localeProvider); + final ValueNotifier state = useState(selectedLocale?.languageCode); + return AlertDialog( + title: Text(translate.language), + scrollable: true, + alignment: Alignment.center, + content: ListBody( + children: [ + RadioListTile( + value: null, + groupValue: state.value, + title: Text(translate.system), + onChanged: (value) => state.value = value, + ), + for (final locale in S.delegate.supportedLocales) + RadioListTile( + value: locale.languageCode, + groupValue: state.value, + title: Text(translate.localization(locale.languageCode)), + onChanged: (value) => state.value = value, + ), + ], + ), + actions: [ + TextButton( + child: Text(localizations.cancelButtonLabel.capitalize()), + onPressed: Navigator.of(context).pop, + ), + ElevatedButton( + child: Text(localizations.saveButtonLabel.capitalize()), + onPressed: () async => + Navigator.of(context).pop(Result(state.value)), + ), + ], + ); + } +} diff --git a/pubspec.yaml b/pubspec.yaml index 2f44ea8..3ecc48d 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -11,7 +11,7 @@ description: An Amiibo collection app designed to allow you to keep track of whi # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 2.1.91+84 +version: 2.1.91+85 environment: sdk: '>=3.4.0 <4.0.0'