Make some hard coded strings translatable

This commit is contained in:
Josu Igoa 2024-07-16 17:02:55 +02:00
parent a2ba46ea45
commit ae9e3d8b7f
31 changed files with 251 additions and 107 deletions

View File

@ -83,7 +83,7 @@ abstract class LanguageLocals {
// ),
"eu": const ISOLanguageName(
name: "Basque",
nativeName: "euskara",
nativeName: "Euskara",
),
// "be": const ISOLanguageName(
// name: "Belarusian",
@ -354,8 +354,8 @@ abstract class LanguageLocals {
// nativeName: "KiKongo",
// ),
"ko": const ISOLanguageName(
name: "Korean",
nativeName: "한국어 (韓國語), 조선말 (朝鮮語)",
name: "Korean",
nativeName: "한국어 (韓國語), 조선말 (朝鮮語)",
),
// "ku": const ISOLanguageName(
// name: "Kurdish",

View File

@ -15,15 +15,12 @@ class SelectDeviceDialog extends HookConsumerWidget {
final remoteService = connectClients.asData!.value.resolvedService!;
return AlertDialog(
title: const Text("Choose the device:"),
title: Text(context.l10n.choose_the_device),
insetPadding: const EdgeInsets.all(16),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
const Text(
"There are multiple device connected.\n"
"Choose the device you want this action to take place",
),
Text(context.l10n.multiple_device_connected),
RadioListTile.adaptive(
title: Text(remoteService.name),
value: true,
@ -33,7 +30,7 @@ class SelectDeviceDialog extends HookConsumerWidget {
},
),
RadioListTile.adaptive(
title: const Text("This Device"),
title: Text(context.l10n.this_device),
value: false,
groupValue: isRemoteService.value,
onChanged: (value) {

View File

@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import 'package:spotube/collections/assets.gen.dart';
import 'package:spotube/extensions/context.dart';
class NotFound extends StatelessWidget {
final bool vertical;
@ -18,9 +19,9 @@ class NotFound extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text("Nothing found", style: theme.textTheme.titleLarge),
Text(context.l10n.nothing_found, style: theme.textTheme.titleLarge),
Text(
"The box is empty",
context.l10n.the_box_is_empty,
style: theme.textTheme.titleMedium,
),
],

View File

@ -3,11 +3,11 @@ import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:gap/gap.dart';
import 'package:skeletonizer/skeletonizer.dart';
import 'package:spotube/collections/spotube_icons.dart';
import 'package:spotube/components/hover_builder.dart';
import 'package:spotube/components/image/universal_image.dart';
import 'package:spotube/extensions/constrains.dart';
import 'package:spotube/extensions/context.dart';
import 'package:spotube/extensions/string.dart';
import 'package:spotube/hooks/utils/use_breakpoint_value.dart';
import 'package:spotube/hooks/utils/use_brightness_value.dart';
@ -128,7 +128,7 @@ class PlaybuttonCard extends HookWidget {
),
if (isHovered)
Text(
"Owned by you",
context.l10n.owned_by_you,
style: theme.textTheme.bodySmall?.copyWith(
color: Colors.white,
),

View File

@ -32,6 +32,9 @@ class TrackViewHeaderActions extends HookConsumerWidget {
final auth = ref.watch(authenticationProvider);
final copiedText =
context.l10n.copied_shareurl_to_clipboard(props.shareUrl);
return Row(
mainAxisSize: MainAxisSize.min,
children: [
@ -48,7 +51,7 @@ class TrackViewHeaderActions extends HookConsumerWidget {
width: 300,
behavior: SnackBarBehavior.floating,
content: Text(
"Copied ${props.shareUrl} to clipboard",
copiedText,
textAlign: TextAlign.center,
),
),

View File

@ -326,5 +326,64 @@
"this_device": "This Device",
"remote": "Remote",
"stats": "Stats",
"and_n_more": "and {count} more"
"and_n_more": "and {count} more",
"recently_played": "Recently Played",
"browse_more": "Browse More",
"no_title": "No Title",
"not_playing": "Not playing",
"epic_failure": "Epic failure!",
"added_num_tracks_to_queue": "Added {tracks_length} tracks to queue",
"spotube_has_an_update": "Spotube has an update",
"download_now": "Download Now",
"nightly_version": "Spotube Nightly {nightlyBuildNum} has been released",
"release_version": "Spotube v{version} has been released",
"read_the_latest": "Read the latest ",
"release_notes": "release notes",
"pick_color_scheme": "Pick color scheme",
"save": "Save",
"choose_the_device": "Choose the device:",
"multiple_device_connected": "There are multiple device connected.\nChoose the device you want this action to take place",
"nothing_found": "Nothing found",
"the_box_is_empty": "The box is empty",
"top_tracks": "Top Tracks",
"top_artists": "Top Artists",
"top_albums": "Top Albums",
"this_week": "This week",
"this_month": "This month",
"last_6_months": "Last 6 months",
"this_year": "This year",
"last_2_years": "Last 2 years",
"all_time": "All time",
"powered_by_provider": "Powered by {providerName}",
"email": "Email",
"profile_followers": "Followers",
"birthday": "Birthday",
"country": "Country",
"subscription": "Subscription",
"not_born": "Not born",
"hacker": "Hacker",
"profile": "Profile",
"no_name": "No Name",
"edit": "Edit",
"user_profile": "User Profile",
"count_plays": "{count} plays",
"streaming_fees_hypothetical": "Streaming fees (hypothetical)",
"minutes_listened": "Minutes listened",
"streamed_songs": "Streamed songs",
"count_streams": "{count} streams",
"owned_by_you": "Owned by you",
"copied_shareurl_to_clipboard": "Copied {shareUrl} to clipboard",
"spotify_hipotetical_calculation": "*This is calculated based on Spotify's per stream\npayout of $0.003 to $0.005. This is a hypothetical\ncalculation to give user insight about how much they\nwould have paid to the artists if they were to listen\ntheir song in Spotify.",
"count_mins": "{minutes} mins",
"summary_minutes": "minutes",
"summary_listened_to_music": "Listened to music",
"summary_songs": "songs",
"summary_streamed_overall": "Streamed overall",
"summary_owed_to_artists": "Owed to artists\nthis month",
"summary_artists": "artist's",
"summary_music_reached_you": "Music reached you",
"summary_full_albums": "full albums",
"summary_got_your_love": "Got your love",
"summary_playlists": "playlists",
"summary_were_on_repeat": "Were on repeat"
}

View File

@ -107,6 +107,9 @@
"always_on_top": "Beti ikusgai",
"exit_mini_player": "Irten mini erreproduzitzailetik",
"download_location": "Deskargen kokapena",
"local_library": "Liburutegi lokala",
"add_library_location": "Gehitu liburutegira",
"remove_library_location": "Kendu liburutegitik",
"account": "Kontua",
"login_with_spotify": "Hasi saioa zure Spotify kontuarekin",
"connect_with_spotify": "Spotify-rekin konektatu",
@ -118,8 +121,8 @@
"market_place_region": "Dendaren herrialdea",
"recommendation_country": "Gomendio herrialdea",
"appearance": "Itxura",
"layout_mode": "Diseinu modua",
"override_layout_settings": "Responsive diseinu moduaren ezarpenak ezeztatu",
"layout_mode": "Diseinua",
"override_layout_settings": "Responsive diseinuaren ezarpenak ezeztatu",
"adaptive": "Moldagarria",
"compact": "Trinkoa",
"extended": "Hedatua",
@ -287,7 +290,7 @@
"genres": "Generoak",
"explore_genres": "Esploratu generoak",
"friends": "Lagunak",
"no_lyrics_available": "Sentitzen dut, ezin dira kanta honen hitzak aurkitu",
"no_lyrics_available": "Sentitzen dugu, ezin dira kanta honen hitzak aurkitu",
"start_a_radio": "Hasi Irrati bat",
"how_to_start_radio": "Nola hasi nahi duzu irratia?",
"replace_queue_question": "Uneko zerrenda ordezkatu nahi duzu edo bertan gehitu?",
@ -295,6 +298,7 @@
"delete_playlist": "Ezabatu zerrenda",
"delete_playlist_confirmation": "Ziur zaude zerrenda ezabatu nahi duzula?",
"local_tracks": "Kanta lokalak",
"local_tab": "Lokalean",
"song_link": "Kantaren lotura",
"skip_this_nonsense": "Utzi txorakeria hau",
"freedom_of_music": "“Musika Askatasuna”",
@ -321,9 +325,65 @@
"connect_client_alert": "{client} gailuak kontrolatzen zaitu",
"this_device": "Gailu hau",
"remote": "Urrunekoa",
"local_library": "Liburutegi lokala",
"add_library_location": "Gehitu liburutegira",
"remove_library_location": "Kendu liburutegitik",
"local_tab": "Tokiko",
"stats": "Estatistikak"
"stats": "Estatistikak",
"and_n_more": "eta {count} gehiago",
"recently_played": "Berriki entzunak",
"browse_more": "Gehiago Bilatu",
"no_title": "Titulurik ez",
"not_playing": "Erreprodukziorik ez",
"epic_failure": "Sekulako errorea!",
"added_num_tracks_to_queue": "{tracks_length} kanta gehitu dira zerrendara",
"spotube_has_an_update": "Spotube-ren eguneraketa bat dago",
"download_now": "Orain deskargatu",
"nightly_version": "Spotube {nightlyBuildNum} Nightly-a argitaratu da",
"release_version": "Spotube v{version} argitaratu da",
"read_the_latest": "Irakurri azken ",
"release_notes": "argitatratze oharrak",
"pick_color_scheme": "Aukeratu kolore eskema",
"save": "Gorde",
"choose_the_device": "Aukeratu gailua:",
"multiple_device_connected": "Hainbat gailu daude konektatuta.\nAukeratu zein gailutan aplikatu nahi duzun ekintza hau",
"nothing_found": "Ezer ez da aurkitu",
"the_box_is_empty": "Kaxa hutsik dago",
"top_tracks": "Top Kantak",
"top_artists": "Top Artistak",
"top_albums": "Top Albumak",
"this_week": "Aste honetan",
"this_month": "Hilabete honetan",
"last_6_months": "Azken 6 hilabeteetan",
"this_year": "Aurten",
"last_2_years": "Azken 2 urtetan",
"all_time": "Betidanik",
"powered_by_provider": "{providerName}-ren eskutik",
"email": "Email",
"profile_followers": "Jarraitzaileak",
"birthday": "Jaiotze-data",
"country": "Herrialdea",
"subscription": "Harpidetzak",
"not_born": "Jaio gabe",
"hacker": "Hacker",
"profile": "Profila",
"no_name": "Izenik Ez",
"edit": "Editatu",
"user_profile": "Erabiltzaile Profila",
"count_plays": "{count} erreprodukzio",
"streaming_fees_hypothetical": "Streaming ordainketa (hipotetikoa)",
"minutes_listened": "Entzundako minutuak",
"streamed_songs": "Stream-eatutako kantak",
"count_streams": "{count} stream",
"owned_by_you": "Zure jabetzakoa",
"copied_shareurl_to_clipboard": "{shareUrl} arbelera kopiatua",
"spotify_hipotetical_calculation": "*Sportify-k stream bakoitzeko duen $0.003 eta $0.005\nordainsarian oinarritua da. Kalkulu hipotetiko bat,\nkanta hauek Spotify-n entzun bazenitu,\nberaiek artistari zenbat ordaiduko lioketen jakin dezazun.",
"count_mins": "{minutes} minutu",
"summary_minutes": "minutu",
"summary_listened_to_music": "Musika entzuten",
"summary_songs": "kanta",
"summary_streamed_overall": "Stream-eatuta oro har",
"summary_owed_to_artists": "Hilabete honetan\nartistei zor zaiena",
"summary_artists": "artisten",
"summary_music_reached_you": "Musika ailegatu zaizu",
"summary_full_albums": "album osok",
"summary_got_your_love": "Izan dute zure maitasuna",
"summary_playlists": "zerrenda",
"summary_were_on_repeat": "Dituzu errepikatze moduan"
}

View File

@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:spotube/collections/spotube_icons.dart';
import 'package:spotube/components/horizontal_playbutton_card_view/horizontal_playbutton_card_view.dart';
import 'package:spotube/extensions/context.dart';
import 'package:spotube/pages/home/feed/feed_section.dart';
import 'package:spotube/provider/spotify/views/home.dart';
import 'package:spotube/utils/service_utils.dart';
@ -33,14 +34,14 @@ class HomePageFeedSection extends HookConsumerWidget {
else if (item.playlist != null)
item.playlist!.asPlaylist
],
title: Text(section.title ?? "No Titel"),
title: Text(section.title ?? context.l10n.no_title),
hasNextPage: false,
isLoadingNextPage: false,
onFetchMore: () {},
titleTrailing: Directionality(
textDirection: TextDirection.rtl,
child: TextButton.icon(
label: const Text("Browse More"),
label: Text(context.l10n.browse_more),
icon: const Icon(SpotubeIcons.angleRight),
onPressed: () => ServiceUtils.pushNamed(
context,

View File

@ -6,6 +6,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:skeletonizer/skeletonizer.dart';
import 'package:spotube/collections/fake.dart';
import 'package:spotube/modules/home/sections/friends/friend_item.dart';
import 'package:spotube/extensions/context.dart';
import 'package:spotube/hooks/utils/use_breakpoint_value.dart';
import 'package:spotube/models/spotify_friends.dart';
import 'package:spotube/provider/authentication/authentication.dart';
@ -73,7 +74,7 @@ class HomePageFriendsSection extends HookConsumerWidget {
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
'Friends',
context.l10n.friends,
style: Theme.of(context).textTheme.titleMedium,
),
),

View File

@ -3,6 +3,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:skeletonizer/skeletonizer.dart';
import 'package:spotube/collections/fake.dart';
import 'package:spotube/components/horizontal_playbutton_card_view/horizontal_playbutton_card_view.dart';
import 'package:spotube/extensions/context.dart';
import 'package:spotube/models/database/database.dart';
import 'package:spotube/provider/history/recent.dart';
@ -22,7 +23,7 @@ class HomeRecentlyPlayedSection extends HookConsumerWidget {
return Skeletonizer(
enabled: history.isLoading,
child: HorizontalPlaybuttonCardView(
title: const Text('Recently Played'),
title: Text(context.l10n.recently_played),
items: [
for (final item in historyData)
if (item.playlist != null)

View File

@ -187,7 +187,7 @@ class _MultiSelectDialog<T> extends HookWidget {
return AlertDialog(
scrollable: true,
title: dialogTitle ?? const Text('Select'),
title: dialogTitle ?? Text(context.l10n.select),
contentPadding: mediaQuery.mdAndUp ? null : const EdgeInsets.all(16),
insetPadding: const EdgeInsets.all(16),
actions: [

View File

@ -233,7 +233,8 @@ class PlayerView extends HookConsumerWidget {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
AutoSizeText(
currentTrack?.name ?? "Not playing",
currentTrack?.name ??
context.l10n.not_playing,
style: TextStyle(
color: titleTextColor,
fontSize: 22,

View File

@ -4,6 +4,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:spotify/spotify.dart';
import 'package:spotube/components/dialogs/select_device_dialog.dart';
import 'package:spotube/components/playbutton_card.dart';
import 'package:spotube/extensions/context.dart';
import 'package:spotube/extensions/image.dart';
import 'package:spotube/models/connect/connect.dart';
import 'package:spotube/pages/playlist/playlist.dart';
@ -133,8 +134,8 @@ class PlaylistCard extends HookConsumerWidget {
historyNotifier.addPlaylists([playlist]);
if (context.mounted) {
final snackbar = SnackBar(
content:
Text("Added ${fetchedInitialTracks.length} tracks to queue"),
content: Text(context.l10n
.added_num_tracks_to_queue(fetchedInitialTracks.length)),
action: SnackBarAction(
label: "Undo",
onPressed: () {

View File

@ -76,7 +76,7 @@ class PlaylistCreateDialog extends HookConsumerWidget {
scaffold.showSnackBar(
SnackBar(
content: Text(
l10n.error(error.message ?? "Epic failure!"),
l10n.error(error.message ?? context.l10n.epic_failure),
style: theme.textTheme.bodyMedium!.copyWith(
color: theme.colorScheme.onError,
),

View File

@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'package:spotube/components/links/anchor_button.dart';
import 'package:url_launcher/url_launcher_string.dart';
import 'package:spotube/extensions/context.dart';
import 'package:version/version.dart';
class RootAppUpdateDialog extends StatelessWidget {
@ -16,10 +17,10 @@ class RootAppUpdateDialog extends StatelessWidget {
const url = "https://spotube.krtirtho.dev/downloads";
const nightlyUrl = "https://spotube.krtirtho.dev/downloads/nightly";
return AlertDialog(
title: const Text("Spotube has an update"),
title: Text(context.l10n.spotube_has_an_update),
actions: [
FilledButton(
child: const Text("Download Now"),
child: Text(context.l10n.download_now),
onPressed: () => launchUrlString(
nightlyBuildNum != null ? nightlyUrl : url,
mode: LaunchMode.externalApplication,
@ -31,16 +32,16 @@ class RootAppUpdateDialog extends StatelessWidget {
children: [
Text(
nightlyBuildNum != null
? "Spotube Nightly $nightlyBuildNum has been released"
: "Spotube v$version has been released",
? context.l10n.nightly_version(nightlyBuildNum!)
: context.l10n.release_version(version!),
),
if (nightlyBuildNum == null)
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text("Read the latest "),
Text(context.l10n.read_the_latest),
AnchorButton(
"release notes",
context.l10n.release_notes,
style: const TextStyle(color: Colors.blue),
onTap: () => launchUrlString(
url,

View File

@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:spotube/extensions/context.dart';
import 'package:spotube/provider/user_preferences/user_preferences_provider.dart';
import 'package:system_theme/system_theme.dart';
@ -69,17 +70,17 @@ class ColorSchemePickerDialog extends HookConsumerWidget {
}
return AlertDialog(
title: const Text("Pick color scheme"),
title: Text(context.l10n.pick_color_scheme),
actions: [
OutlinedButton(
child: const Text("Cancel"),
child: Text(context.l10n.cancel),
onPressed: () {
Navigator.pop(context);
},
),
FilledButton(
onPressed: onOk,
child: const Text("Save"),
child: Text(context.l10n.save),
),
],
content: SizedBox(

View File

@ -5,6 +5,7 @@ import 'package:spotube/collections/fake.dart';
import 'package:spotube/collections/formatters.dart';
import 'package:spotube/modules/stats/summary/summary_card.dart';
import 'package:spotube/extensions/constrains.dart';
import 'package:spotube/extensions/context.dart';
import 'package:spotube/pages/stats/albums/albums.dart';
import 'package:spotube/pages/stats/artists/artists.dart';
import 'package:spotube/pages/stats/fees/fees.dart';
@ -45,8 +46,8 @@ class StatsPageSummarySection extends HookConsumerWidget {
delegate: SliverChildListDelegate([
SummaryCard(
title: summaryData.duration.inMinutes.toDouble(),
unit: "minutes",
description: 'Listened to music',
unit: context.l10n.summary_minutes,
description: context.l10n.summary_listened_to_music,
color: Colors.purple,
onTap: () {
ServiceUtils.pushNamed(context, StatsMinutesPage.name);
@ -54,8 +55,8 @@ class StatsPageSummarySection extends HookConsumerWidget {
),
SummaryCard(
title: summaryData.tracks.toDouble(),
unit: "songs",
description: 'Streamed overall',
unit: context.l10n.summary_songs,
description: context.l10n.summary_streamed_overall,
color: Colors.lightBlue,
onTap: () {
ServiceUtils.pushNamed(context, StatsStreamsPage.name);
@ -64,7 +65,7 @@ class StatsPageSummarySection extends HookConsumerWidget {
SummaryCard.unformatted(
title: usdFormatter.format(summaryData.fees.toDouble()),
unit: "",
description: 'Owed to artists\nthis month',
description: context.l10n.summary_owed_to_artists,
color: Colors.green,
onTap: () {
ServiceUtils.pushNamed(context, StatsStreamFeesPage.name);
@ -72,8 +73,8 @@ class StatsPageSummarySection extends HookConsumerWidget {
),
SummaryCard(
title: summaryData.artists.toDouble(),
unit: "artist's",
description: 'Music reached you',
unit: context.l10n.summary_artists,
description: context.l10n.summary_music_reached_you,
color: Colors.yellow,
onTap: () {
ServiceUtils.pushNamed(context, StatsArtistsPage.name);
@ -81,8 +82,8 @@ class StatsPageSummarySection extends HookConsumerWidget {
),
SummaryCard(
title: summaryData.albums.toDouble(),
unit: "full albums",
description: 'Got your love',
unit: context.l10n.summary_full_albums,
description: context.l10n.summary_got_your_love,
color: Colors.pink,
onTap: () {
ServiceUtils.pushNamed(context, StatsAlbumsPage.name);
@ -90,8 +91,8 @@ class StatsPageSummarySection extends HookConsumerWidget {
),
SummaryCard(
title: summaryData.playlists.toDouble(),
unit: "playlists",
description: 'Were on repeat',
unit: context.l10n.summary_playlists,
description: context.l10n.summary_were_on_repeat,
color: Colors.teal,
onTap: () {
ServiceUtils.pushNamed(context, StatsPlaylistsPage.name);

View File

@ -3,6 +3,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:skeletonizer/skeletonizer.dart';
import 'package:spotube/collections/formatters.dart';
import 'package:spotube/modules/stats/common/album_item.dart';
import 'package:spotube/extensions/context.dart';
import 'package:spotube/provider/history/top.dart';
import 'package:spotube/provider/history/top/albums.dart';
import 'package:spotube/provider/spotify/spotify.dart';
@ -35,7 +36,8 @@ class TopAlbums extends HookConsumerWidget {
return StatsAlbumItem(
album: album.album,
info: Text(
"${compactNumberFormatter.format(album.count)} plays",
context.l10n
.count_plays(compactNumberFormatter.format(album.count)),
),
);
},

View File

@ -4,6 +4,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:skeletonizer/skeletonizer.dart';
import 'package:spotube/collections/formatters.dart';
import 'package:spotube/modules/stats/common/artist_item.dart';
import 'package:spotube/extensions/context.dart';
import 'package:spotube/provider/history/top.dart';
import 'package:spotube/provider/history/top/tracks.dart';
import 'package:spotube/provider/spotify/spotify.dart';
@ -38,7 +39,10 @@ class TopArtists extends HookConsumerWidget {
final artist = artistsData[index];
return StatsArtistItem(
artist: artist.artist,
info: Text("${compactNumberFormatter.format(artist.count)} plays"),
info: Text(
context.l10n
.count_plays(compactNumberFormatter.format(artist.count)),
),
);
},
),

View File

@ -5,6 +5,7 @@ import 'package:spotube/components/themed_button_tab_bar.dart';
import 'package:spotube/modules/stats/top/albums.dart';
import 'package:spotube/modules/stats/top/artists.dart';
import 'package:spotube/modules/stats/top/tracks.dart';
import 'package:spotube/extensions/context.dart';
import 'package:spotube/provider/history/top.dart';
@ -24,23 +25,23 @@ class StatsPageTopSection extends HookConsumerWidget {
floating: true,
flexibleSpace: ThemedButtonsTabBar(
controller: tabController,
tabs: const [
tabs: [
Tab(
child: Padding(
padding: EdgeInsets.all(5),
child: Text("Top Tracks"),
padding: const EdgeInsets.all(5),
child: Text(context.l10n.top_tracks),
),
),
Tab(
child: Padding(
padding: EdgeInsets.all(5),
child: Text("Top Artists"),
padding: const EdgeInsets.all(5),
child: Text(context.l10n.top_artists),
),
),
Tab(
child: Padding(
padding: EdgeInsets.all(5),
child: Text("Top Albums"),
padding: const EdgeInsets.all(5),
child: Text(context.l10n.top_albums),
),
),
],
@ -61,30 +62,30 @@ class StatsPageTopSection extends HookConsumerWidget {
historyDurationNotifier.update((_) => value);
},
icon: const Icon(Icons.arrow_drop_down),
items: const [
items: [
DropdownMenuItem(
value: HistoryDuration.days7,
child: Text("This week"),
child: Text(context.l10n.this_week),
),
DropdownMenuItem(
value: HistoryDuration.days30,
child: Text("This month"),
child: Text(context.l10n.this_month),
),
DropdownMenuItem(
value: HistoryDuration.months6,
child: Text("Last 6 months"),
child: Text(context.l10n.last_6_months),
),
DropdownMenuItem(
value: HistoryDuration.year,
child: Text("This year"),
child: Text(context.l10n.this_year),
),
DropdownMenuItem(
value: HistoryDuration.years2,
child: Text("Last 2 years"),
child: Text(context.l10n.last_2_years),
),
DropdownMenuItem(
value: HistoryDuration.allTime,
child: Text("All time"),
child: Text(context.l10n.all_time),
),
],
),

View File

@ -3,6 +3,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:skeletonizer/skeletonizer.dart';
import 'package:spotube/collections/formatters.dart';
import 'package:spotube/modules/stats/common/track_item.dart';
import 'package:spotube/extensions/context.dart';
import 'package:spotube/provider/history/top.dart';
import 'package:spotube/provider/history/top/tracks.dart';
import 'package:spotube/provider/spotify/spotify.dart';
@ -37,7 +38,8 @@ class TopTracks extends HookConsumerWidget {
return StatsTrackItem(
track: track.track,
info: Text(
"${compactNumberFormatter.format(track.count)} plays",
context.l10n
.count_plays(compactNumberFormatter.format(track.count)),
),
);
},

View File

@ -73,7 +73,7 @@ class LyricsPage extends HookConsumerWidget {
return Align(
alignment: Alignment.bottomRight,
child: Text("Powered by $providerName"),
child: Text(context.l10n.powered_by_provider(providerName)),
);
},
),

View File

@ -103,7 +103,7 @@ class SyncedLyrics extends HookConsumerWidget {
backgroundColor: Colors.transparent,
centerTitle: true,
title: Text(
playlist.activeTrack?.name ?? "Not Playing",
playlist.activeTrack?.name ?? context.l10n.not_playing,
style: headlineTextStyle,
),
bottom: PreferredSize(

View File

@ -9,6 +9,7 @@ import 'package:spotube/collections/spotify_markets.dart';
import 'package:spotube/collections/spotube_icons.dart';
import 'package:spotube/components/image/universal_image.dart';
import 'package:spotube/components/titlebar/titlebar.dart';
import 'package:spotube/extensions/context.dart';
import 'package:spotube/extensions/image.dart';
import 'package:spotube/provider/spotify/spotify.dart';
import 'package:url_launcher/url_launcher_string.dart';
@ -27,21 +28,22 @@ class ProfilePage extends HookConsumerWidget {
final userProperties = useMemoized(
() => {
"Email": meData.email ?? "N/A",
"Followers": meData.followers?.total.toString() ?? "N/A",
"Birthday": meData.birthdate ?? "Not born",
"Country": spotifyMarkets
context.l10n.email: meData.email ?? "N/A",
context.l10n.profile_followers:
meData.followers?.total.toString() ?? "N/A",
context.l10n.birthday: meData.birthdate ?? context.l10n.not_born,
context.l10n.country: spotifyMarkets
.firstWhere((market) => market.$1 == meData.country)
.$2,
"Subscription": meData.product ?? "Hacker",
context.l10n.subscription: meData.product ?? context.l10n.hacker,
},
[meData],
);
return SafeArea(
child: Scaffold(
appBar: const PageWindowTitleBar(
title: Text("Profile"),
appBar: PageWindowTitleBar(
title: Text(context.l10n.profile),
titleSpacing: 0,
automaticallyImplyLeading: true,
centerTitle: false,
@ -72,7 +74,7 @@ class ProfilePage extends HookConsumerWidget {
const SliverGap(10),
SliverToBoxAdapter(
child: Text(
meData.displayName ?? "No Name",
meData.displayName ?? context.l10n.no_name,
style: textTheme.titleLarge,
textAlign: TextAlign.center,
),
@ -85,7 +87,7 @@ class ProfilePage extends HookConsumerWidget {
mainAxisAlignment: MainAxisAlignment.end,
children: [
TextButton.icon(
label: const Text("Edit"),
label: Text(context.l10n.edit),
icon: const Icon(SpotubeIcons.edit),
onPressed: () {
launchUrlString(
@ -118,7 +120,7 @@ class ProfilePage extends HookConsumerWidget {
child: Padding(
padding: const EdgeInsets.all(6),
child: Text(
key,
'$key',
style: textTheme.titleSmall,
),
),

View File

@ -92,7 +92,7 @@ class SettingsAccountSection extends HookConsumerWidget {
if (auth.asData?.value != null)
ListTile(
leading: const Icon(SpotubeIcons.user),
title: const Text("User Profile"),
title: Text(context.l10n.user_profile),
trailing: Padding(
padding: const EdgeInsets.all(8.0),
child: CircleAvatar(

View File

@ -4,6 +4,7 @@ import 'package:skeletonizer/skeletonizer.dart';
import 'package:spotube/collections/formatters.dart';
import 'package:spotube/components/titlebar/titlebar.dart';
import 'package:spotube/modules/stats/common/album_item.dart';
import 'package:spotube/extensions/context.dart';
import 'package:spotube/provider/history/top.dart';
import 'package:spotube/provider/history/top/albums.dart';
@ -24,10 +25,10 @@ class StatsAlbumsPage extends HookConsumerWidget {
final albumsData = topAlbums.asData?.value.items ?? [];
return Scaffold(
appBar: const PageWindowTitleBar(
appBar: PageWindowTitleBar(
automaticallyImplyLeading: true,
centerTitle: false,
title: Text("Albums"),
title: Text(context.l10n.albums),
),
body: Skeletonizer(
enabled: topAlbums.isLoading && !topAlbums.isLoadingNextPage,
@ -43,9 +44,8 @@ class StatsAlbumsPage extends HookConsumerWidget {
final album = albumsData[index];
return StatsAlbumItem(
album: album.album,
info: Text(
"${compactNumberFormatter.format(album.count)} plays",
),
info: Text(context.l10n
.count_plays(compactNumberFormatter.format(album.count))),
);
},
),

View File

@ -5,6 +5,7 @@ import 'package:skeletonizer/skeletonizer.dart';
import 'package:spotube/collections/formatters.dart';
import 'package:spotube/components/titlebar/titlebar.dart';
import 'package:spotube/modules/stats/common/artist_item.dart';
import 'package:spotube/extensions/context.dart';
import 'package:spotube/provider/history/top.dart';
import 'package:spotube/provider/history/top/tracks.dart';
@ -27,10 +28,10 @@ class StatsArtistsPage extends HookConsumerWidget {
() => topTracks.asData?.value.artists ?? [], [topTracks.asData?.value]);
return Scaffold(
appBar: const PageWindowTitleBar(
appBar: PageWindowTitleBar(
automaticallyImplyLeading: true,
centerTitle: false,
title: Text("Artists"),
title: Text(context.l10n.artists),
),
body: Skeletonizer(
enabled: topTracks.isLoading && !topTracks.isLoadingNextPage,
@ -46,8 +47,8 @@ class StatsArtistsPage extends HookConsumerWidget {
final artist = artistsData[index];
return StatsArtistItem(
artist: artist.artist,
info:
Text("${compactNumberFormatter.format(artist.count)} plays"),
info: Text(context.l10n
.count_plays(compactNumberFormatter.format(artist.count))),
);
},
),

View File

@ -6,6 +6,7 @@ import 'package:sliver_tools/sliver_tools.dart';
import 'package:spotube/collections/formatters.dart';
import 'package:spotube/components/titlebar/titlebar.dart';
import 'package:spotube/modules/stats/common/artist_item.dart';
import 'package:spotube/extensions/context.dart';
import 'package:spotube/provider/history/top.dart';
import 'package:spotube/provider/history/top/tracks.dart';
@ -40,10 +41,10 @@ class StatsStreamFeesPage extends HookConsumerWidget {
);
return Scaffold(
appBar: const PageWindowTitleBar(
appBar: PageWindowTitleBar(
automaticallyImplyLeading: true,
centerTitle: false,
title: Text("Streaming fees (hypothetical)"),
title: Text(context.l10n.streaming_fees_hypothetical),
),
body: CustomScrollView(
slivers: [
@ -54,11 +55,7 @@ class StatsStreamFeesPage extends HookConsumerWidget {
padding: const EdgeInsets.all(16.0),
sliver: SliverToBoxAdapter(
child: Text(
"*This is calculated based on Spotify's per stream "
"payout of \$0.003 to \$0.005. This is a hypothetical "
"calculation to give user insight about how much they "
"would have paid to the artists if they were to listen "
"their song in Spotify.",
context.l10n.spotify_hipotetical_calculation,
style: textTheme.bodySmall?.copyWith(
color: hintColor,
),

View File

@ -5,6 +5,7 @@ import 'package:skeletonizer/skeletonizer.dart';
import 'package:spotube/collections/formatters.dart';
import 'package:spotube/components/titlebar/titlebar.dart';
import 'package:spotube/modules/stats/common/track_item.dart';
import 'package:spotube/extensions/context.dart';
import 'package:spotube/provider/history/top.dart';
import 'package:spotube/provider/history/top/tracks.dart';
@ -27,8 +28,8 @@ class StatsMinutesPage extends HookConsumerWidget {
final tracksData = topTracks.asData?.value.items ?? [];
return Scaffold(
appBar: const PageWindowTitleBar(
title: Text("Minutes listened"),
appBar: PageWindowTitleBar(
title: Text(context.l10n.minutes_listened),
centerTitle: false,
automaticallyImplyLeading: true,
),
@ -48,7 +49,8 @@ class StatsMinutesPage extends HookConsumerWidget {
return StatsTrackItem(
track: track.track,
info: Text(
"${compactNumberFormatter.format(track.count)} plays",
context.l10n
.count_plays(compactNumberFormatter.format(track.count)),
),
);
},

View File

@ -4,6 +4,7 @@ import 'package:skeletonizer/skeletonizer.dart';
import 'package:spotube/collections/formatters.dart';
import 'package:spotube/components/titlebar/titlebar.dart';
import 'package:spotube/modules/stats/common/playlist_item.dart';
import 'package:spotube/extensions/context.dart';
import 'package:spotube/provider/history/top.dart';
import 'package:spotube/provider/history/top/playlists.dart';
@ -25,10 +26,10 @@ class StatsPlaylistsPage extends HookConsumerWidget {
final playlistsData = topPlaylists.asData?.value.items ?? [];
return Scaffold(
appBar: const PageWindowTitleBar(
appBar: PageWindowTitleBar(
automaticallyImplyLeading: true,
centerTitle: false,
title: Text("Playlists"),
title: Text(context.l10n.playlists),
),
body: Skeletonizer(
enabled: topPlaylists.isLoading && !topPlaylists.isLoadingNextPage,
@ -45,7 +46,9 @@ class StatsPlaylistsPage extends HookConsumerWidget {
return StatsPlaylistItem(
playlist: playlist.playlist,
info: Text(
"${compactNumberFormatter.format(playlist.count)} plays"),
context.l10n
.count_plays(compactNumberFormatter.format(playlist.count)),
),
);
},
),

View File

@ -5,6 +5,7 @@ import 'package:skeletonizer/skeletonizer.dart';
import 'package:spotube/collections/formatters.dart';
import 'package:spotube/components/titlebar/titlebar.dart';
import 'package:spotube/modules/stats/common/track_item.dart';
import 'package:spotube/extensions/context.dart';
import 'package:spotube/provider/history/top.dart';
import 'package:spotube/provider/history/top/tracks.dart';
@ -27,8 +28,8 @@ class StatsStreamsPage extends HookConsumerWidget {
final tracksData = topTracks.asData?.value.items ?? [];
return Scaffold(
appBar: const PageWindowTitleBar(
title: Text("Streamed songs"),
appBar: PageWindowTitleBar(
title: Text(context.l10n.streamed_songs),
centerTitle: false,
automaticallyImplyLeading: true,
),
@ -48,7 +49,8 @@ class StatsStreamsPage extends HookConsumerWidget {
return StatsTrackItem(
track: track.track,
info: Text(
"${compactNumberFormatter.format(track.count * track.track.duration!.inMinutes)} mins",
context.l10n.count_mins(compactNumberFormatter
.format(track.count * track.track.duration!.inMinutes)),
),
);
},