From f12d81259f9e7005e681a7ca9867291d9228a8b1 Mon Sep 17 00:00:00 2001 From: Kingkor Roy Tirtho Date: Sat, 29 Apr 2023 00:13:37 +0600 Subject: [PATCH] feat: setup localization (l10n) and language switcher, add sidebar and navbar locale --- l10n.yaml | 3 + lib/collections/side_bar_tiles.dart | 25 +++--- lib/collections/spotube_icons.dart | 1 + lib/components/root/sidebar.dart | 6 +- .../root/spotube_navigation_bar.dart | 4 + lib/extensions/context.dart | 6 ++ lib/l10n/app_bn.arb | 8 ++ lib/l10n/app_en.arb | 8 ++ lib/l10n/l10n.dart | 8 ++ lib/main.dart | 12 +++ lib/pages/settings/settings.dart | 86 ++++++++++++------- lib/provider/user_preferences_provider.dart | 15 ++++ pubspec.lock | 9 +- pubspec.yaml | 28 +++--- 14 files changed, 161 insertions(+), 58 deletions(-) create mode 100644 l10n.yaml create mode 100644 lib/extensions/context.dart create mode 100644 lib/l10n/app_bn.arb create mode 100644 lib/l10n/app_en.arb create mode 100644 lib/l10n/l10n.dart diff --git a/l10n.yaml b/l10n.yaml new file mode 100644 index 00000000..4e6692e5 --- /dev/null +++ b/l10n.yaml @@ -0,0 +1,3 @@ +arb-dir: lib/l10n +template-arb-file: app_en.arb +output-localization-file: app_localizations.dart \ No newline at end of file diff --git a/lib/collections/side_bar_tiles.dart b/lib/collections/side_bar_tiles.dart index 9115b0d1..09aa9136 100644 --- a/lib/collections/side_bar_tiles.dart +++ b/lib/collections/side_bar_tiles.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:spotube/collections/spotube_icons.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; class SideBarTiles { final IconData icon; @@ -7,16 +8,16 @@ class SideBarTiles { SideBarTiles({required this.icon, required this.title}); } -List sidebarTileList = [ - SideBarTiles(icon: SpotubeIcons.home, title: "Browse"), - SideBarTiles(icon: SpotubeIcons.search, title: "Search"), - SideBarTiles(icon: SpotubeIcons.library, title: "Library"), - SideBarTiles(icon: SpotubeIcons.music, title: "Lyrics") -]; +List getSidebarTileList(AppLocalizations l10n) => [ + SideBarTiles(icon: SpotubeIcons.home, title: l10n.browse), + SideBarTiles(icon: SpotubeIcons.search, title: l10n.search), + SideBarTiles(icon: SpotubeIcons.library, title: l10n.library), + SideBarTiles(icon: SpotubeIcons.music, title: l10n.lyrics), + ]; -List navbarTileList = [ - SideBarTiles(icon: SpotubeIcons.home, title: "Browse"), - SideBarTiles(icon: SpotubeIcons.search, title: "Search"), - SideBarTiles(icon: SpotubeIcons.library, title: "Library"), - SideBarTiles(icon: SpotubeIcons.settings, title: "Settings") -]; +List getNavbarTileList(AppLocalizations l10n) => [ + SideBarTiles(icon: SpotubeIcons.home, title: l10n.browse), + SideBarTiles(icon: SpotubeIcons.search, title: l10n.search), + SideBarTiles(icon: SpotubeIcons.library, title: l10n.library), + SideBarTiles(icon: SpotubeIcons.settings, title: l10n.settings) + ]; diff --git a/lib/collections/spotube_icons.dart b/lib/collections/spotube_icons.dart index 9676237e..55e09f91 100644 --- a/lib/collections/spotube_icons.dart +++ b/lib/collections/spotube_icons.dart @@ -77,4 +77,5 @@ abstract class SpotubeIcons { static const dragHandle = Icons.drag_indicator; static const lightning = Icons.flash_on_rounded; static const colorSync = FeatherIcons.activity; + static const language = FeatherIcons.globe; } diff --git a/lib/components/root/sidebar.dart b/lib/components/root/sidebar.dart index da9afc7f..c6aa7bb2 100644 --- a/lib/components/root/sidebar.dart +++ b/lib/components/root/sidebar.dart @@ -9,6 +9,7 @@ import 'package:spotube/collections/assets.gen.dart'; import 'package:spotube/collections/side_bar_tiles.dart'; import 'package:spotube/collections/spotube_icons.dart'; import 'package:spotube/components/shared/image/universal_image.dart'; +import 'package:spotube/extensions/context.dart'; import 'package:spotube/hooks/use_breakpoints.dart'; import 'package:spotube/hooks/use_brightness_value.dart'; import 'package:spotube/hooks/use_sidebarx_controller.dart'; @@ -70,6 +71,9 @@ class Sidebar extends HookConsumerWidget { Color.lerp(bg, Colors.black, 0.45)!, ); + final sidebarTileList = + useMemoized(() => getSidebarTileList(context.l10n), [context.l10n]); + useEffect(() { controller.addListener(() { onSelectedIndexChanged(controller.selectedIndex); @@ -263,7 +267,7 @@ class SidebarFooter extends HookConsumerWidget { const SizedBox(width: 10), Flexible( child: Text( - data.displayName ?? "Guest", + data.displayName ?? context.l10n.guest, maxLines: 1, softWrap: false, overflow: TextOverflow.fade, diff --git a/lib/components/root/spotube_navigation_bar.dart b/lib/components/root/spotube_navigation_bar.dart index c58c2d5d..f7d6d64d 100644 --- a/lib/components/root/spotube_navigation_bar.dart +++ b/lib/components/root/spotube_navigation_bar.dart @@ -7,6 +7,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:spotube/collections/side_bar_tiles.dart'; import 'package:spotube/components/root/sidebar.dart'; +import 'package:spotube/extensions/context.dart'; import 'package:spotube/hooks/use_breakpoints.dart'; import 'package:spotube/hooks/use_brightness_value.dart'; import 'package:spotube/provider/downloader_provider.dart'; @@ -39,6 +40,9 @@ class SpotubeNavigationBar extends HookConsumerWidget { theme.colorScheme.primary.withOpacity(0.2), ); + final navbarTileList = + useMemoized(() => getNavbarTileList(context.l10n), [context.l10n]); + useEffect(() { insideSelectedIndex.value = selectedIndex; return null; diff --git a/lib/extensions/context.dart b/lib/extensions/context.dart new file mode 100644 index 00000000..9ca1e237 --- /dev/null +++ b/lib/extensions/context.dart @@ -0,0 +1,6 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; + +extension AppLocale on BuildContext { + AppLocalizations get l10n => AppLocalizations.of(this)!; +} diff --git a/lib/l10n/app_bn.arb b/lib/l10n/app_bn.arb new file mode 100644 index 00000000..c5550afe --- /dev/null +++ b/lib/l10n/app_bn.arb @@ -0,0 +1,8 @@ +{ + "guest": "অতিথি", + "browse": "ব্রাউজ করুন", + "search": "অনুসন্ধান করুন", + "library": "লাইব্রেরী", + "lyrics": "গানের কথা", + "settings": "সেটিংস" +} \ No newline at end of file diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb new file mode 100644 index 00000000..4b561135 --- /dev/null +++ b/lib/l10n/app_en.arb @@ -0,0 +1,8 @@ +{ + "guest": "Guest", + "browse": "Browse", + "search": "Search", + "library": "Library", + "lyrics": "Lyrics", + "settings": "Settings" +} \ No newline at end of file diff --git a/lib/l10n/l10n.dart b/lib/l10n/l10n.dart new file mode 100644 index 00000000..889bcac5 --- /dev/null +++ b/lib/l10n/l10n.dart @@ -0,0 +1,8 @@ +import 'package:flutter/material.dart'; + +class L10n { + static final all = [ + const Locale('en'), + const Locale('bn', 'BD'), + ]; +} diff --git a/lib/main.dart b/lib/main.dart index 7bc7c71d..9f7a9e05 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -8,6 +8,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_desktop_tools/flutter_desktop_tools.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:hive_flutter/hive_flutter.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:metadata_god/metadata_god.dart'; @@ -17,6 +18,7 @@ import 'package:spotube/components/shared/dialogs/replace_downloaded_dialog.dart import 'package:spotube/entities/cache_track.dart'; import 'package:spotube/collections/routes.dart'; import 'package:spotube/collections/intents.dart'; +import 'package:spotube/l10n/l10n.dart'; import 'package:spotube/models/logger.dart'; import 'package:spotube/provider/downloader_provider.dart'; import 'package:spotube/provider/palette_provider.dart'; @@ -29,6 +31,7 @@ import 'package:spotube/utils/persisted_state_notifier.dart'; import 'package:system_theme/system_theme.dart'; import 'package:path_provider/path_provider.dart'; import 'package:spotube/hooks/use_init_sys_tray.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; Future main(List rawArgs) async { final parser = ArgParser(); @@ -189,6 +192,7 @@ class SpotubeState extends ConsumerState { ref.watch(userPreferencesProvider.select((s) => s.themeMode)); final accentMaterialColor = ref.watch(userPreferencesProvider.select((s) => s.accentColorScheme)); + final locale = ref.watch(userPreferencesProvider.select((s) => s.locale)); final paletteColor = ref.watch(paletteProvider.select((s) => s?.dominantColor?.color)); @@ -203,6 +207,14 @@ class SpotubeState extends ConsumerState { }, []); return MaterialApp.router( + supportedLocales: L10n.all, + locale: locale.languageCode == "system" ? null : locale, + localizationsDelegates: const [ + AppLocalizations.delegate, + GlobalMaterialLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, + ], routeInformationParser: router.routeInformationParser, routerDelegate: router.routerDelegate, routeInformationProvider: router.routeInformationProvider, diff --git a/lib/pages/settings/settings.dart b/lib/pages/settings/settings.dart index 32f118e0..c32467dd 100644 --- a/lib/pages/settings/settings.dart +++ b/lib/pages/settings/settings.dart @@ -12,6 +12,7 @@ import 'package:spotube/components/settings/color_scheme_picker_dialog.dart'; import 'package:spotube/components/shared/adaptive/adaptive_list_tile.dart'; import 'package:spotube/components/shared/page_window_title_bar.dart'; import 'package:spotube/collections/spotify_markets.dart'; +import 'package:spotube/l10n/l10n.dart'; import 'package:spotube/provider/authentication_provider.dart'; import 'package:spotube/provider/downloader_provider.dart'; import 'package:spotube/provider/user_preferences_provider.dart'; @@ -127,6 +128,60 @@ class SettingsPage extends HookConsumerWidget { ), ); }), + Text( + " Language & Region", + style: theme.textTheme.headlineSmall + ?.copyWith(fontWeight: FontWeight.bold), + ), + ListTile( + leading: const Icon(SpotubeIcons.language), + title: const Text("Language"), + trailing: DropdownButton( + value: preferences.locale, + items: [ + const DropdownMenuItem( + value: Locale("system"), + child: Text("System Default"), + ), + for (final locale in L10n.all) + DropdownMenuItem( + value: locale, + child: Text(locale.languageCode), + ), + ], + onChanged: (value) { + if (value != null) { + preferences.setLocale(value); + } + }, + ), + ), + AdaptiveListTile( + leading: const Icon(SpotubeIcons.shoppingBag), + title: const Text("Market Place Region"), + subtitle: const Text("Recommendation Country"), + trailing: (context, update) => ConstrainedBox( + constraints: const BoxConstraints(maxWidth: 350), + child: DropdownMenu( + initialSelection: preferences.recommendationMarket, + dropdownMenuEntries: spotifyMarkets + .map( + (country) => DropdownMenuEntry( + value: country.first, + label: country.last, + ), + ) + .toList(), + onSelected: (value) { + if (value == null) return; + preferences.setRecommendationMarket( + value as String, + ); + update?.call(() {}); + }, + ), + ), + ), Text( " Appearance", style: theme.textTheme.headlineSmall @@ -273,37 +328,6 @@ class SettingsPage extends HookConsumerWidget { }, trailing: const Icon(SpotubeIcons.angleRight), ), - Text( - " Search", - style: theme.textTheme.headlineSmall - ?.copyWith(fontWeight: FontWeight.bold), - ), - AdaptiveListTile( - leading: const Icon(SpotubeIcons.shoppingBag), - title: const Text("Market Place"), - subtitle: const Text("Recommendation Country"), - trailing: (context, update) => ConstrainedBox( - constraints: const BoxConstraints(maxWidth: 350), - child: DropdownMenu( - initialSelection: preferences.recommendationMarket, - dropdownMenuEntries: spotifyMarkets - .map( - (country) => DropdownMenuEntry( - value: country.first, - label: country.last, - ), - ) - .toList(), - onSelected: (value) { - if (value == null) return; - preferences.setRecommendationMarket( - value as String, - ); - update?.call(() {}); - }, - ), - ), - ), Text( " Downloads", style: theme.textTheme.headlineSmall diff --git a/lib/provider/user_preferences_provider.dart b/lib/provider/user_preferences_provider.dart index 6d26aaf1..701b1efd 100644 --- a/lib/provider/user_preferences_provider.dart +++ b/lib/provider/user_preferences_provider.dart @@ -48,6 +48,8 @@ class UserPreferences extends PersistedChangeNotifier { bool showSystemTrayIcon; + Locale locale; + final Ref ref; UserPreferences( @@ -65,6 +67,7 @@ class UserPreferences extends PersistedChangeNotifier { this.downloadLocation = "", this.closeBehavior = CloseBehavior.minimizeToTray, this.showSystemTrayIcon = true, + this.locale = const Locale("system"), }) : super() { if (downloadLocation.isEmpty) { _getDefaultDownloadDirectory().then( @@ -159,6 +162,12 @@ class UserPreferences extends PersistedChangeNotifier { updatePersistence(); } + void setLocale(Locale locale) { + this.locale = locale; + notifyListeners(); + updatePersistence(); + } + Future _getDefaultDownloadDirectory() async { if (kIsAndroid) return "/storage/emulated/0/Download/Spotube"; @@ -201,6 +210,11 @@ class UserPreferences extends PersistedChangeNotifier { : closeBehavior; showSystemTrayIcon = map["showSystemTrayIcon"] ?? showSystemTrayIcon; + + locale = Locale( + map["locale"]?["lc"] ?? locale.languageCode, + map["locale"]?["cc"] ?? locale.countryCode, + ); } @override @@ -219,6 +233,7 @@ class UserPreferences extends PersistedChangeNotifier { "predownload": predownload, "closeBehavior": closeBehavior.index, "showSystemTrayIcon": showSystemTrayIcon, + "locale": {"lc": locale.languageCode, "cc": locale.countryCode}, }; } } diff --git a/pubspec.lock b/pubspec.lock index f9d915b1..597f5017 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -467,7 +467,7 @@ packages: source: hosted version: "1.1.0" dbus: - dependency: "direct main" + dependency: transitive description: name: dbus sha256: "6f07cba3f7b3448d42d015bfd3d53fe12e5b36da2423f23838efc1d5fb31a263" @@ -701,6 +701,11 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.1" + flutter_localizations: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" flutter_mailer: dependency: transitive description: @@ -946,7 +951,7 @@ packages: source: sdk version: "0.0.0" intl: - dependency: transitive + dependency: "direct main" description: name: intl sha256: "910f85bce16fb5c6f614e117efa303e85a1731bb0081edf3604a2ae6e9a3cc91" diff --git a/pubspec.yaml b/pubspec.yaml index 1ca40885..8db89e59 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -32,9 +32,15 @@ dependencies: flutter: sdk: flutter flutter_cache_manager: ^3.3.0 + flutter_desktop_tools: + git: + url: https://github.com/KRTirtho/flutter_desktop_tools.git + ref: 1f0bec3283626dcbd8ee2f54e238d096d8dea50e flutter_feather_icons: ^2.0.0+1 flutter_hooks: ^0.18.2+1 flutter_inappwebview: ^5.7.2+3 + flutter_localizations: + sdk: flutter flutter_riverpod: ^2.1.1 flutter_secure_storage: ^8.0.0 flutter_svg: ^1.1.6 @@ -45,12 +51,16 @@ dependencies: hooks_riverpod: ^2.1.1 html: ^0.15.1 http: ^0.13.5 + intl: ^0.17.0 introduction_screen: ^3.0.2 json_annotation: ^4.8.0 json_serializable: ^6.6.0 logger: ^1.1.0 metadata_god: ^0.4.1 mime: ^1.0.2 + mpris_service: + git: + url: https://github.com/alexmercerind/mpris_service package_info_plus: ^3.0.2 palette_generator: ^0.3.3 path: ^1.8.0 @@ -64,6 +74,11 @@ dependencies: sidebarx: ^0.15.0 simple_circular_progress_bar: ^1.0.2 skeleton_text: ^3.0.0 + smtc_windows: + git: + url: https://github.com/KRTirtho/smtc_windows.git + ref: 6cc93624b8fab8d7727c8693e91577a7413ccd13 + path: packages/smtc_windows spotify: ^0.11.0 system_theme: ^2.1.0 titlebar_buttons: ^1.0.0 @@ -79,18 +94,6 @@ dependencies: ref: a738913c8ce2c9f47515382d40827e794a334274 path: plugins/window_size youtube_explode_dart: ^1.12.1 - flutter_desktop_tools: - git: - url: https://github.com/KRTirtho/flutter_desktop_tools.git - ref: 1f0bec3283626dcbd8ee2f54e238d096d8dea50e - smtc_windows: - git: - url: https://github.com/KRTirtho/smtc_windows.git - ref: 6cc93624b8fab8d7727c8693e91577a7413ccd13 - path: packages/smtc_windows - mpris_service: - git: - url: https://github.com/alexmercerind/mpris_service dev_dependencies: build_runner: ^2.3.2 @@ -111,6 +114,7 @@ dependency_overrides: package_info_plus: ^3.0.2 flutter: + generate: true uses-material-design: true assets: - assets/