feat: Add i18n using slang

This commit is contained in:
2023-06-21 22:07:12 +02:00
parent 7530fe5b80
commit 13ed7144cb
14 changed files with 493 additions and 79 deletions

View File

@@ -1,3 +1,4 @@
import 'package:anitrack/i18n/strings.g.dart';
import 'package:json_annotation/json_annotation.dart';
/// The type of medium we are tracking. Useful for UI stuff.
@@ -74,25 +75,25 @@ extension MediumStateExtension on MediumTrackingState {
case MediumTrackingState.ongoing:
switch (type) {
case TrackingMediumType.anime:
return 'Watching';
return t.data.ongoing.anime;
case TrackingMediumType.manga:
return 'Reading';
return t.data.ongoing.manga;
}
case MediumTrackingState.completed:
return 'Completed';
return t.data.completed;
case MediumTrackingState.planned:
switch (type) {
case TrackingMediumType.anime:
return 'Plan to watch';
return t.data.planned.anime;
case TrackingMediumType.manga:
return 'Plan to read';
return t.data.planned.manga;
}
case MediumTrackingState.dropped:
return 'Dropped';
return t.data.dropped;
case MediumTrackingState.paused:
return 'Paused';
return t.data.paused;
case MediumTrackingState.all:
return 'All';
return t.data.all;
}
}
}

View File

@@ -1,3 +1,4 @@
import 'package:anitrack/i18n/strings.g.dart';
import 'package:anitrack/licenses.g.dart';
import 'package:anitrack/src/ui/constants.dart';
import 'package:flutter/material.dart';
@@ -19,7 +20,7 @@ class AboutPage extends StatelessWidget {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('About'),
title: Text(t.about.title),
),
body: ListView.builder(
itemCount: ossLicenses.length + 1,
@@ -41,7 +42,7 @@ class AboutPage extends StatelessWidget {
mode: LaunchMode.externalApplication,
);
},
child: const Text('Source'),
child: Text(t.about.source),
),
],
),

View File

@@ -1,3 +1,4 @@
import 'package:anitrack/i18n/strings.g.dart';
import 'package:anitrack/src/data/type.dart';
import 'package:anitrack/src/ui/bloc/anime_list_bloc.dart';
import 'package:anitrack/src/ui/bloc/anime_search_bloc.dart';
@@ -58,9 +59,9 @@ class AnimeListPageState extends State<AnimeListPage> {
String _getPageTitle(TrackingMediumType type) {
switch (type) {
case TrackingMediumType.anime:
return 'Anime';
return t.content.anime;
case TrackingMediumType.manga:
return 'Manga';
return t.content.manga;
}
}
@@ -156,14 +157,14 @@ class AnimeListPageState extends State<AnimeListPage> {
),
ListTile(
leading: const Icon(Icons.settings),
title: const Text('Settings'),
title: Text(t.settings.title),
onTap: () {
Navigator.of(context).pushNamed(settingsRoute);
},
),
ListTile(
leading: const Icon(Icons.info),
title: const Text('About'),
title: Text(t.about.title),
onTap: () {
Navigator.of(context).pushNamed(aboutRoute);
},
@@ -294,7 +295,7 @@ class AnimeListPageState extends State<AnimeListPage> {
AnimeSearchRequestedEvent(state.trackingType),
);
},
tooltip: 'Add new item',
tooltip: t.tooltips.addNewItem,
child: const Icon(Icons.add),
),
);
@@ -314,15 +315,15 @@ class AnimeListPageState extends State<AnimeListPage> {
_controller.jumpToPage(index);
},
items: const [
items: [
BottomBarItem(
icon: Icon(Icons.tv),
title: Text('Anime'),
icon: const Icon(Icons.tv),
title: Text(t.content.anime),
activeColor: Colors.blue,
),
BottomBarItem(
icon: Icon(Icons.auto_stories),
title: Text('Manga'),
icon: const Icon(Icons.auto_stories),
title: Text(t.content.manga),
activeColor: Colors.red,
),
],

View File

@@ -1,3 +1,4 @@
import 'package:anitrack/i18n/strings.g.dart';
import 'package:anitrack/src/data/type.dart';
import 'package:anitrack/src/ui/bloc/anime_search_bloc.dart';
import 'package:anitrack/src/ui/constants.dart';
@@ -25,8 +26,8 @@ class AnimeSearchPage extends StatelessWidget {
appBar: AppBar(
title: Text(
state.trackingType == TrackingMediumType.anime
? 'Anime Search'
: 'Manga Search',
? t.search.anime
: t.search.manga,
),
),
body: Column(
@@ -34,9 +35,9 @@ class AnimeSearchPage extends StatelessWidget {
Padding(
padding: const EdgeInsets.all(8),
child: TextField(
decoration: const InputDecoration(
border: OutlineInputBorder(),
labelText: 'Search query',
decoration: InputDecoration(
border: const OutlineInputBorder(),
labelText: t.search.query,
),
onSubmitted: (_) {
context.read<AnimeSearchBloc>().add(

View File

@@ -1,3 +1,4 @@
import 'package:anitrack/i18n/strings.g.dart';
import 'package:anitrack/src/data/anime.dart';
import 'package:anitrack/src/data/manga.dart';
import 'package:anitrack/src/data/type.dart';
@@ -26,7 +27,7 @@ class DetailsPage extends StatelessWidget {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Details'),
title: Text(t.details.title),
),
body: BlocBuilder<DetailsBloc, DetailsState>(
builder: (context, state) {
@@ -68,10 +69,10 @@ class DetailsPage extends StatelessWidget {
builder: (context) {
return AlertDialog(
title: Text(
'Remove "${state.data!.title}"?',
t.details.removeTitle(title: state.data!.title),
),
content: Text(
'Are you sure you want to remove "${state.data!.title}" from the list?',
t.details.removeBody(title: state.data!.title),
),
actions: [
TextButton(
@@ -82,14 +83,14 @@ class DetailsPage extends StatelessWidget {
style: TextButton.styleFrom(
foregroundColor: Colors.red,
),
child: const Text('Remove'),
child: Text(t.details.removeButton),
),
TextButton(
onPressed: () {
Navigator.of(context)
.pop(false);
},
child: const Text('Cancel'),
child: Text(t.details.cancelButton),
),
],
);
@@ -120,8 +121,8 @@ class DetailsPage extends StatelessWidget {
),
child: DropdownSelector<MediumTrackingState>(
title: state.trackingType == TrackingMediumType.anime
? 'Watch state'
: 'Read state',
? t.details.watchState
: t.details.readState,
onChanged: (MediumTrackingState newState) {
if (state.trackingType ==
TrackingMediumType.anime) {
@@ -182,8 +183,8 @@ class DetailsPage extends StatelessWidget {
child: IntegerInput(
labelText:
state.trackingType == TrackingMediumType.anime
? 'Episodes'
: 'Chapters',
? t.details.episodes
: t.details.chapters,
onChanged: (value) {
switch (state.trackingType) {
case TrackingMediumType.anime:
@@ -221,7 +222,7 @@ class DetailsPage extends StatelessWidget {
vertical: 8,
),
child: IntegerInput(
labelText: 'Volumes owned',
labelText: t.details.volumesOwned,
onChanged: (value) {
final data = state.data! as MangaTrackingData;
context.read<DetailsBloc>().add(

View File

@@ -1,3 +1,4 @@
import 'package:anitrack/i18n/strings.g.dart';
import 'package:anitrack/src/ui/bloc/settings_bloc.dart';
import 'package:anitrack/src/ui/constants.dart';
import 'package:file_picker/file_picker.dart';
@@ -32,15 +33,13 @@ class SettingsPage extends StatelessWidget {
bottom: 0,
child: Scaffold(
appBar: AppBar(
title: const Text('Settings'),
title: Text(t.settings.title),
),
body: ListView(
children: [
ListTile(
title: const Text('Import anime list'),
subtitle: const Text(
'Import anime list exported from MyAnimeList.',
),
title: Text(t.settings.importAnime),
subtitle: Text(t.settings.importAnimeDesc),
onTap: () async {
// Pick the file
final result = await FilePicker.platform.pickFiles();
@@ -49,11 +48,9 @@ class SettingsPage extends StatelessWidget {
if (!result.files.first.path!.endsWith('.xml.gz')) {
await showDialog<void>(
context: context,
builder: (_) => const AlertDialog(
title: Text('Invalid anime list'),
content: Text(
'The selected file is not a MAL anime list. It lacks the ".xml.gz" suffix.',
),
builder: (_) => AlertDialog(
title: Text(t.settings.invalidAnimeListTitle),
content: Text(t.settings.invalidAnimeListBody),
),
);
return;
@@ -68,10 +65,8 @@ class SettingsPage extends StatelessWidget {
},
),
ListTile(
title: const Text('Import manga list'),
subtitle: const Text(
'Import manga list exported from MyAnimeList.',
),
title: Text(t.settings.importManga),
subtitle: Text(t.settings.importMangaDesc),
onTap: () async {
// Pick the file
final result = await FilePicker.platform.pickFiles();
@@ -80,11 +75,9 @@ class SettingsPage extends StatelessWidget {
if (!result.files.first.path!.endsWith('.xml.gz')) {
await showDialog<void>(
context: context,
builder: (_) => const AlertDialog(
title: Text('Invalid manga list'),
content: Text(
'The selected file is not a MAL manga list. It lacks the ".xml.gz" suffix.',
),
builder: (_) => AlertDialog(
title: Text(t.settings.invalidMangaListTitle),
content: Text(t.settings.invalidMangaListBody),
),
);
return;
@@ -137,7 +130,10 @@ class SettingsPage extends StatelessWidget {
child: CircularProgressIndicator(),
),
Text(
'${state.importCurrent} of ${state.importTotal}',
t.settings.importIndicator(
current: state.importCurrent,
total: state.importTotal,
),
style: Theme.of(context).textTheme.bodyMedium,
),
],