Lokalisierung implementieren #112
1
.gitignore
vendored
@@ -195,3 +195,4 @@ app.*.map.json
|
||||
/android/app/debug
|
||||
/android/app/profile
|
||||
/android/app/release
|
||||
/devtools_options.yaml
|
||||
|
||||
5
l10n.yaml
Normal file
@@ -0,0 +1,5 @@
|
||||
arb-dir: lib/l10n/arb
|
||||
template-arb-file: app_en.arb
|
||||
output-localization-file: app_localizations.dart
|
||||
output-dir: lib/l10n/generated
|
||||
nullable-getter: false
|
||||
@@ -1,3 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:game_tracker/l10n/generated/app_localizations.dart';
|
||||
|
||||
/// Button types used for styling the [CustomWidthButton]
|
||||
enum ButtonType { primary, secondary, tertiary }
|
||||
|
||||
@@ -30,16 +33,17 @@ enum ExportResult { success, canceled, unknownException }
|
||||
/// - [Ruleset.leastPoints]: The player with the fewest points wins.
|
||||
enum Ruleset { singleWinner, singleLoser, mostPoints, leastPoints }
|
||||
|
||||
/// Translates a [Ruleset] enum value to its corresponding string representation.
|
||||
String translateRulesetToString(Ruleset ruleset) {
|
||||
/// Translates a [Ruleset] enum value to its corresponding localized string.
|
||||
String translateRulesetToString(Ruleset ruleset, BuildContext context) {
|
||||
final loc = AppLocalizations.of(context);
|
||||
switch (ruleset) {
|
||||
case Ruleset.singleWinner:
|
||||
return 'Single Winner';
|
||||
return loc.single_winner;
|
||||
case Ruleset.singleLoser:
|
||||
return 'Single Loser';
|
||||
return loc.single_loser;
|
||||
case Ruleset.mostPoints:
|
||||
return 'Most Points';
|
||||
return loc.most_points;
|
||||
case Ruleset.leastPoints:
|
||||
return 'Least Points';
|
||||
return loc.least_points;
|
||||
}
|
||||
}
|
||||
|
||||
83
lib/l10n/arb/app_de.arb
Normal file
@@ -0,0 +1,83 @@
|
||||
{
|
||||
|
sneeex marked this conversation as resolved
|
||||
"@@locale": "de",
|
||||
"all_players": "Alle Spieler:innen:",
|
||||
"all_players_selected": "Alle Spieler:innen ausgewählt",
|
||||
"amount_of_matches": "Anzahl der Matches",
|
||||
"cancel": "Abbrechen",
|
||||
"choose_game": "Spielvorlage wählen",
|
||||
"choose_group": "Gruppe wählen",
|
||||
"choose_ruleset": "Regelwerk wählen",
|
||||
"could_not_add_player": "Spieler:in {playerName} konnte nicht hinzugefügt werden",
|
||||
"create_group": "Gruppe erstellen",
|
||||
"create_match": "Match erstellen",
|
||||
"create_new_group": "Neue Gruppe erstellen",
|
||||
"create_new_match": "Neues Match erstellen",
|
||||
"data_successfully_deleted": "Daten erfolgreich gelöscht",
|
||||
"data_successfully_exported": "Daten erfolgreich exportiert",
|
||||
"data_successfully_imported": "Daten erfolgreich importiert",
|
||||
"days_ago": "vor {count} Tagen",
|
||||
"delete": "Löschen",
|
||||
"delete_all_data": "Alle Daten löschen?",
|
||||
"error_creating_group": "Fehler beim Erstellen der Gruppe, bitte erneut versuchen",
|
||||
"error_reading_file": "Fehler beim Lesen der Datei",
|
||||
"export_canceled": "Export abgebrochen",
|
||||
"export_data": "Daten exportieren",
|
||||
"format_exception": "Formatfehler (siehe Konsole)",
|
||||
"game": "Spielvorlage",
|
||||
"game_name": "Spielvorlagenname",
|
||||
"group": "Gruppe",
|
||||
"group_name": "Gruppenname",
|
||||
"groups": "Gruppen",
|
||||
"home": "Startseite",
|
||||
"import_canceled": "Import abgebrochen",
|
||||
"import_data": "Daten importieren",
|
||||
"info": "Info",
|
||||
"invalid_schema": "Ungültiges Schema",
|
||||
"least_points": "Niedrigste Punkte",
|
||||
"match_in_progress": "Match läuft...",
|
||||
"match_name": "Matchname",
|
||||
"matches": "Matches",
|
||||
"menu": "Menü",
|
||||
"most_points": "Höchste Punkte",
|
||||
"no_data_available": "Keine Daten verfügbar",
|
||||
"no_groups_created_yet": "Noch keine Gruppen erstellt",
|
||||
"no_matches_created_yet": "Noch keine Matches erstellt",
|
||||
"no_players_created_yet": "Noch keine Spieler:in erstellt",
|
||||
"no_players_found_with_that_name": "Keine Spieler:in mit diesem Namen gefunden",
|
||||
"no_players_selected": "Keine Spieler:in ausgewählt",
|
||||
"no_recent_matches_available": "Keine letzten Matches verfügbar",
|
||||
"no_second_match_available": "Kein zweites Match verfügbar",
|
||||
"no_statistics_available": "Keine Statistiken verfügbar",
|
||||
"none": "Kein",
|
||||
"none_group": "Keine",
|
||||
"not_available": "Nicht verfügbar",
|
||||
"player_name": "Spieler:innenname",
|
||||
"players": "Spieler:in",
|
||||
"players_count": "{count} Spieler",
|
||||
"quick_create": "Schnellzugriff",
|
||||
"recent_matches": "Letzte Matches",
|
||||
"ruleset": "Regelwerk",
|
||||
"ruleset_least_points": "Umgekehrte Wertung: Der/die Spieler:in mit den wenigsten Punkten gewinnt.",
|
||||
"ruleset_most_points": "Traditionelles Regelwerk: Der/die Spieler:in mit den meisten Punkten gewinnt.",
|
||||
"ruleset_single_loser": "Genau ein:e Verlierer:in wird bestimmt; der letzte Platz erhält die Strafe oder Konsequenz.",
|
||||
"ruleset_single_winner": "Genau ein:e Gewinner:in wird gewählt; Unentschieden werden durch einen vordefinierten Tie-Breaker aufgelöst.",
|
||||
"search_for_groups": "Nach Gruppen suchen",
|
||||
"search_for_players": "Nach Spieler:innen suchen",
|
||||
"select_winner": "Gewinner:in wählen:",
|
||||
"selected_players": "Ausgewählte Spieler:in: {count}",
|
||||
"settings": "Einstellungen",
|
||||
"single_loser": "Ein:e Verlierer:in",
|
||||
"single_winner": "Ein:e Gewinner:in",
|
||||
|
sneeex marked this conversation as resolved
Outdated
flixcoo
commented
Gendern? (auch generell) Gendern? (auch generell)
|
||||
"statistics": "Statistiken",
|
||||
"stats": "Statistiken",
|
||||
"successfully_added_player": "Spieler:in {playerName} erfolgreich hinzugefügt",
|
||||
"there_is_no_group_matching_your_search": "Es gibt keine Gruppe, die deiner Suche entspricht",
|
||||
"this_cannot_be_undone": "Dies kann nicht rückgängig gemacht werden",
|
||||
"today_at": "Heute um {time}",
|
||||
"undo": "Rückgängig",
|
||||
"unknown_exception": "Unbekannter Fehler (siehe Konsole)",
|
||||
"winner": "Gewinner:in: {winnerName}",
|
||||
"winrate": "Siegquote",
|
||||
"wins": "Siege",
|
||||
"yesterday_at": "Gestern um {time}"
|
||||
}
|
||||
367
lib/l10n/arb/app_en.arb
Normal file
@@ -0,0 +1,367 @@
|
||||
{
|
||||
|
sneeex marked this conversation as resolved
flixcoo
commented
Datei alphabetisch sortierne Datei alphabetisch sortierne
|
||||
"@@locale": "en",
|
||||
"@all_players": {
|
||||
"description": "Label for all players list"
|
||||
},
|
||||
"@all_players_selected": {
|
||||
"description": "Message when all players are added to selection"
|
||||
},
|
||||
"@amount_of_matches": {
|
||||
"description": "Label for amount of matches statistic"
|
||||
},
|
||||
"@cancel": {
|
||||
"description": "Cancel button text"
|
||||
},
|
||||
"@choose_game": {
|
||||
"description": "Label for choosing a game"
|
||||
},
|
||||
"@choose_group": {
|
||||
"description": "Label for choosing a group"
|
||||
},
|
||||
"@choose_ruleset": {
|
||||
"description": "Label for choosing a ruleset"
|
||||
},
|
||||
"@could_not_add_player": {
|
||||
"description": "Error message when adding a player fails",
|
||||
"placeholders": {
|
||||
"playerName": {
|
||||
"type": "String",
|
||||
"example": "John"
|
||||
}
|
||||
}
|
||||
},
|
||||
"@create_group": {
|
||||
"description": "Button text to create a group"
|
||||
},
|
||||
"@create_match": {
|
||||
"description": "Button text to create a match"
|
||||
},
|
||||
"@create_new_group": {
|
||||
"description": "Button text to create a new group"
|
||||
},
|
||||
"@create_new_match": {
|
||||
"description": "Button text to create a new match"
|
||||
},
|
||||
"@data_successfully_deleted": {
|
||||
"description": "Success message after deleting data"
|
||||
},
|
||||
"@data_successfully_exported": {
|
||||
"description": "Success message after exporting data"
|
||||
},
|
||||
"@data_successfully_imported": {
|
||||
"description": "Success message after importing data"
|
||||
},
|
||||
"@days_ago": {
|
||||
"description": "Date format for days ago",
|
||||
"placeholders": {
|
||||
"count": {
|
||||
"type": "int"
|
||||
}
|
||||
}
|
||||
},
|
||||
"@delete": {
|
||||
"description": "Delete button text"
|
||||
},
|
||||
"@delete_all_data": {
|
||||
"description": "Confirmation dialog for deleting all data"
|
||||
},
|
||||
"@error_creating_group": {
|
||||
"description": "Error message when group creation fails"
|
||||
},
|
||||
"@error_reading_file": {
|
||||
"description": "Error message when file cannot be read"
|
||||
},
|
||||
"@export_canceled": {
|
||||
"description": "Message when export is canceled"
|
||||
},
|
||||
"@export_data": {
|
||||
"description": "Export data menu item"
|
||||
},
|
||||
"@format_exception": {
|
||||
"description": "Error message for format exceptions"
|
||||
},
|
||||
"@game": {
|
||||
"description": "Game label"
|
||||
},
|
||||
"@game_name": {
|
||||
"description": "Placeholder for game name search"
|
||||
},
|
||||
"@game_tracker": {
|
||||
"description": "App Name"
|
||||
},
|
||||
"@group": {
|
||||
"description": "Group label"
|
||||
|
sneeex marked this conversation as resolved
Outdated
flixcoo
commented
Würde Würde `:` und die Variable wegnehmen. Wäre die Variable innerhalb des Strings, dann sinnvoll, ansonsten nicht und einfach an der jeweiligen Stelle mit interpolation machen
|
||||
},
|
||||
"@group_name": {
|
||||
"description": "Placeholder for group name input"
|
||||
},
|
||||
"@groups": {
|
||||
"description": "Label for groups"
|
||||
},
|
||||
"@home": {
|
||||
"description": "Home tab label"
|
||||
},
|
||||
"@import_canceled": {
|
||||
"description": "Message when import is canceled"
|
||||
},
|
||||
"@import_data": {
|
||||
"description": "Import data menu item"
|
||||
},
|
||||
"@info": {
|
||||
"description": "Info label"
|
||||
},
|
||||
"@invalid_schema": {
|
||||
"description": "Error message for invalid schema"
|
||||
},
|
||||
"@least_points": {
|
||||
"description": "Title for least points ruleset"
|
||||
},
|
||||
"@match_in_progress": {
|
||||
"description": "Message when match is in progress"
|
||||
},
|
||||
"@match_name": {
|
||||
"description": "Placeholder for match name input"
|
||||
},
|
||||
"@matches": {
|
||||
"description": "Label for matches"
|
||||
},
|
||||
"@menu": {
|
||||
"description": "Menu label"
|
||||
},
|
||||
"@most_points": {
|
||||
"description": "Title for most points ruleset"
|
||||
},
|
||||
"@no_data_available": {
|
||||
"description": "Message when no data in the statistic tiles is given"
|
||||
},
|
||||
"@no_groups_created_yet": {
|
||||
"description": "Message when no groups exist"
|
||||
},
|
||||
"@no_matches_created_yet": {
|
||||
"description": "Message when no matches exist"
|
||||
},
|
||||
"@no_players_created_yet": {
|
||||
"description": "Message when no players exist"
|
||||
},
|
||||
"@no_players_found_with_that_name": {
|
||||
"description": "Message when search returns no results"
|
||||
},
|
||||
"@no_players_selected": {
|
||||
"description": "Message when no players are selected"
|
||||
},
|
||||
"@no_recent_matches_available": {
|
||||
"description": "Message when no recent matches exist"
|
||||
},
|
||||
"@no_second_match_available": {
|
||||
"description": "Message when no second match exists"
|
||||
},
|
||||
"@no_statistics_available": {
|
||||
"description": "Message when no statistics are available, because no matches were played yet"
|
||||
},
|
||||
"@none": {
|
||||
"description": "None option label"
|
||||
},
|
||||
"@none_group": {
|
||||
"description": "None group option label"
|
||||
},
|
||||
"@not_available": {
|
||||
"description": "Abbreviation for not available"
|
||||
},
|
||||
"@player_name": {
|
||||
"description": "Placeholder for player name input"
|
||||
},
|
||||
"@players": {
|
||||
"description": "Players label"
|
||||
},
|
||||
"@players_count": {
|
||||
"description": "Shows the number of players",
|
||||
"placeholders": {
|
||||
"count": {
|
||||
"type": "int"
|
||||
}
|
||||
}
|
||||
},
|
||||
"@quick_create": {
|
||||
"description": "Title for quick create section"
|
||||
},
|
||||
"@recent_matches": {
|
||||
"description": "Title for recent matches section"
|
||||
},
|
||||
"@ruleset": {
|
||||
"description": "Ruleset label"
|
||||
},
|
||||
"@ruleset_least_points": {
|
||||
"description": "Description for least points ruleset"
|
||||
},
|
||||
"@ruleset_most_points": {
|
||||
"description": "Description for most points ruleset"
|
||||
},
|
||||
"@ruleset_single_loser": {
|
||||
"description": "Description for single loser ruleset"
|
||||
},
|
||||
"@ruleset_single_winner": {
|
||||
"description": "Description for single winner ruleset"
|
||||
},
|
||||
"@search_for_groups": {
|
||||
"description": "Hint text for group search input field"
|
||||
},
|
||||
"@search_for_players": {
|
||||
"description": "Hint text for player search input field"
|
||||
},
|
||||
"@select_winner": {
|
||||
"description": "Label to select the winner"
|
||||
},
|
||||
"@selected_players": {
|
||||
"description": "Shows the number of selected players",
|
||||
"placeholders": {
|
||||
"count": {
|
||||
"type": "int",
|
||||
"format": "compact"
|
||||
}
|
||||
}
|
||||
},
|
||||
"@settings": {
|
||||
"description": "Settings label"
|
||||
},
|
||||
"@single_loser": {
|
||||
"description": "Title for single loser ruleset"
|
||||
},
|
||||
"@single_winner": {
|
||||
"description": "Title for single winner ruleset"
|
||||
},
|
||||
"@statistics": {
|
||||
"description": "Statistics tab label"
|
||||
},
|
||||
"@stats": {
|
||||
"description": "Stats tab label (short)"
|
||||
},
|
||||
"@successfully_added_player": {
|
||||
"description": "Success message when adding a player",
|
||||
"placeholders": {
|
||||
"playerName": {
|
||||
"type": "String",
|
||||
"example": "John"
|
||||
}
|
||||
}
|
||||
},
|
||||
"@there_is_no_group_matching_your_search": {
|
||||
"description": "Message when search returns no groups"
|
||||
},
|
||||
"@this_cannot_be_undone": {
|
||||
"description": "Warning message for irreversible actions"
|
||||
},
|
||||
"@today_at": {
|
||||
"description": "Date format for today",
|
||||
"placeholders": {
|
||||
"time": {
|
||||
"type": "String",
|
||||
"example": "14:30"
|
||||
}
|
||||
}
|
||||
},
|
||||
"@undo": {
|
||||
"description": "Undo button text"
|
||||
},
|
||||
"@unknown_exception": {
|
||||
"description": "Error message for unknown exceptions"
|
||||
},
|
||||
"@winner": {
|
||||
"description": "Winner label"
|
||||
},
|
||||
"@winrate": {
|
||||
"description": "Label for winrate statistic"
|
||||
},
|
||||
"@wins": {
|
||||
"description": "Label for wins statistic"
|
||||
},
|
||||
"@yesterday_at": {
|
||||
"description": "Date format for yesterday",
|
||||
"placeholders": {
|
||||
"time": {
|
||||
"type": "String",
|
||||
"example": "14:30"
|
||||
}
|
||||
}
|
||||
},
|
||||
"all_players": "All players:",
|
||||
"all_players_selected": "All players selected",
|
||||
"amount_of_matches": "Amount of Matches",
|
||||
"cancel": "Cancel",
|
||||
"choose_game": "Choose Game",
|
||||
"choose_group": "Choose Group",
|
||||
"choose_ruleset": "Choose Ruleset",
|
||||
"could_not_add_player": "Could not add player {playerName}",
|
||||
"create_group": "Create Group",
|
||||
"create_match": "Create match",
|
||||
"create_new_group": "Create new group",
|
||||
"create_new_match": "Create new match",
|
||||
"data_successfully_deleted": "Data successfully deleted",
|
||||
"data_successfully_exported": "Data successfully exported",
|
||||
"data_successfully_imported": "Data successfully imported",
|
||||
"days_ago": "{count} days ago",
|
||||
"delete": "Delete",
|
||||
"delete_all_data": "Delete all data?",
|
||||
"error_creating_group": "Error while creating group, please try again",
|
||||
"error_reading_file": "Error reading file",
|
||||
"export_canceled": "Export canceled",
|
||||
"export_data": "Export data",
|
||||
"format_exception": "Format Exception (see console)",
|
||||
"game": "Game",
|
||||
"game_name": "Game Name",
|
||||
"game_tracker": "Game Tracker",
|
||||
"group": "Group",
|
||||
"group_name": "Group name",
|
||||
"groups": "Groups",
|
||||
"home": "Home",
|
||||
"import_canceled": "Import canceled",
|
||||
"import_data": "Import data",
|
||||
"info": "Info",
|
||||
"invalid_schema": "Invalid Schema",
|
||||
"least_points": "Least Points",
|
||||
"match_in_progress": "Match in progress...",
|
||||
"match_name": "Match name",
|
||||
"matches": "Matches",
|
||||
"menu": "Menu",
|
||||
"most_points": "Most Points",
|
||||
"no_data_available": "No data available",
|
||||
"no_groups_created_yet": "No groups created yet",
|
||||
"no_matches_created_yet": "No matches created yet",
|
||||
"no_players_created_yet": "No players created yet",
|
||||
"no_players_found_with_that_name": "No players found with that name",
|
||||
"no_players_selected": "No players selected",
|
||||
"no_recent_matches_available": "No recent matches available",
|
||||
"no_second_match_available": "No second match available",
|
||||
"no_statistics_available": "No statistics available",
|
||||
"none": "None",
|
||||
"none_group": "None",
|
||||
"not_available": "Not available",
|
||||
"player_name": "Player name",
|
||||
"players": "Players",
|
||||
"players_count": "{count} Players",
|
||||
"quick_create": "Quick Create",
|
||||
"recent_matches": "Recent Matches",
|
||||
"ruleset": "Ruleset",
|
||||
"ruleset_least_points": "Inverse scoring: the player with the fewest points wins.",
|
||||
"ruleset_most_points": "Traditional ruleset: the player with the most points wins.",
|
||||
"ruleset_single_loser": "Exactly one loser is determined; last place receives the penalty or consequence.",
|
||||
"ruleset_single_winner": "Exactly one winner is chosen; ties are resolved by a predefined tiebreaker.",
|
||||
"search_for_groups": "Search for groups",
|
||||
"search_for_players": "Search for players",
|
||||
"select_winner": "Select Winner:",
|
||||
"selected_players": "Selected players: {count}",
|
||||
"settings": "Settings",
|
||||
"single_loser": "Single Loser",
|
||||
"single_winner": "Single Winner",
|
||||
"statistics": "Statistics",
|
||||
"stats": "Stats",
|
||||
"successfully_added_player": "Successfully added player {playerName}",
|
||||
"there_is_no_group_matching_your_search": "There is no group matching your search",
|
||||
"this_cannot_be_undone": "This can't be undone",
|
||||
"today_at": "Today at {time}",
|
||||
"undo": "Undo",
|
||||
"unknown_exception": "Unknown Exception (see console)",
|
||||
"winner": "Winner",
|
||||
"winrate": "Winrate",
|
||||
"wins": "Wins",
|
||||
"yesterday_at": "Yesterday at {time}"
|
||||
}
|
||||
620
lib/l10n/generated/app_localizations.dart
Normal file
@@ -0,0 +1,620 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||
import 'package:intl/intl.dart' as intl;
|
||||
|
||||
import 'app_localizations_de.dart';
|
||||
import 'app_localizations_en.dart';
|
||||
|
||||
// ignore_for_file: type=lint
|
||||
|
||||
/// Callers can lookup localized strings with an instance of AppLocalizations
|
||||
/// returned by `AppLocalizations.of(context)`.
|
||||
///
|
||||
/// Applications need to include `AppLocalizations.delegate()` in their app's
|
||||
/// `localizationDelegates` list, and the locales they support in the app's
|
||||
/// `supportedLocales` list. For example:
|
||||
///
|
||||
/// ```dart
|
||||
/// import 'generated/app_localizations.dart';
|
||||
///
|
||||
/// return MaterialApp(
|
||||
/// localizationsDelegates: AppLocalizations.localizationsDelegates,
|
||||
/// supportedLocales: AppLocalizations.supportedLocales,
|
||||
/// home: MyApplicationHome(),
|
||||
/// );
|
||||
/// ```
|
||||
///
|
||||
/// ## Update pubspec.yaml
|
||||
///
|
||||
/// Please make sure to update your pubspec.yaml to include the following
|
||||
/// packages:
|
||||
///
|
||||
/// ```yaml
|
||||
/// dependencies:
|
||||
/// # Internationalization support.
|
||||
/// flutter_localizations:
|
||||
/// sdk: flutter
|
||||
/// intl: any # Use the pinned version from flutter_localizations
|
||||
///
|
||||
/// # Rest of dependencies
|
||||
/// ```
|
||||
///
|
||||
/// ## iOS Applications
|
||||
///
|
||||
/// iOS applications define key application metadata, including supported
|
||||
/// locales, in an Info.plist file that is built into the application bundle.
|
||||
/// To configure the locales supported by your app, you’ll need to edit this
|
||||
/// file.
|
||||
///
|
||||
/// First, open your project’s ios/Runner.xcworkspace Xcode workspace file.
|
||||
/// Then, in the Project Navigator, open the Info.plist file under the Runner
|
||||
/// project’s Runner folder.
|
||||
///
|
||||
/// Next, select the Information Property List item, select Add Item from the
|
||||
/// Editor menu, then select Localizations from the pop-up menu.
|
||||
///
|
||||
/// Select and expand the newly-created Localizations item then, for each
|
||||
/// locale your application supports, add a new item and select the locale
|
||||
/// you wish to add from the pop-up menu in the Value field. This list should
|
||||
/// be consistent with the languages listed in the AppLocalizations.supportedLocales
|
||||
/// property.
|
||||
abstract class AppLocalizations {
|
||||
AppLocalizations(String locale)
|
||||
: localeName = intl.Intl.canonicalizedLocale(locale.toString());
|
||||
|
||||
final String localeName;
|
||||
|
||||
static AppLocalizations of(BuildContext context) {
|
||||
return Localizations.of<AppLocalizations>(context, AppLocalizations)!;
|
||||
}
|
||||
|
||||
static const LocalizationsDelegate<AppLocalizations> delegate =
|
||||
_AppLocalizationsDelegate();
|
||||
|
||||
/// A list of this localizations delegate along with the default localizations
|
||||
/// delegates.
|
||||
///
|
||||
/// Returns a list of localizations delegates containing this delegate along with
|
||||
/// GlobalMaterialLocalizations.delegate, GlobalCupertinoLocalizations.delegate,
|
||||
/// and GlobalWidgetsLocalizations.delegate.
|
||||
///
|
||||
/// Additional delegates can be added by appending to this list in
|
||||
/// MaterialApp. This list does not have to be used at all if a custom list
|
||||
/// of delegates is preferred or required.
|
||||
static const List<LocalizationsDelegate<dynamic>> localizationsDelegates =
|
||||
<LocalizationsDelegate<dynamic>>[
|
||||
delegate,
|
||||
GlobalMaterialLocalizations.delegate,
|
||||
GlobalCupertinoLocalizations.delegate,
|
||||
GlobalWidgetsLocalizations.delegate,
|
||||
];
|
||||
|
||||
/// A list of this localizations delegate's supported locales.
|
||||
static const List<Locale> supportedLocales = <Locale>[
|
||||
Locale('de'),
|
||||
Locale('en'),
|
||||
];
|
||||
|
||||
/// Label for choosing a group
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Choose Group'**
|
||||
String get choose_group;
|
||||
|
||||
/// Button text to create a new match
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Create new match'**
|
||||
String get create_new_match;
|
||||
|
||||
/// Label for choosing a ruleset
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Choose Ruleset'**
|
||||
String get choose_ruleset;
|
||||
|
||||
/// Label for choosing a game
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Choose Game'**
|
||||
String get choose_game;
|
||||
|
||||
/// Label to select the winner
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Select Winner:'**
|
||||
String get select_winner;
|
||||
|
||||
/// App Name
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Game Tracker'**
|
||||
String get game_tracker;
|
||||
|
||||
/// Message when no recent matches exist
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'No recent matches available'**
|
||||
String get no_recent_matches_available;
|
||||
|
||||
/// Message when no second match exists
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'No second match available'**
|
||||
String get no_second_match_available;
|
||||
|
||||
/// Confirmation dialog for deleting all data
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Delete all data?'**
|
||||
String get delete_all_data;
|
||||
|
||||
/// Cancel button text
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Cancel'**
|
||||
String get cancel;
|
||||
|
||||
/// Delete button text
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Delete'**
|
||||
String get delete;
|
||||
|
||||
/// Button text to create a new group
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Create new group'**
|
||||
String get create_new_group;
|
||||
|
||||
/// Error message when group creation fails
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Error while creating group, please try again'**
|
||||
String get error_creating_group;
|
||||
|
||||
/// Shows the number of selected players
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Selected players: {count}'**
|
||||
String selected_players(int count);
|
||||
|
||||
/// Message when no players are selected
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'No players selected'**
|
||||
String get no_players_selected;
|
||||
|
||||
/// Label for all players list
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'All players:'**
|
||||
String get all_players;
|
||||
|
||||
/// Success message when adding a player
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Successfully added player {playerName}'**
|
||||
String successfully_added_player(String playerName);
|
||||
|
||||
/// Error message when adding a player fails
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Could not add player {playerName}'**
|
||||
String could_not_add_player(String playerName);
|
||||
|
||||
/// Shows the winner's name
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Winner: {winnerName}'**
|
||||
String winner(String winnerName);
|
||||
|
||||
/// Players label
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Players'**
|
||||
String get players;
|
||||
|
||||
/// Message when no statistics are available, because no matches were played yet
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'No statistics available'**
|
||||
String get no_statistics_available;
|
||||
|
||||
/// Message when no data in the statistic tiles is given
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'No data available'**
|
||||
String get no_data_available;
|
||||
|
||||
/// Label for matches
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Matches'**
|
||||
String get matches;
|
||||
|
||||
/// Label for groups
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Groups'**
|
||||
String get groups;
|
||||
|
||||
/// Title for recent matches section
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Recent Matches'**
|
||||
String get recent_matches;
|
||||
|
||||
/// Title for quick create section
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Quick Create'**
|
||||
String get quick_create;
|
||||
|
||||
/// Message when match is in progress
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Match in progress...'**
|
||||
String get match_in_progress;
|
||||
|
||||
/// Menu label
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Menu'**
|
||||
String get menu;
|
||||
|
||||
/// Settings label
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Settings'**
|
||||
String get settings;
|
||||
|
||||
/// Export data menu item
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Export data'**
|
||||
String get export_data;
|
||||
|
||||
/// Import data menu item
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Import data'**
|
||||
String get import_data;
|
||||
|
||||
/// Warning message for irreversible actions
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'This can\'t be undone'**
|
||||
String get this_cannot_be_undone;
|
||||
|
||||
/// Success message after deleting data
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Data successfully deleted'**
|
||||
String get data_successfully_deleted;
|
||||
|
||||
/// Success message after importing data
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Data successfully imported'**
|
||||
String get data_successfully_imported;
|
||||
|
||||
/// Error message for invalid schema
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Invalid Schema'**
|
||||
String get invalid_schema;
|
||||
|
||||
/// Error message when file cannot be read
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Error reading file'**
|
||||
String get error_reading_file;
|
||||
|
||||
/// Message when import is canceled
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Import canceled'**
|
||||
String get import_canceled;
|
||||
|
||||
/// Error message for format exceptions
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Format Exception (see console)'**
|
||||
String get format_exception;
|
||||
|
||||
/// Error message for unknown exceptions
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Unknown Exception (see console)'**
|
||||
String get unknown_exception;
|
||||
|
||||
/// Success message after exporting data
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Data successfully exported'**
|
||||
String get data_successfully_exported;
|
||||
|
||||
/// Message when export is canceled
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Export canceled'**
|
||||
String get export_canceled;
|
||||
|
||||
/// Undo button text
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Undo'**
|
||||
String get undo;
|
||||
|
||||
/// Label for wins statistic
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Wins'**
|
||||
String get wins;
|
||||
|
||||
/// Label for winrate statistic
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Winrate'**
|
||||
String get winrate;
|
||||
|
||||
/// Label for amount of matches statistic
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Amount of Matches'**
|
||||
String get amount_of_matches;
|
||||
|
||||
/// Info label
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Info'**
|
||||
String get info;
|
||||
|
||||
/// Message when no groups exist
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'No groups created yet'**
|
||||
String get no_groups_created_yet;
|
||||
|
||||
/// Message when no players exist
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'No players created yet'**
|
||||
String get no_players_created_yet;
|
||||
|
||||
/// Button text to create a group
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Create Group'**
|
||||
String get create_group;
|
||||
|
||||
/// Placeholder for group name input
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Group name'**
|
||||
String get group_name;
|
||||
|
||||
/// Placeholder for player name input
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Player name'**
|
||||
String get player_name;
|
||||
|
||||
/// Message when no matches exist
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'No matches created yet'**
|
||||
String get no_matches_created_yet;
|
||||
|
||||
/// Placeholder for match name input
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Match name'**
|
||||
String get match_name;
|
||||
|
||||
/// Game label
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Game'**
|
||||
String get game;
|
||||
|
||||
/// Ruleset label
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Ruleset'**
|
||||
String get ruleset;
|
||||
|
||||
/// Group label
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Group'**
|
||||
String get group;
|
||||
|
||||
/// None option label
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'None'**
|
||||
String get none;
|
||||
|
||||
/// None group option label
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'None'**
|
||||
String get none_group;
|
||||
|
||||
/// Button text to create a match
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Create match'**
|
||||
String get create_match;
|
||||
|
||||
/// Message when search returns no results
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'No players found with that name'**
|
||||
String get no_players_found_with_that_name;
|
||||
|
||||
/// Message when all players are added to selection
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'All players selected'**
|
||||
String get all_players_selected;
|
||||
|
||||
/// Date format for today
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Today at {time}'**
|
||||
String today_at(String time);
|
||||
|
||||
/// Date format for yesterday
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Yesterday at {time}'**
|
||||
String yesterday_at(String time);
|
||||
|
||||
/// Date format for days ago
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'{count} days ago'**
|
||||
String days_ago(int count);
|
||||
|
||||
/// Home tab label
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Home'**
|
||||
String get home;
|
||||
|
||||
/// Statistics tab label
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Statistics'**
|
||||
String get statistics;
|
||||
|
||||
/// Stats tab label (short)
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Stats'**
|
||||
String get stats;
|
||||
|
||||
/// Shows the number of players
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'{count} Players'**
|
||||
String players_count(int count);
|
||||
|
||||
/// Message when search returns no groups
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'There is no group matching your search'**
|
||||
String get there_is_no_group_matching_your_search;
|
||||
|
||||
/// Placeholder for game name search
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Game Name'**
|
||||
String get game_name;
|
||||
|
||||
/// Description for single winner ruleset
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Exactly one winner is chosen; ties are resolved by a predefined tiebreaker.'**
|
||||
String get ruleset_single_winner;
|
||||
|
||||
/// Description for single loser ruleset
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Exactly one loser is determined; last place receives the penalty or consequence.'**
|
||||
String get ruleset_single_loser;
|
||||
|
||||
/// Description for most points ruleset
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Traditional ruleset: the player with the most points wins.'**
|
||||
String get ruleset_most_points;
|
||||
|
||||
/// Description for least points ruleset
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Inverse scoring: the player with the fewest points wins.'**
|
||||
String get ruleset_least_points;
|
||||
|
||||
/// Title for single winner ruleset
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Single Winner'**
|
||||
String get single_winner;
|
||||
|
||||
/// Title for single loser ruleset
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Single Loser'**
|
||||
String get single_loser;
|
||||
|
||||
/// Title for most points ruleset
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Most Points'**
|
||||
String get most_points;
|
||||
|
||||
/// Title for least points ruleset
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Least Points'**
|
||||
String get least_points;
|
||||
|
||||
/// Hint text for player search input field
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Search for players'**
|
||||
String get search_for_players;
|
||||
|
||||
/// Hint text for group search input field
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Search for groups'**
|
||||
String get search_for_groups;
|
||||
|
||||
/// Abbreviation for not available
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Not available'**
|
||||
String get not_available;
|
||||
}
|
||||
|
||||
class _AppLocalizationsDelegate
|
||||
extends LocalizationsDelegate<AppLocalizations> {
|
||||
const _AppLocalizationsDelegate();
|
||||
|
||||
@override
|
||||
Future<AppLocalizations> load(Locale locale) {
|
||||
return SynchronousFuture<AppLocalizations>(lookupAppLocalizations(locale));
|
||||
}
|
||||
|
||||
@override
|
||||
bool isSupported(Locale locale) =>
|
||||
<String>['de', 'en'].contains(locale.languageCode);
|
||||
|
||||
@override
|
||||
bool shouldReload(_AppLocalizationsDelegate old) => false;
|
||||
}
|
||||
|
||||
AppLocalizations lookupAppLocalizations(Locale locale) {
|
||||
// Lookup logic when only language code is specified.
|
||||
switch (locale.languageCode) {
|
||||
case 'de':
|
||||
return AppLocalizationsDe();
|
||||
case 'en':
|
||||
return AppLocalizationsEn();
|
||||
}
|
||||
|
||||
throw FlutterError(
|
||||
'AppLocalizations.delegate failed to load unsupported locale "$locale". This is likely '
|
||||
'an issue with the localizations generation tool. Please file an issue '
|
||||
'on GitHub with a reproducible sample app and the gen-l10n configuration '
|
||||
'that was used.',
|
||||
);
|
||||
}
|
||||
282
lib/l10n/generated/app_localizations_de.dart
Normal file
@@ -0,0 +1,282 @@
|
||||
// ignore: unused_import
|
||||
import 'package:intl/intl.dart' as intl;
|
||||
import 'app_localizations.dart';
|
||||
|
||||
// ignore_for_file: type=lint
|
||||
|
||||
/// The translations for German (`de`).
|
||||
class AppLocalizationsDe extends AppLocalizations {
|
||||
AppLocalizationsDe([String locale = 'de']) : super(locale);
|
||||
|
||||
@override
|
||||
String get choose_group => 'Gruppe wählen';
|
||||
|
||||
@override
|
||||
String get create_new_match => 'Neues Match erstellen';
|
||||
|
||||
@override
|
||||
String get choose_ruleset => 'Regelwerk wählen';
|
||||
|
||||
@override
|
||||
String get choose_game => 'Spielvorlage wählen';
|
||||
|
||||
@override
|
||||
String get select_winner => 'Gewinner:in wählen:';
|
||||
|
||||
@override
|
||||
String get game_tracker => 'Game Tracker';
|
||||
|
||||
@override
|
||||
String get no_recent_matches_available => 'Keine letzten Matches verfügbar';
|
||||
|
||||
@override
|
||||
String get no_second_match_available => 'Kein zweites Match verfügbar';
|
||||
|
||||
@override
|
||||
String get delete_all_data => 'Alle Daten löschen?';
|
||||
|
||||
@override
|
||||
String get cancel => 'Abbrechen';
|
||||
|
||||
@override
|
||||
String get delete => 'Löschen';
|
||||
|
||||
@override
|
||||
String get create_new_group => 'Neue Gruppe erstellen';
|
||||
|
||||
@override
|
||||
String get error_creating_group =>
|
||||
'Fehler beim Erstellen der Gruppe, bitte erneut versuchen';
|
||||
|
||||
@override
|
||||
String selected_players(int count) {
|
||||
final intl.NumberFormat countNumberFormat = intl.NumberFormat.compact(
|
||||
locale: localeName,
|
||||
);
|
||||
final String countString = countNumberFormat.format(count);
|
||||
|
||||
return 'Ausgewählte Spieler:in: $countString';
|
||||
}
|
||||
|
||||
@override
|
||||
String get no_players_selected => 'Keine Spieler:in ausgewählt';
|
||||
|
||||
@override
|
||||
String get all_players => 'Alle Spieler:innen:';
|
||||
|
||||
@override
|
||||
String successfully_added_player(String playerName) {
|
||||
return 'Spieler:in $playerName erfolgreich hinzugefügt';
|
||||
}
|
||||
|
||||
@override
|
||||
String could_not_add_player(String playerName) {
|
||||
return 'Spieler:in $playerName konnte nicht hinzugefügt werden';
|
||||
}
|
||||
|
||||
@override
|
||||
String winner(String winnerName) {
|
||||
return 'Gewinner:in: $winnerName';
|
||||
}
|
||||
|
||||
@override
|
||||
String get players => 'Spieler:in';
|
||||
|
||||
@override
|
||||
String get no_statistics_available => 'Keine Statistiken verfügbar';
|
||||
|
||||
@override
|
||||
String get no_data_available => 'Keine Daten verfügbar';
|
||||
|
||||
@override
|
||||
String get matches => 'Matches';
|
||||
|
||||
@override
|
||||
String get groups => 'Gruppen';
|
||||
|
||||
@override
|
||||
String get recent_matches => 'Letzte Matches';
|
||||
|
||||
@override
|
||||
String get quick_create => 'Schnellzugriff';
|
||||
|
||||
@override
|
||||
String get match_in_progress => 'Match läuft...';
|
||||
|
||||
@override
|
||||
String get menu => 'Menü';
|
||||
|
||||
@override
|
||||
String get settings => 'Einstellungen';
|
||||
|
||||
@override
|
||||
String get export_data => 'Daten exportieren';
|
||||
|
||||
@override
|
||||
String get import_data => 'Daten importieren';
|
||||
|
||||
@override
|
||||
String get this_cannot_be_undone =>
|
||||
'Dies kann nicht rückgängig gemacht werden';
|
||||
|
||||
@override
|
||||
String get data_successfully_deleted => 'Daten erfolgreich gelöscht';
|
||||
|
||||
@override
|
||||
String get data_successfully_imported => 'Daten erfolgreich importiert';
|
||||
|
||||
@override
|
||||
String get invalid_schema => 'Ungültiges Schema';
|
||||
|
||||
@override
|
||||
String get error_reading_file => 'Fehler beim Lesen der Datei';
|
||||
|
||||
@override
|
||||
String get import_canceled => 'Import abgebrochen';
|
||||
|
||||
@override
|
||||
String get format_exception => 'Formatfehler (siehe Konsole)';
|
||||
|
||||
@override
|
||||
String get unknown_exception => 'Unbekannter Fehler (siehe Konsole)';
|
||||
|
||||
@override
|
||||
String get data_successfully_exported => 'Daten erfolgreich exportiert';
|
||||
|
||||
@override
|
||||
String get export_canceled => 'Export abgebrochen';
|
||||
|
||||
@override
|
||||
String get undo => 'Rückgängig';
|
||||
|
||||
@override
|
||||
String get wins => 'Siege';
|
||||
|
||||
@override
|
||||
String get winrate => 'Siegquote';
|
||||
|
||||
@override
|
||||
String get amount_of_matches => 'Anzahl der Matches';
|
||||
|
||||
@override
|
||||
String get info => 'Info';
|
||||
|
||||
@override
|
||||
String get no_groups_created_yet => 'Noch keine Gruppen erstellt';
|
||||
|
||||
@override
|
||||
String get no_players_created_yet => 'Noch keine Spieler:in erstellt';
|
||||
|
||||
@override
|
||||
String get create_group => 'Gruppe erstellen';
|
||||
|
||||
@override
|
||||
String get group_name => 'Gruppenname';
|
||||
|
||||
@override
|
||||
String get player_name => 'Spieler:innenname';
|
||||
|
||||
@override
|
||||
String get no_matches_created_yet => 'Noch keine Matches erstellt';
|
||||
|
||||
@override
|
||||
String get match_name => 'Matchname';
|
||||
|
||||
@override
|
||||
String get game => 'Spielvorlage';
|
||||
|
||||
@override
|
||||
String get ruleset => 'Regelwerk';
|
||||
|
||||
@override
|
||||
String get group => 'Gruppe';
|
||||
|
||||
@override
|
||||
String get none => 'Kein';
|
||||
|
||||
@override
|
||||
String get none_group => 'Keine';
|
||||
|
||||
@override
|
||||
String get create_match => 'Match erstellen';
|
||||
|
||||
@override
|
||||
String get no_players_found_with_that_name =>
|
||||
'Keine Spieler:in mit diesem Namen gefunden';
|
||||
|
||||
@override
|
||||
String get all_players_selected => 'Alle Spieler:innen ausgewählt';
|
||||
|
||||
@override
|
||||
String today_at(String time) {
|
||||
return 'Heute um $time';
|
||||
}
|
||||
|
||||
@override
|
||||
String yesterday_at(String time) {
|
||||
return 'Gestern um $time';
|
||||
}
|
||||
|
||||
@override
|
||||
String days_ago(int count) {
|
||||
return 'vor $count Tagen';
|
||||
}
|
||||
|
||||
@override
|
||||
String get home => 'Startseite';
|
||||
|
||||
@override
|
||||
String get statistics => 'Statistiken';
|
||||
|
||||
@override
|
||||
String get stats => 'Statistiken';
|
||||
|
||||
@override
|
||||
String players_count(int count) {
|
||||
return '$count Spieler';
|
||||
}
|
||||
|
||||
@override
|
||||
String get there_is_no_group_matching_your_search =>
|
||||
'Es gibt keine Gruppe, die deiner Suche entspricht';
|
||||
|
||||
@override
|
||||
String get game_name => 'Spielvorlagenname';
|
||||
|
||||
@override
|
||||
String get ruleset_single_winner =>
|
||||
'Genau ein:e Gewinner:in wird gewählt; Unentschieden werden durch einen vordefinierten Tie-Breaker aufgelöst.';
|
||||
|
||||
@override
|
||||
String get ruleset_single_loser =>
|
||||
'Genau ein:e Verlierer:in wird bestimmt; der letzte Platz erhält die Strafe oder Konsequenz.';
|
||||
|
||||
@override
|
||||
String get ruleset_most_points =>
|
||||
'Traditionelles Regelwerk: Der/die Spieler:in mit den meisten Punkten gewinnt.';
|
||||
|
||||
@override
|
||||
String get ruleset_least_points =>
|
||||
'Umgekehrte Wertung: Der/die Spieler:in mit den wenigsten Punkten gewinnt.';
|
||||
|
||||
@override
|
||||
String get single_winner => 'Ein:e Gewinner:in';
|
||||
|
||||
@override
|
||||
String get single_loser => 'Ein:e Verlierer:in';
|
||||
|
||||
@override
|
||||
String get most_points => 'Höchste Punkte';
|
||||
|
||||
@override
|
||||
String get least_points => 'Niedrigste Punkte';
|
||||
|
||||
@override
|
||||
String get search_for_players => 'Nach Spieler:innen suchen';
|
||||
|
||||
@override
|
||||
String get search_for_groups => 'Nach Gruppen suchen';
|
||||
|
||||
@override
|
||||
String get not_available => 'Nicht verfügbar';
|
||||
}
|
||||
281
lib/l10n/generated/app_localizations_en.dart
Normal file
@@ -0,0 +1,281 @@
|
||||
// ignore: unused_import
|
||||
import 'package:intl/intl.dart' as intl;
|
||||
import 'app_localizations.dart';
|
||||
|
||||
// ignore_for_file: type=lint
|
||||
|
||||
/// The translations for English (`en`).
|
||||
class AppLocalizationsEn extends AppLocalizations {
|
||||
AppLocalizationsEn([String locale = 'en']) : super(locale);
|
||||
|
||||
@override
|
||||
String get choose_group => 'Choose Group';
|
||||
|
||||
@override
|
||||
String get create_new_match => 'Create new match';
|
||||
|
||||
@override
|
||||
String get choose_ruleset => 'Choose Ruleset';
|
||||
|
||||
@override
|
||||
String get choose_game => 'Choose Game';
|
||||
|
||||
@override
|
||||
String get select_winner => 'Select Winner:';
|
||||
|
||||
@override
|
||||
String get game_tracker => 'Game Tracker';
|
||||
|
||||
@override
|
||||
String get no_recent_matches_available => 'No recent matches available';
|
||||
|
||||
@override
|
||||
String get no_second_match_available => 'No second match available';
|
||||
|
||||
@override
|
||||
String get delete_all_data => 'Delete all data?';
|
||||
|
||||
@override
|
||||
String get cancel => 'Cancel';
|
||||
|
||||
@override
|
||||
String get delete => 'Delete';
|
||||
|
||||
@override
|
||||
String get create_new_group => 'Create new group';
|
||||
|
||||
@override
|
||||
String get error_creating_group =>
|
||||
'Error while creating group, please try again';
|
||||
|
||||
@override
|
||||
String selected_players(int count) {
|
||||
final intl.NumberFormat countNumberFormat = intl.NumberFormat.compact(
|
||||
locale: localeName,
|
||||
);
|
||||
final String countString = countNumberFormat.format(count);
|
||||
|
||||
return 'Selected players: $countString';
|
||||
}
|
||||
|
||||
@override
|
||||
String get no_players_selected => 'No players selected';
|
||||
|
||||
@override
|
||||
String get all_players => 'All players:';
|
||||
|
||||
@override
|
||||
String successfully_added_player(String playerName) {
|
||||
return 'Successfully added player $playerName';
|
||||
}
|
||||
|
||||
@override
|
||||
String could_not_add_player(String playerName) {
|
||||
return 'Could not add player $playerName';
|
||||
}
|
||||
|
||||
@override
|
||||
String winner(String winnerName) {
|
||||
return 'Winner: $winnerName';
|
||||
}
|
||||
|
||||
@override
|
||||
String get players => 'Players';
|
||||
|
||||
@override
|
||||
String get no_statistics_available => 'No statistics available';
|
||||
|
||||
@override
|
||||
String get no_data_available => 'No data available';
|
||||
|
||||
@override
|
||||
String get matches => 'Matches';
|
||||
|
||||
@override
|
||||
String get groups => 'Groups';
|
||||
|
||||
@override
|
||||
String get recent_matches => 'Recent Matches';
|
||||
|
||||
@override
|
||||
String get quick_create => 'Quick Create';
|
||||
|
||||
@override
|
||||
String get match_in_progress => 'Match in progress...';
|
||||
|
||||
@override
|
||||
String get menu => 'Menu';
|
||||
|
||||
@override
|
||||
String get settings => 'Settings';
|
||||
|
||||
@override
|
||||
String get export_data => 'Export data';
|
||||
|
||||
@override
|
||||
String get import_data => 'Import data';
|
||||
|
||||
@override
|
||||
String get this_cannot_be_undone => 'This can\'t be undone';
|
||||
|
||||
@override
|
||||
String get data_successfully_deleted => 'Data successfully deleted';
|
||||
|
||||
@override
|
||||
String get data_successfully_imported => 'Data successfully imported';
|
||||
|
||||
@override
|
||||
String get invalid_schema => 'Invalid Schema';
|
||||
|
||||
@override
|
||||
String get error_reading_file => 'Error reading file';
|
||||
|
||||
@override
|
||||
String get import_canceled => 'Import canceled';
|
||||
|
||||
@override
|
||||
String get format_exception => 'Format Exception (see console)';
|
||||
|
||||
@override
|
||||
String get unknown_exception => 'Unknown Exception (see console)';
|
||||
|
||||
@override
|
||||
String get data_successfully_exported => 'Data successfully exported';
|
||||
|
||||
@override
|
||||
String get export_canceled => 'Export canceled';
|
||||
|
||||
@override
|
||||
String get undo => 'Undo';
|
||||
|
||||
@override
|
||||
String get wins => 'Wins';
|
||||
|
||||
@override
|
||||
String get winrate => 'Winrate';
|
||||
|
||||
@override
|
||||
String get amount_of_matches => 'Amount of Matches';
|
||||
|
||||
@override
|
||||
String get info => 'Info';
|
||||
|
||||
@override
|
||||
String get no_groups_created_yet => 'No groups created yet';
|
||||
|
||||
@override
|
||||
String get no_players_created_yet => 'No players created yet';
|
||||
|
||||
@override
|
||||
String get create_group => 'Create Group';
|
||||
|
||||
@override
|
||||
String get group_name => 'Group name';
|
||||
|
||||
@override
|
||||
String get player_name => 'Player name';
|
||||
|
||||
@override
|
||||
String get no_matches_created_yet => 'No matches created yet';
|
||||
|
||||
@override
|
||||
String get match_name => 'Match name';
|
||||
|
||||
@override
|
||||
String get game => 'Game';
|
||||
|
||||
@override
|
||||
String get ruleset => 'Ruleset';
|
||||
|
||||
@override
|
||||
String get group => 'Group';
|
||||
|
||||
@override
|
||||
String get none => 'None';
|
||||
|
||||
@override
|
||||
String get none_group => 'None';
|
||||
|
||||
@override
|
||||
String get create_match => 'Create match';
|
||||
|
||||
@override
|
||||
String get no_players_found_with_that_name =>
|
||||
'No players found with that name';
|
||||
|
||||
@override
|
||||
String get all_players_selected => 'All players selected';
|
||||
|
||||
@override
|
||||
String today_at(String time) {
|
||||
return 'Today at $time';
|
||||
}
|
||||
|
||||
@override
|
||||
String yesterday_at(String time) {
|
||||
return 'Yesterday at $time';
|
||||
}
|
||||
|
||||
@override
|
||||
String days_ago(int count) {
|
||||
return '$count days ago';
|
||||
}
|
||||
|
||||
@override
|
||||
String get home => 'Home';
|
||||
|
||||
@override
|
||||
String get statistics => 'Statistics';
|
||||
|
||||
@override
|
||||
String get stats => 'Stats';
|
||||
|
||||
@override
|
||||
String players_count(int count) {
|
||||
return '$count Players';
|
||||
}
|
||||
|
||||
@override
|
||||
String get there_is_no_group_matching_your_search =>
|
||||
'There is no group matching your search';
|
||||
|
||||
@override
|
||||
String get game_name => 'Game Name';
|
||||
|
||||
@override
|
||||
String get ruleset_single_winner =>
|
||||
'Exactly one winner is chosen; ties are resolved by a predefined tiebreaker.';
|
||||
|
||||
@override
|
||||
String get ruleset_single_loser =>
|
||||
'Exactly one loser is determined; last place receives the penalty or consequence.';
|
||||
|
||||
@override
|
||||
String get ruleset_most_points =>
|
||||
'Traditional ruleset: the player with the most points wins.';
|
||||
|
||||
@override
|
||||
String get ruleset_least_points =>
|
||||
'Inverse scoring: the player with the fewest points wins.';
|
||||
|
||||
@override
|
||||
String get single_winner => 'Single Winner';
|
||||
|
||||
@override
|
||||
String get single_loser => 'Single Loser';
|
||||
|
||||
@override
|
||||
String get most_points => 'Most Points';
|
||||
|
||||
@override
|
||||
String get least_points => 'Least Points';
|
||||
|
||||
@override
|
||||
String get search_for_players => 'Search for players';
|
||||
|
||||
@override
|
||||
String get search_for_groups => 'Search for groups';
|
||||
|
||||
@override
|
||||
String get not_available => 'Not available';
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:game_tracker/core/custom_theme.dart';
|
||||
import 'package:game_tracker/data/db/database.dart';
|
||||
import 'package:game_tracker/l10n/generated/app_localizations.dart';
|
||||
import 'package:game_tracker/presentation/views/main_menu/custom_navigation_bar.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
@@ -20,8 +21,20 @@ class GameTracker extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MaterialApp(
|
||||
|
sneeex marked this conversation as resolved
Outdated
flixcoo
commented
Debug-Print entfernen Debug-Print entfernen
|
||||
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
||||
supportedLocales: AppLocalizations.supportedLocales,
|
||||
localeResolutionCallback: (locale, supportedLocales) {
|
||||
for (final supportedLocale in supportedLocales) {
|
||||
if (supportedLocale.languageCode == locale?.languageCode) {
|
||||
return supportedLocale;
|
||||
}
|
||||
}
|
||||
return supportedLocales.firstWhere(
|
||||
(locale) => locale.languageCode == 'en',
|
||||
);
|
||||
},
|
||||
debugShowCheckedModeBanner: false,
|
||||
title: 'Game Tracker',
|
||||
onGenerateTitle: (context) => AppLocalizations.of(context).game_tracker,
|
||||
darkTheme: ThemeData.dark(),
|
||||
|
sneeex marked this conversation as resolved
Outdated
flixcoo
commented
Lokalisierung fehlt Lokalisierung fehlt
|
||||
|
||||
themeMode: ThemeMode.dark, // forces dark mode
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:game_tracker/core/custom_theme.dart';
|
||||
import 'package:game_tracker/l10n/generated/app_localizations.dart';
|
||||
import 'package:game_tracker/presentation/views/main_menu/group_view/groups_view.dart';
|
||||
import 'package:game_tracker/presentation/views/main_menu/home_view.dart';
|
||||
import 'package:game_tracker/presentation/views/main_menu/match_view/match_view.dart';
|
||||
@@ -19,13 +20,9 @@ class _CustomNavigationBarState extends State<CustomNavigationBar>
|
||||
int currentIndex = 0;
|
||||
int tabKeyCount = 0;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final loc = AppLocalizations.of(context);
|
||||
// Pretty ugly but works
|
||||
final List<Widget> tabs = [
|
||||
KeyedSubtree(key: ValueKey('home_$tabKeyCount'), child: const HomeView()),
|
||||
@@ -46,7 +43,7 @@ class _CustomNavigationBarState extends State<CustomNavigationBar>
|
||||
appBar: AppBar(
|
||||
centerTitle: true,
|
||||
title: Text(
|
||||
_currentTabTitle(),
|
||||
_currentTabTitle(context),
|
||||
style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
|
||||
),
|
||||
backgroundColor: CustomTheme.backgroundColor,
|
||||
@@ -89,28 +86,28 @@ class _CustomNavigationBarState extends State<CustomNavigationBar>
|
||||
index: 0,
|
||||
isSelected: currentIndex == 0,
|
||||
icon: Icons.home_rounded,
|
||||
label: 'Home',
|
||||
label: loc.home,
|
||||
onTabTapped: onTabTapped,
|
||||
),
|
||||
NavbarItem(
|
||||
index: 1,
|
||||
isSelected: currentIndex == 1,
|
||||
icon: Icons.gamepad_rounded,
|
||||
label: 'Matches',
|
||||
label: loc.matches,
|
||||
onTabTapped: onTabTapped,
|
||||
),
|
||||
NavbarItem(
|
||||
index: 2,
|
||||
isSelected: currentIndex == 2,
|
||||
icon: Icons.group_rounded,
|
||||
label: 'Groups',
|
||||
label: loc.groups,
|
||||
onTabTapped: onTabTapped,
|
||||
),
|
||||
NavbarItem(
|
||||
index: 3,
|
||||
isSelected: currentIndex == 3,
|
||||
icon: Icons.bar_chart_rounded,
|
||||
label: 'Stats',
|
||||
label: loc.statistics,
|
||||
onTabTapped: onTabTapped,
|
||||
),
|
||||
],
|
||||
@@ -128,16 +125,17 @@ class _CustomNavigationBarState extends State<CustomNavigationBar>
|
||||
});
|
||||
}
|
||||
|
||||
String _currentTabTitle() {
|
||||
String _currentTabTitle(context) {
|
||||
final loc = AppLocalizations.of(context);
|
||||
switch (currentIndex) {
|
||||
case 0:
|
||||
return 'Home';
|
||||
return loc.home;
|
||||
case 1:
|
||||
return 'Matches';
|
||||
return loc.matches;
|
||||
case 2:
|
||||
return 'Groups';
|
||||
return loc.groups;
|
||||
case 3:
|
||||
return 'Statistics';
|
||||
return loc.statistics;
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import 'package:game_tracker/core/enums.dart';
|
||||
import 'package:game_tracker/data/db/database.dart';
|
||||
import 'package:game_tracker/data/dto/group.dart';
|
||||
import 'package:game_tracker/data/dto/player.dart';
|
||||
import 'package:game_tracker/l10n/generated/app_localizations.dart';
|
||||
import 'package:game_tracker/presentation/widgets/buttons/custom_width_button.dart';
|
||||
import 'package:game_tracker/presentation/widgets/player_selection.dart';
|
||||
import 'package:game_tracker/presentation/widgets/text_input/text_input_field.dart';
|
||||
@@ -19,11 +20,13 @@ class CreateGroupView extends StatefulWidget {
|
||||
class _CreateGroupViewState extends State<CreateGroupView> {
|
||||
final _groupNameController = TextEditingController();
|
||||
late final AppDatabase db;
|
||||
|
||||
List<Player> selectedPlayers = [];
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
db = Provider.of<AppDatabase>(context, listen: false);
|
||||
_groupNameController.addListener(() {
|
||||
setState(() {});
|
||||
@@ -38,14 +41,15 @@ class _CreateGroupViewState extends State<CreateGroupView> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final loc = AppLocalizations.of(context);
|
||||
return Scaffold(
|
||||
backgroundColor: CustomTheme.backgroundColor,
|
||||
appBar: AppBar(
|
||||
backgroundColor: CustomTheme.backgroundColor,
|
||||
scrolledUnderElevation: 0,
|
||||
title: const Text(
|
||||
'Create new group',
|
||||
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
|
||||
title: Text(
|
||||
loc.create_new_group,
|
||||
style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
|
||||
),
|
||||
centerTitle: true,
|
||||
),
|
||||
@@ -57,7 +61,7 @@ class _CreateGroupViewState extends State<CreateGroupView> {
|
||||
margin: const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
|
||||
child: TextInputField(
|
||||
controller: _groupNameController,
|
||||
hintText: 'Group name',
|
||||
hintText: loc.group_name,
|
||||
onChanged: (value) {
|
||||
setState(() {});
|
||||
},
|
||||
@@ -73,7 +77,7 @@ class _CreateGroupViewState extends State<CreateGroupView> {
|
||||
),
|
||||
),
|
||||
CustomWidthButton(
|
||||
text: 'Create group',
|
||||
text: loc.create_group,
|
||||
sizeRelativeToWidth: 0.95,
|
||||
buttonType: ButtonType.primary,
|
||||
onPressed:
|
||||
@@ -94,10 +98,12 @@ class _CreateGroupViewState extends State<CreateGroupView> {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
backgroundColor: CustomTheme.boxColor,
|
||||
content: const Center(
|
||||
content: Center(
|
||||
child: Text(
|
||||
|
sneeex marked this conversation as resolved
Outdated
flixcoo
commented
Abkürzen für etwas generelles, z.B. Abkürzen für etwas generelles, z.B. `creating_group_error` oder `creating_group_error_message`
|
||||
'Error while creating group, please try again',
|
||||
style: TextStyle(color: Colors.white),
|
||||
AppLocalizations.of(
|
||||
context,
|
||||
).error_creating_group,
|
||||
style: const TextStyle(color: Colors.white),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -4,6 +4,7 @@ import 'package:game_tracker/core/custom_theme.dart';
|
||||
import 'package:game_tracker/data/db/database.dart';
|
||||
import 'package:game_tracker/data/dto/group.dart';
|
||||
import 'package:game_tracker/data/dto/player.dart';
|
||||
import 'package:game_tracker/l10n/generated/app_localizations.dart';
|
||||
import 'package:game_tracker/presentation/views/main_menu/group_view/create_group_view.dart';
|
||||
import 'package:game_tracker/presentation/widgets/app_skeleton.dart';
|
||||
import 'package:game_tracker/presentation/widgets/buttons/custom_width_button.dart';
|
||||
@@ -34,12 +35,14 @@ class _GroupsViewState extends State<GroupsView> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
db = Provider.of<AppDatabase>(context, listen: false);
|
||||
loadGroups();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final loc = AppLocalizations.of(context);
|
||||
return Scaffold(
|
||||
backgroundColor: CustomTheme.backgroundColor,
|
||||
body: Stack(
|
||||
@@ -49,11 +52,11 @@ class _GroupsViewState extends State<GroupsView> {
|
||||
enabled: isLoading,
|
||||
child: Visibility(
|
||||
visible: groups.isNotEmpty,
|
||||
replacement: const Center(
|
||||
replacement: Center(
|
||||
child: TopCenteredMessage(
|
||||
icon: Icons.info,
|
||||
title: 'Info',
|
||||
message: 'No groups created yet',
|
||||
title: loc.info,
|
||||
message: loc.no_groups_created_yet,
|
||||
),
|
||||
),
|
||||
child: ListView.builder(
|
||||
@@ -73,7 +76,7 @@ class _GroupsViewState extends State<GroupsView> {
|
||||
Positioned(
|
||||
bottom: MediaQuery.paddingOf(context).bottom,
|
||||
child: CustomWidthButton(
|
||||
text: 'Create Group',
|
||||
text: loc.create_group,
|
||||
sizeRelativeToWidth: 0.90,
|
||||
onPressed: () async {
|
||||
await Navigator.push(
|
||||
|
||||
@@ -4,6 +4,7 @@ import 'package:game_tracker/data/db/database.dart';
|
||||
import 'package:game_tracker/data/dto/group.dart';
|
||||
import 'package:game_tracker/data/dto/match.dart';
|
||||
import 'package:game_tracker/data/dto/player.dart';
|
||||
import 'package:game_tracker/l10n/generated/app_localizations.dart';
|
||||
import 'package:game_tracker/presentation/widgets/app_skeleton.dart';
|
||||
import 'package:game_tracker/presentation/widgets/buttons/quick_create_button.dart';
|
||||
import 'package:game_tracker/presentation/widgets/tiles/info_tile.dart';
|
||||
@@ -71,6 +72,7 @@ class _HomeViewState extends State<HomeView> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final loc = AppLocalizations.of(context);
|
||||
return LayoutBuilder(
|
||||
builder: (BuildContext context, BoxConstraints constraints) {
|
||||
return AppSkeleton(
|
||||
@@ -86,7 +88,7 @@ class _HomeViewState extends State<HomeView> {
|
||||
QuickInfoTile(
|
||||
width: constraints.maxWidth * 0.45,
|
||||
height: constraints.maxHeight * 0.15,
|
||||
title: 'Matches',
|
||||
title: loc.matches,
|
||||
icon: Icons.groups_rounded,
|
||||
value: matchCount,
|
||||
),
|
||||
@@ -94,7 +96,7 @@ class _HomeViewState extends State<HomeView> {
|
||||
QuickInfoTile(
|
||||
width: constraints.maxWidth * 0.45,
|
||||
height: constraints.maxHeight * 0.15,
|
||||
title: 'Groups',
|
||||
title: loc.groups,
|
||||
icon: Icons.groups_rounded,
|
||||
value: groupCount,
|
||||
),
|
||||
@@ -104,15 +106,19 @@ class _HomeViewState extends State<HomeView> {
|
||||
padding: const EdgeInsets.symmetric(vertical: 16.0),
|
||||
child: InfoTile(
|
||||
width: constraints.maxWidth * 0.95,
|
||||
title: 'Recent Matches',
|
||||
title: loc.recent_matches,
|
||||
icon: Icons.timer,
|
||||
content: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 40.0),
|
||||
child: Visibility(
|
||||
visible: !isLoading && loadedRecentMatches.isNotEmpty,
|
||||
replacement: const Center(
|
||||
replacement: Center(
|
||||
heightFactor: 12,
|
||||
child: Text('No recent matches available'),
|
||||
child: Text(
|
||||
AppLocalizations.of(
|
||||
context,
|
||||
|
flixcoo marked this conversation as resolved
Outdated
flixcoo
commented
Idee: Generell alle Info-Messages auch als solche bennenen z.B. Idee: Generell alle Info-Messages auch als solche bennenen z.B. `info_no_recent_matches`
sneeex
commented
finde ich eigentlich unnötig finde ich eigentlich unnötig
sneeex
commented
weil eigentlich kann man die messages ja auch anders benutzen und es ist ja eigentlich egal obs ne info ist weil eigentlich kann man die messages ja auch anders benutzen und es ist ja eigentlich egal obs ne info ist
|
||||
).no_recent_matches_available,
|
||||
),
|
||||
),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
@@ -122,9 +128,14 @@ class _HomeViewState extends State<HomeView> {
|
||||
matchTitle: recentMatches[0].name,
|
||||
game: 'Winner',
|
||||
ruleset: 'Ruleset',
|
||||
players: _getPlayerText(recentMatches[0]),
|
||||
players: _getPlayerText(
|
||||
|
sneeex marked this conversation as resolved
Outdated
flixcoo
commented
Wenn Wenn `winner_label`und `ruleset_label` nirgends anders verwendet werden, einfach die normalen Strings belassen, weil diese ja in Zukunft durch das Ruleset und das Game ersetzt werden
|
||||
recentMatches[0],
|
||||
context,
|
||||
),
|
||||
winner: recentMatches[0].winner == null
|
||||
? 'Match in progress...'
|
||||
? AppLocalizations.of(
|
||||
context,
|
||||
).match_in_progress
|
||||
: recentMatches[0].winner!.name,
|
||||
),
|
||||
const Padding(
|
||||
@@ -136,16 +147,25 @@ class _HomeViewState extends State<HomeView> {
|
||||
matchTitle: recentMatches[1].name,
|
||||
game: 'Winner',
|
||||
ruleset: 'Ruleset',
|
||||
players: _getPlayerText(recentMatches[1]),
|
||||
players: _getPlayerText(
|
||||
recentMatches[1],
|
||||
context,
|
||||
),
|
||||
winner: recentMatches[1].winner == null
|
||||
? 'Match in progress...'
|
||||
? AppLocalizations.of(
|
||||
context,
|
||||
).match_in_progress
|
||||
: recentMatches[1].winner!.name,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
] else ...[
|
||||
const Center(
|
||||
Center(
|
||||
heightFactor: 5.35,
|
||||
child: Text('No second match available'),
|
||||
child: Text(
|
||||
AppLocalizations.of(
|
||||
context,
|
||||
).no_second_match_available,
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
@@ -156,7 +176,7 @@ class _HomeViewState extends State<HomeView> {
|
||||
),
|
||||
InfoTile(
|
||||
width: constraints.maxWidth * 0.95,
|
||||
title: 'Quick Create',
|
||||
title: loc.quick_create,
|
||||
icon: Icons.add_box_rounded,
|
||||
content: Column(
|
||||
children: [
|
||||
@@ -210,10 +230,11 @@ class _HomeViewState extends State<HomeView> {
|
||||
);
|
||||
}
|
||||
|
||||
String _getPlayerText(Match game) {
|
||||
String _getPlayerText(Match game, context) {
|
||||
final loc = AppLocalizations.of(context);
|
||||
if (game.group == null) {
|
||||
final playerCount = game.players?.length ?? 0;
|
||||
return '$playerCount Players';
|
||||
return loc.players_count(playerCount);
|
||||
}
|
||||
if (game.players == null || game.players!.isEmpty) {
|
||||
return game.group!.name;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:game_tracker/core/custom_theme.dart';
|
||||
import 'package:game_tracker/core/enums.dart';
|
||||
import 'package:game_tracker/l10n/generated/app_localizations.dart';
|
||||
import 'package:game_tracker/presentation/widgets/text_input/custom_search_bar.dart';
|
||||
import 'package:game_tracker/presentation/widgets/tiles/title_description_list_tile.dart';
|
||||
|
||||
@@ -20,6 +21,7 @@ class ChooseGameView extends StatefulWidget {
|
||||
|
||||
class _ChooseGameViewState extends State<ChooseGameView> {
|
||||
late int selectedGameIndex;
|
||||
|
||||
final TextEditingController searchBarController = TextEditingController();
|
||||
|
||||
@override
|
||||
@@ -30,6 +32,7 @@ class _ChooseGameViewState extends State<ChooseGameView> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final loc = AppLocalizations.of(context);
|
||||
return Scaffold(
|
||||
backgroundColor: CustomTheme.backgroundColor,
|
||||
appBar: AppBar(
|
||||
@@ -41,9 +44,9 @@ class _ChooseGameViewState extends State<ChooseGameView> {
|
||||
Navigator.of(context).pop(selectedGameIndex);
|
||||
},
|
||||
),
|
||||
title: const Text(
|
||||
'Choose Game',
|
||||
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
|
||||
title: Text(
|
||||
loc.choose_game,
|
||||
style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
|
||||
),
|
||||
centerTitle: true,
|
||||
),
|
||||
@@ -63,7 +66,7 @@ class _ChooseGameViewState extends State<ChooseGameView> {
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10),
|
||||
child: CustomSearchBar(
|
||||
controller: searchBarController,
|
||||
hintText: 'Game Name',
|
||||
hintText: loc.game_name,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 5),
|
||||
@@ -74,7 +77,10 @@ class _ChooseGameViewState extends State<ChooseGameView> {
|
||||
return TitleDescriptionListTile(
|
||||
title: widget.games[index].$1,
|
||||
description: widget.games[index].$2,
|
||||
badgeText: translateRulesetToString(widget.games[index].$3),
|
||||
badgeText: translateRulesetToString(
|
||||
widget.games[index].$3,
|
||||
context,
|
||||
),
|
||||
isHighlighted: selectedGameIndex == index,
|
||||
onPressed: () async {
|
||||
setState(() {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:game_tracker/core/custom_theme.dart';
|
||||
import 'package:game_tracker/data/dto/group.dart';
|
||||
import 'package:game_tracker/l10n/generated/app_localizations.dart';
|
||||
import 'package:game_tracker/presentation/widgets/text_input/custom_search_bar.dart';
|
||||
import 'package:game_tracker/presentation/widgets/tiles/group_tile.dart';
|
||||
import 'package:game_tracker/presentation/widgets/top_centered_message.dart';
|
||||
@@ -22,7 +23,6 @@ class ChooseGroupView extends StatefulWidget {
|
||||
class _ChooseGroupViewState extends State<ChooseGroupView> {
|
||||
late String selectedGroupId;
|
||||
final TextEditingController controller = TextEditingController();
|
||||
final String hintText = 'Group Name';
|
||||
late final List<Group> filteredGroups;
|
||||
|
||||
@override
|
||||
@@ -34,6 +34,7 @@ class _ChooseGroupViewState extends State<ChooseGroupView> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final loc = AppLocalizations.of(context);
|
||||
return Scaffold(
|
||||
backgroundColor: CustomTheme.backgroundColor,
|
||||
appBar: AppBar(
|
||||
@@ -51,9 +52,9 @@ class _ChooseGroupViewState extends State<ChooseGroupView> {
|
||||
);
|
||||
},
|
||||
),
|
||||
title: const Text(
|
||||
'Choose Group',
|
||||
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
|
||||
title: Text(
|
||||
loc.choose_group,
|
||||
style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
|
||||
),
|
||||
centerTitle: true,
|
||||
),
|
||||
@@ -79,7 +80,7 @@ class _ChooseGroupViewState extends State<ChooseGroupView> {
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10),
|
||||
child: CustomSearchBar(
|
||||
controller: controller,
|
||||
hintText: hintText,
|
||||
hintText: loc.search_for_groups,
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
filterGroups(value);
|
||||
@@ -92,15 +93,17 @@ class _ChooseGroupViewState extends State<ChooseGroupView> {
|
||||
visible: filteredGroups.isNotEmpty,
|
||||
replacement: Visibility(
|
||||
visible: widget.groups.isNotEmpty,
|
||||
replacement: const TopCenteredMessage(
|
||||
replacement: TopCenteredMessage(
|
||||
icon: Icons.info,
|
||||
title: 'Info',
|
||||
message: 'You have no groups created yet',
|
||||
title: loc.info,
|
||||
message: loc.no_groups_created_yet,
|
||||
),
|
||||
child: const TopCenteredMessage(
|
||||
child: TopCenteredMessage(
|
||||
icon: Icons.info,
|
||||
title: 'Info',
|
||||
message: 'There is no group matching your search',
|
||||
title: loc.info,
|
||||
message: AppLocalizations.of(
|
||||
context,
|
||||
).there_is_no_group_matching_your_search,
|
||||
),
|
||||
),
|
||||
child: ListView.builder(
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:game_tracker/core/custom_theme.dart';
|
||||
import 'package:game_tracker/core/enums.dart';
|
||||
import 'package:game_tracker/l10n/generated/app_localizations.dart';
|
||||
import 'package:game_tracker/presentation/widgets/tiles/title_description_list_tile.dart';
|
||||
|
||||
class ChooseRulesetView extends StatefulWidget {
|
||||
@@ -28,6 +29,7 @@ class _ChooseRulesetViewState extends State<ChooseRulesetView> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final loc = AppLocalizations.of(context);
|
||||
return DefaultTabController(
|
||||
length: 2,
|
||||
initialIndex: 0,
|
||||
@@ -46,9 +48,9 @@ class _ChooseRulesetViewState extends State<ChooseRulesetView> {
|
||||
);
|
||||
},
|
||||
),
|
||||
title: const Text(
|
||||
'Choose Ruleset',
|
||||
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
|
||||
title: Text(
|
||||
loc.choose_ruleset,
|
||||
style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
|
||||
),
|
||||
centerTitle: true,
|
||||
),
|
||||
@@ -80,7 +82,10 @@ class _ChooseRulesetViewState extends State<ChooseRulesetView> {
|
||||
}
|
||||
});
|
||||
},
|
||||
title: translateRulesetToString(widget.rulesets[index].$1),
|
||||
title: translateRulesetToString(
|
||||
widget.rulesets[index].$1,
|
||||
context,
|
||||
),
|
||||
description: widget.rulesets[index].$2,
|
||||
isHighlighted: selectedRulesetIndex == index,
|
||||
);
|
||||
|
||||
@@ -6,6 +6,7 @@ import 'package:game_tracker/data/db/database.dart';
|
||||
import 'package:game_tracker/data/dto/group.dart';
|
||||
import 'package:game_tracker/data/dto/match.dart';
|
||||
import 'package:game_tracker/data/dto/player.dart';
|
||||
import 'package:game_tracker/l10n/generated/app_localizations.dart';
|
||||
import 'package:game_tracker/presentation/views/main_menu/match_view/create_match/choose_game_view.dart';
|
||||
import 'package:game_tracker/presentation/views/main_menu/match_view/create_match/choose_group_view.dart';
|
||||
import 'package:game_tracker/presentation/views/main_menu/match_view/create_match/choose_ruleset_view.dart';
|
||||
@@ -67,34 +68,6 @@ class _CreateMatchViewState extends State<CreateMatchView> {
|
||||
/// The currently selected players
|
||||
List<Player>? selectedPlayers;
|
||||
|
||||
/// List of available rulesets with their descriptions
|
||||
/// as tuples of (Ruleset, String)
|
||||
/// TODO: Replace when rulesets are implemented
|
||||
List<(Ruleset, String)> rulesets = [
|
||||
(
|
||||
Ruleset.singleWinner,
|
||||
'Exactly one winner is chosen; ties are resolved by a predefined tiebreaker.',
|
||||
),
|
||||
(
|
||||
Ruleset.singleLoser,
|
||||
'Exactly one loser is determined; last place receives the penalty or consequence.',
|
||||
),
|
||||
(
|
||||
Ruleset.mostPoints,
|
||||
'Traditional ruleset: the player with the most points wins.',
|
||||
),
|
||||
(
|
||||
Ruleset.leastPoints,
|
||||
'Inverse scoring: the player with the fewest points wins.',
|
||||
),
|
||||
];
|
||||
|
||||
// TODO: Replace when games are implemented
|
||||
List<(String, String, Ruleset)> games = [
|
||||
('Example Game 1', 'This is a discription', Ruleset.leastPoints),
|
||||
('Example Game 2', '', Ruleset.singleWinner),
|
||||
];
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
@@ -116,16 +89,33 @@ class _CreateMatchViewState extends State<CreateMatchView> {
|
||||
});
|
||||
}
|
||||
|
||||
List<(Ruleset, String)> _getRulesets(BuildContext context) {
|
||||
final loc = AppLocalizations.of(context);
|
||||
return [
|
||||
(Ruleset.singleWinner, loc.ruleset_single_winner),
|
||||
(Ruleset.singleLoser, loc.ruleset_single_loser),
|
||||
|
sneeex marked this conversation as resolved
Outdated
flixcoo
commented
Warum Warum `_desc`?
|
||||
(Ruleset.mostPoints, loc.ruleset_most_points),
|
||||
(Ruleset.leastPoints, loc.ruleset_least_points),
|
||||
];
|
||||
}
|
||||
|
||||
// TODO: Replace when games are implemented
|
||||
List<(String, String, Ruleset)> games = [
|
||||
('Example Game 1', 'This is a description', Ruleset.leastPoints),
|
||||
('Example Game 2', '', Ruleset.singleWinner),
|
||||
];
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final loc = AppLocalizations.of(context);
|
||||
return Scaffold(
|
||||
backgroundColor: CustomTheme.backgroundColor,
|
||||
appBar: AppBar(
|
||||
backgroundColor: CustomTheme.backgroundColor,
|
||||
scrolledUnderElevation: 0,
|
||||
title: const Text(
|
||||
'Create new match',
|
||||
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
|
||||
title: Text(
|
||||
loc.create_new_match,
|
||||
style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
|
||||
),
|
||||
centerTitle: true,
|
||||
),
|
||||
@@ -141,9 +131,9 @@ class _CreateMatchViewState extends State<CreateMatchView> {
|
||||
),
|
||||
),
|
||||
ChooseTile(
|
||||
title: 'Game',
|
||||
title: loc.game,
|
||||
trailingText: selectedGameIndex == -1
|
||||
? 'None'
|
||||
? loc.none
|
||||
: games[selectedGameIndex].$1,
|
||||
onPressed: () async {
|
||||
selectedGameIndex = await Navigator.of(context).push(
|
||||
@@ -158,9 +148,9 @@ class _CreateMatchViewState extends State<CreateMatchView> {
|
||||
if (selectedGameIndex != -1) {
|
||||
hintText = games[selectedGameIndex].$1;
|
||||
selectedRuleset = games[selectedGameIndex].$3;
|
||||
selectedRulesetIndex = rulesets.indexWhere(
|
||||
(r) => r.$1 == selectedRuleset,
|
||||
);
|
||||
selectedRulesetIndex = _getRulesets(
|
||||
context,
|
||||
).indexWhere((r) => r.$1 == selectedRuleset);
|
||||
} else {
|
||||
hintText = 'Match Name';
|
||||
selectedRuleset = null;
|
||||
@@ -169,11 +159,12 @@ class _CreateMatchViewState extends State<CreateMatchView> {
|
||||
},
|
||||
),
|
||||
ChooseTile(
|
||||
title: 'Ruleset',
|
||||
title: loc.ruleset,
|
||||
trailingText: selectedRuleset == null
|
||||
? 'None'
|
||||
: translateRulesetToString(selectedRuleset!),
|
||||
? loc.none
|
||||
: translateRulesetToString(selectedRuleset!, context),
|
||||
onPressed: () async {
|
||||
final rulesets = _getRulesets(context);
|
||||
selectedRuleset = await Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (context) => ChooseRulesetView(
|
||||
@@ -182,6 +173,7 @@ class _CreateMatchViewState extends State<CreateMatchView> {
|
||||
),
|
||||
),
|
||||
);
|
||||
if (!mounted) return;
|
||||
selectedRulesetIndex = rulesets.indexWhere(
|
||||
(r) => r.$1 == selectedRuleset,
|
||||
);
|
||||
@@ -190,9 +182,9 @@ class _CreateMatchViewState extends State<CreateMatchView> {
|
||||
},
|
||||
),
|
||||
ChooseTile(
|
||||
title: 'Group',
|
||||
title: loc.group,
|
||||
trailingText: selectedGroup == null
|
||||
? 'None'
|
||||
? loc.none_group
|
||||
: selectedGroup!.name,
|
||||
onPressed: () async {
|
||||
selectedGroup = await Navigator.of(context).push(
|
||||
@@ -229,7 +221,7 @@ class _CreateMatchViewState extends State<CreateMatchView> {
|
||||
),
|
||||
),
|
||||
CustomWidthButton(
|
||||
text: 'Create match',
|
||||
text: loc.create_match,
|
||||
sizeRelativeToWidth: 0.95,
|
||||
buttonType: ButtonType.primary,
|
||||
onPressed: _enableCreateGameButton()
|
||||
|
||||
@@ -3,6 +3,7 @@ import 'package:game_tracker/core/custom_theme.dart';
|
||||
import 'package:game_tracker/data/db/database.dart';
|
||||
import 'package:game_tracker/data/dto/match.dart';
|
||||
import 'package:game_tracker/data/dto/player.dart';
|
||||
import 'package:game_tracker/l10n/generated/app_localizations.dart';
|
||||
import 'package:game_tracker/presentation/widgets/tiles/custom_radio_list_tile.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
@@ -35,6 +36,7 @@ class _MatchResultViewState extends State<MatchResultView> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final loc = AppLocalizations.of(context);
|
||||
return Scaffold(
|
||||
backgroundColor: CustomTheme.backgroundColor,
|
||||
appBar: AppBar(
|
||||
@@ -79,9 +81,9 @@ class _MatchResultViewState extends State<MatchResultView> {
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text(
|
||||
'Select Winner:',
|
||||
style: TextStyle(
|
||||
Text(
|
||||
loc.select_winner,
|
||||
style: const TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
|
||||
@@ -8,6 +8,7 @@ import 'package:game_tracker/data/db/database.dart';
|
||||
import 'package:game_tracker/data/dto/group.dart';
|
||||
import 'package:game_tracker/data/dto/match.dart';
|
||||
import 'package:game_tracker/data/dto/player.dart';
|
||||
import 'package:game_tracker/l10n/generated/app_localizations.dart';
|
||||
import 'package:game_tracker/presentation/views/main_menu/match_view/create_match/create_match_view.dart';
|
||||
import 'package:game_tracker/presentation/views/main_menu/match_view/match_result_view.dart';
|
||||
import 'package:game_tracker/presentation/widgets/app_skeleton.dart';
|
||||
@@ -43,12 +44,14 @@ class _MatchViewState extends State<MatchView> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
db = Provider.of<AppDatabase>(context, listen: false);
|
||||
loadGames();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final loc = AppLocalizations.of(context);
|
||||
return Scaffold(
|
||||
backgroundColor: CustomTheme.backgroundColor,
|
||||
body: Stack(
|
||||
@@ -58,11 +61,11 @@ class _MatchViewState extends State<MatchView> {
|
||||
enabled: isLoading,
|
||||
child: Visibility(
|
||||
visible: matches.isNotEmpty,
|
||||
replacement: const Center(
|
||||
replacement: Center(
|
||||
child: TopCenteredMessage(
|
||||
icon: Icons.report,
|
||||
title: 'Info',
|
||||
message: 'No games created yet',
|
||||
title: loc.info,
|
||||
message: loc.no_matches_created_yet,
|
||||
),
|
||||
),
|
||||
child: ListView.builder(
|
||||
@@ -96,7 +99,7 @@ class _MatchViewState extends State<MatchView> {
|
||||
Positioned(
|
||||
bottom: MediaQuery.paddingOf(context).bottom,
|
||||
child: CustomWidthButton(
|
||||
text: 'Create Match',
|
||||
text: loc.create_match,
|
||||
sizeRelativeToWidth: 0.90,
|
||||
onPressed: () async {
|
||||
Navigator.push(
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:game_tracker/core/custom_theme.dart';
|
||||
import 'package:game_tracker/core/enums.dart';
|
||||
import 'package:game_tracker/l10n/generated/app_localizations.dart';
|
||||
import 'package:game_tracker/presentation/widgets/tiles/settings_list_tile.dart';
|
||||
import 'package:game_tracker/services/data_transfer_service.dart';
|
||||
|
||||
@@ -12,8 +13,14 @@ class SettingsView extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _SettingsViewState extends State<SettingsView> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final loc = AppLocalizations.of(context);
|
||||
return Scaffold(
|
||||
appBar: AppBar(backgroundColor: CustomTheme.backgroundColor),
|
||||
backgroundColor: CustomTheme.backgroundColor,
|
||||
@@ -24,30 +31,33 @@ class _SettingsViewState extends State<SettingsView> {
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Padding(
|
||||
padding: EdgeInsets.fromLTRB(24, 0, 24, 10),
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(24, 0, 24, 10),
|
||||
child: Text(
|
||||
textAlign: TextAlign.start,
|
||||
'Menu',
|
||||
style: TextStyle(
|
||||
loc.menu,
|
||||
style: const TextStyle(
|
||||
fontSize: 28,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
const Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 24, vertical: 10),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 24,
|
||||
vertical: 10,
|
||||
),
|
||||
child: Text(
|
||||
textAlign: TextAlign.start,
|
||||
'Settings',
|
||||
style: TextStyle(
|
||||
loc.settings,
|
||||
style: const TextStyle(
|
||||
fontSize: 22,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
SettingsListTile(
|
||||
title: 'Export data',
|
||||
title: loc.export_data,
|
||||
icon: Icons.upload_outlined,
|
||||
suffixWidget: const Icon(Icons.arrow_forward_ios, size: 16),
|
||||
onPressed: () async {
|
||||
@@ -62,7 +72,7 @@ class _SettingsViewState extends State<SettingsView> {
|
||||
},
|
||||
),
|
||||
SettingsListTile(
|
||||
title: 'Import data',
|
||||
title: loc.import_data,
|
||||
icon: Icons.download_outlined,
|
||||
suffixWidget: const Icon(Icons.arrow_forward_ios, size: 16),
|
||||
onPressed: () async {
|
||||
@@ -74,23 +84,23 @@ class _SettingsViewState extends State<SettingsView> {
|
||||
},
|
||||
),
|
||||
SettingsListTile(
|
||||
title: 'Delete all data',
|
||||
title: loc.delete_all_data,
|
||||
icon: Icons.download_outlined,
|
||||
suffixWidget: const Icon(Icons.arrow_forward_ios, size: 16),
|
||||
onPressed: () {
|
||||
showDialog<bool>(
|
||||
context: context,
|
||||
|
flixcoo marked this conversation as resolved
Outdated
flixcoo
commented
Hier auch sowas wie Hier auch sowas wie `popup` oder so mit einbauen, dass klar wird, wozu dieser text ist
sneeex
commented
finde nicht, das kann man für verschiedene sachen nutzen und reicht so m. M. n. finde nicht, das kann man für verschiedene sachen nutzen und reicht so m. M. n.
|
||||
builder: (context) => AlertDialog(
|
||||
title: const Text('Delete all data?'),
|
||||
content: const Text('This can\'t be undone'),
|
||||
title: Text(loc.delete_all_data),
|
||||
content: Text(loc.this_cannot_be_undone),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(context).pop(false),
|
||||
child: const Text('Abbrechen'),
|
||||
child: Text(loc.cancel),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(context).pop(true),
|
||||
child: const Text('Löschen'),
|
||||
child: Text(loc.delete),
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -99,7 +109,9 @@ class _SettingsViewState extends State<SettingsView> {
|
||||
DataTransferService.deleteAllData(context);
|
||||
showSnackbar(
|
||||
context: context,
|
||||
message: 'Daten erfolgreich gelöscht',
|
||||
message: AppLocalizations.of(
|
||||
context,
|
||||
).data_successfully_deleted,
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -120,25 +132,20 @@ class _SettingsViewState extends State<SettingsView> {
|
||||
required BuildContext context,
|
||||
required ImportResult result,
|
||||
}) {
|
||||
final loc = AppLocalizations.of(context);
|
||||
switch (result) {
|
||||
case ImportResult.success:
|
||||
|
flixcoo marked this conversation as resolved
Outdated
flixcoo
commented
hier sowas wie hier sowas wie `snackbar`?
sneeex
commented
auch nicht relevant m. M. n. auch nicht relevant m. M. n.
|
||||
showSnackbar(context: context, message: 'Data successfully imported');
|
||||
showSnackbar(context: context, message: loc.data_successfully_imported);
|
||||
case ImportResult.invalidSchema:
|
||||
showSnackbar(context: context, message: 'Invalid Schema');
|
||||
showSnackbar(context: context, message: loc.invalid_schema);
|
||||
case ImportResult.fileReadError:
|
||||
showSnackbar(context: context, message: 'Error reading file');
|
||||
showSnackbar(context: context, message: loc.error_reading_file);
|
||||
case ImportResult.canceled:
|
||||
showSnackbar(context: context, message: 'Import canceled');
|
||||
showSnackbar(context: context, message: loc.import_canceled);
|
||||
case ImportResult.formatException:
|
||||
showSnackbar(
|
||||
context: context,
|
||||
message: 'Format Exception (see console)',
|
||||
);
|
||||
showSnackbar(context: context, message: loc.format_exception);
|
||||
case ImportResult.unknownException:
|
||||
showSnackbar(
|
||||
context: context,
|
||||
message: 'Unknown Exception (see console)',
|
||||
);
|
||||
showSnackbar(context: context, message: loc.unknown_exception);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,16 +157,14 @@ class _SettingsViewState extends State<SettingsView> {
|
||||
required BuildContext context,
|
||||
required ExportResult result,
|
||||
}) {
|
||||
final loc = AppLocalizations.of(context);
|
||||
switch (result) {
|
||||
case ExportResult.success:
|
||||
showSnackbar(context: context, message: 'Data successfully exported');
|
||||
showSnackbar(context: context, message: loc.data_successfully_exported);
|
||||
case ExportResult.canceled:
|
||||
showSnackbar(context: context, message: 'Export canceled');
|
||||
showSnackbar(context: context, message: loc.export_canceled);
|
||||
case ExportResult.unknownException:
|
||||
showSnackbar(
|
||||
context: context,
|
||||
message: 'Unknown Exception (see console)',
|
||||
);
|
||||
showSnackbar(context: context, message: loc.unknown_exception);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -175,6 +180,7 @@ class _SettingsViewState extends State<SettingsView> {
|
||||
Duration duration = const Duration(seconds: 3),
|
||||
VoidCallback? action,
|
||||
}) {
|
||||
final loc = AppLocalizations.of(context);
|
||||
final messenger = ScaffoldMessenger.of(context);
|
||||
messenger.hideCurrentSnackBar();
|
||||
messenger.showSnackBar(
|
||||
@@ -183,7 +189,7 @@ class _SettingsViewState extends State<SettingsView> {
|
||||
backgroundColor: CustomTheme.onBoxColor,
|
||||
duration: duration,
|
||||
action: action != null
|
||||
? SnackBarAction(label: 'Rückgängig', onPressed: action)
|
||||
? SnackBarAction(label: loc.undo, onPressed: action)
|
||||
: null,
|
||||
),
|
||||
);
|
||||
|
||||
@@ -3,6 +3,7 @@ import 'package:game_tracker/core/constants.dart';
|
||||
import 'package:game_tracker/data/db/database.dart';
|
||||
import 'package:game_tracker/data/dto/match.dart';
|
||||
import 'package:game_tracker/data/dto/player.dart';
|
||||
import 'package:game_tracker/l10n/generated/app_localizations.dart';
|
||||
import 'package:game_tracker/presentation/widgets/app_skeleton.dart';
|
||||
import 'package:game_tracker/presentation/widgets/tiles/statistics_tile.dart';
|
||||
import 'package:game_tracker/presentation/widgets/top_centered_message.dart';
|
||||
@@ -24,6 +25,7 @@ class _StatisticsViewState extends State<StatisticsView> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
final db = Provider.of<AppDatabase>(context, listen: false);
|
||||
|
||||
Future.wait([
|
||||
@@ -31,21 +33,25 @@ class _StatisticsViewState extends State<StatisticsView> {
|
||||
db.playerDao.getAllPlayers(),
|
||||
Future.delayed(minimumSkeletonDuration),
|
||||
]).then((results) async {
|
||||
if (!mounted) return;
|
||||
final matches = results[0] as List<Match>;
|
||||
final players = results[1] as List<Player>;
|
||||
winCounts = _calculateWinsForAllPlayers(matches, players);
|
||||
matchCounts = _calculateMatchAmountsForAllPlayers(matches, players);
|
||||
winCounts = _calculateWinsForAllPlayers(matches, players, context);
|
||||
matchCounts = _calculateMatchAmountsForAllPlayers(
|
||||
matches,
|
||||
players,
|
||||
context,
|
||||
);
|
||||
winRates = computeWinRatePercent(wins: winCounts, matches: matchCounts);
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
isLoading = false;
|
||||
});
|
||||
}
|
||||
setState(() {
|
||||
isLoading = false;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final loc = AppLocalizations.of(context);
|
||||
return LayoutBuilder(
|
||||
builder: (BuildContext context, BoxConstraints constraints) {
|
||||
return SingleChildScrollView(
|
||||
@@ -68,7 +74,7 @@ class _StatisticsViewState extends State<StatisticsView> {
|
||||
children: [
|
||||
StatisticsTile(
|
||||
icon: Icons.sports_score,
|
||||
title: 'Wins',
|
||||
title: loc.wins,
|
||||
width: constraints.maxWidth * 0.95,
|
||||
values: winCounts,
|
||||
itemCount: 3,
|
||||
@@ -77,7 +83,7 @@ class _StatisticsViewState extends State<StatisticsView> {
|
||||
SizedBox(height: constraints.maxHeight * 0.02),
|
||||
StatisticsTile(
|
||||
icon: Icons.percent,
|
||||
title: 'Winrate',
|
||||
title: loc.winrate,
|
||||
width: constraints.maxWidth * 0.95,
|
||||
values: winRates,
|
||||
itemCount: 5,
|
||||
@@ -86,7 +92,7 @@ class _StatisticsViewState extends State<StatisticsView> {
|
||||
SizedBox(height: constraints.maxHeight * 0.02),
|
||||
StatisticsTile(
|
||||
icon: Icons.casino,
|
||||
title: 'Amount of Matches',
|
||||
title: loc.amount_of_matches,
|
||||
width: constraints.maxWidth * 0.95,
|
||||
values: matchCounts,
|
||||
itemCount: 10,
|
||||
@@ -94,10 +100,12 @@ class _StatisticsViewState extends State<StatisticsView> {
|
||||
),
|
||||
],
|
||||
),
|
||||
child: const TopCenteredMessage(
|
||||
child: TopCenteredMessage(
|
||||
icon: Icons.info,
|
||||
title: 'Info',
|
||||
message: 'No statistics available',
|
||||
title: loc.info,
|
||||
message: AppLocalizations.of(
|
||||
context,
|
||||
).no_statistics_available,
|
||||
),
|
||||
),
|
||||
SizedBox(height: MediaQuery.paddingOf(context).bottom),
|
||||
@@ -115,8 +123,10 @@ class _StatisticsViewState extends State<StatisticsView> {
|
||||
List<(String, int)> _calculateWinsForAllPlayers(
|
||||
List<Match> matches,
|
||||
List<Player> players,
|
||||
BuildContext context,
|
||||
) {
|
||||
List<(String, int)> winCounts = [];
|
||||
final loc = AppLocalizations.of(context);
|
||||
|
||||
// Getting the winners
|
||||
for (var match in matches) {
|
||||
@@ -147,7 +157,7 @@ class _StatisticsViewState extends State<StatisticsView> {
|
||||
final playerId = winCounts[i].$1;
|
||||
final player = players.firstWhere(
|
||||
(p) => p.id == playerId,
|
||||
orElse: () => Player(id: playerId, name: 'N.a.'),
|
||||
orElse: () => Player(id: playerId, name: loc.not_available),
|
||||
);
|
||||
winCounts[i] = (player.name, winCounts[i].$2);
|
||||
}
|
||||
@@ -162,8 +172,10 @@ class _StatisticsViewState extends State<StatisticsView> {
|
||||
List<(String, int)> _calculateMatchAmountsForAllPlayers(
|
||||
List<Match> matches,
|
||||
List<Player> players,
|
||||
BuildContext context,
|
||||
) {
|
||||
List<(String, int)> matchCounts = [];
|
||||
final loc = AppLocalizations.of(context);
|
||||
|
||||
// Counting matches for each player
|
||||
for (var match in matches) {
|
||||
@@ -209,7 +221,7 @@ class _StatisticsViewState extends State<StatisticsView> {
|
||||
final playerId = matchCounts[i].$1;
|
||||
final player = players.firstWhere(
|
||||
(p) => p.id == playerId,
|
||||
orElse: () => Player(id: playerId, name: 'N.a.'),
|
||||
orElse: () => Player(id: playerId, name: loc.not_available),
|
||||
);
|
||||
matchCounts[i] = (player.name, matchCounts[i].$2);
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ import 'package:game_tracker/core/constants.dart';
|
||||
import 'package:game_tracker/core/custom_theme.dart';
|
||||
import 'package:game_tracker/data/db/database.dart';
|
||||
import 'package:game_tracker/data/dto/player.dart';
|
||||
import 'package:game_tracker/l10n/generated/app_localizations.dart';
|
||||
import 'package:game_tracker/presentation/widgets/app_skeleton.dart';
|
||||
import 'package:game_tracker/presentation/widgets/text_input/custom_search_bar.dart';
|
||||
import 'package:game_tracker/presentation/widgets/tiles/text_icon_list_tile.dart';
|
||||
@@ -86,6 +87,7 @@ class _PlayerSelectionState extends State<PlayerSelection> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final loc = AppLocalizations.of(context);
|
||||
return Container(
|
||||
margin: const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
|
||||
padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 10),
|
||||
@@ -96,7 +98,7 @@ class _PlayerSelectionState extends State<PlayerSelection> {
|
||||
CustomSearchBar(
|
||||
controller: _searchBarController,
|
||||
constraints: const BoxConstraints(maxHeight: 45, minHeight: 45),
|
||||
hintText: 'Search for players',
|
||||
hintText: loc.search_for_players,
|
||||
trailingButtonShown: true,
|
||||
trailingButtonicon: Icons.add_circle,
|
||||
trailingButtonEnabled: _searchBarController.text.trim().isNotEmpty,
|
||||
@@ -129,14 +131,16 @@ class _PlayerSelectionState extends State<PlayerSelection> {
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Text(
|
||||
'Selected players: (${selectedPlayers.length})',
|
||||
AppLocalizations.of(
|
||||
context,
|
||||
).selected_players(selectedPlayers.length),
|
||||
style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
SizedBox(
|
||||
height: 50,
|
||||
child: selectedPlayers.isEmpty
|
||||
? const Center(child: Text('No players selected'))
|
||||
? Center(child: Text(loc.no_players_selected))
|
||||
: SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: Row(
|
||||
@@ -177,9 +181,9 @@ class _PlayerSelectionState extends State<PlayerSelection> {
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
const Text(
|
||||
'All players:',
|
||||
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
|
||||
Text(
|
||||
loc.all_players,
|
||||
style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Expanded(
|
||||
@@ -189,8 +193,8 @@ class _PlayerSelectionState extends State<PlayerSelection> {
|
||||
visible: suggestedPlayers.isNotEmpty,
|
||||
replacement: TopCenteredMessage(
|
||||
icon: Icons.info,
|
||||
title: 'Info',
|
||||
message: _getInfoText(),
|
||||
title: loc.info,
|
||||
message: _getInfoText(context),
|
||||
),
|
||||
child: ListView.builder(
|
||||
itemCount: suggestedPlayers.length,
|
||||
@@ -227,6 +231,7 @@ class _PlayerSelectionState extends State<PlayerSelection> {
|
||||
/// Shows a snackbar indicating success or failure.
|
||||
/// [context] - BuildContext to show the snackbar.
|
||||
void addNewPlayerFromSearch({required BuildContext context}) async {
|
||||
final loc = AppLocalizations.of(context);
|
||||
String playerName = _searchBarController.text.trim();
|
||||
Player createdPlayer = Player(name: playerName);
|
||||
bool success = await db.playerDao.addPlayer(player: createdPlayer);
|
||||
@@ -246,7 +251,9 @@ class _PlayerSelectionState extends State<PlayerSelection> {
|
||||
backgroundColor: CustomTheme.boxColor,
|
||||
content: Center(
|
||||
child: Text(
|
||||
'Successfully added player $playerName.',
|
||||
AppLocalizations.of(
|
||||
context,
|
||||
).successfully_added_player(playerName),
|
||||
style: const TextStyle(color: Colors.white),
|
||||
),
|
||||
),
|
||||
@@ -258,7 +265,7 @@ class _PlayerSelectionState extends State<PlayerSelection> {
|
||||
backgroundColor: CustomTheme.boxColor,
|
||||
content: Center(
|
||||
child: Text(
|
||||
'Could not add player $playerName.',
|
||||
loc.could_not_add_player(playerName),
|
||||
style: const TextStyle(color: Colors.white),
|
||||
),
|
||||
),
|
||||
@@ -269,18 +276,19 @@ class _PlayerSelectionState extends State<PlayerSelection> {
|
||||
|
||||
/// Determines the appropriate info text to display when no players
|
||||
/// are available in the suggested players list.
|
||||
String _getInfoText() {
|
||||
String _getInfoText(BuildContext context) {
|
||||
final loc = AppLocalizations.of(context);
|
||||
if (allPlayers.isEmpty) {
|
||||
// No players exist in the database
|
||||
return 'No players created yet';
|
||||
return loc.no_players_created_yet;
|
||||
} else if (selectedPlayers.length == allPlayers.length ||
|
||||
widget.availablePlayers?.isEmpty == true) {
|
||||
// All players have been selected or
|
||||
// available players list is provided but empty
|
||||
return 'No more players to add';
|
||||
return loc.all_players_selected;
|
||||
} else {
|
||||
// No players match the search query
|
||||
return 'No players found with that name';
|
||||
return loc.no_players_found_with_that_name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:game_tracker/core/custom_theme.dart';
|
||||
import 'package:game_tracker/data/dto/match.dart';
|
||||
import 'package:game_tracker/l10n/generated/app_localizations.dart';
|
||||
import 'package:game_tracker/presentation/widgets/tiles/text_icon_tile.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
@@ -20,6 +21,7 @@ class _MatchTileState extends State<MatchTile> {
|
||||
final group = widget.match.group;
|
||||
final winner = widget.match.winner;
|
||||
final allPlayers = _getAllPlayers();
|
||||
final loc = AppLocalizations.of(context);
|
||||
|
||||
return GestureDetector(
|
||||
onTap: widget.onTap,
|
||||
@@ -48,7 +50,7 @@ class _MatchTileState extends State<MatchTile> {
|
||||
),
|
||||
),
|
||||
Text(
|
||||
_formatDate(widget.match.createdAt),
|
||||
_formatDate(widget.match.createdAt, context),
|
||||
style: const TextStyle(fontSize: 12, color: Colors.grey),
|
||||
),
|
||||
],
|
||||
@@ -97,7 +99,7 @@ class _MatchTileState extends State<MatchTile> {
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: Text(
|
||||
'Winner: ${winner.name}',
|
||||
'${loc.winner}: ${winner.name}',
|
||||
style: const TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w600,
|
||||
@@ -113,9 +115,9 @@ class _MatchTileState extends State<MatchTile> {
|
||||
],
|
||||
|
||||
if (allPlayers.isNotEmpty) ...[
|
||||
const Text(
|
||||
'Players',
|
||||
style: TextStyle(
|
||||
Text(
|
||||
loc.players,
|
||||
style: const TextStyle(
|
||||
fontSize: 13,
|
||||
color: Colors.grey,
|
||||
fontWeight: FontWeight.w500,
|
||||
@@ -136,16 +138,21 @@ class _MatchTileState extends State<MatchTile> {
|
||||
);
|
||||
}
|
||||
|
||||
String _formatDate(DateTime dateTime) {
|
||||
String _formatDate(DateTime dateTime, BuildContext context) {
|
||||
final now = DateTime.now();
|
||||
final difference = now.difference(dateTime);
|
||||
final loc = AppLocalizations.of(context);
|
||||
|
||||
if (difference.inDays == 0) {
|
||||
return 'Today at ${DateFormat('HH:mm').format(dateTime)}';
|
||||
return AppLocalizations.of(
|
||||
context,
|
||||
).today_at(DateFormat('HH:mm').format(dateTime));
|
||||
} else if (difference.inDays == 1) {
|
||||
return 'Yesterday at ${DateFormat('HH:mm').format(dateTime)}';
|
||||
return AppLocalizations.of(
|
||||
context,
|
||||
).yesterday_at(DateFormat('HH:mm').format(dateTime));
|
||||
} else if (difference.inDays < 7) {
|
||||
return '${difference.inDays} days ago';
|
||||
return loc.days_ago(difference.inDays);
|
||||
} else {
|
||||
return DateFormat('MMM d, yyyy').format(dateTime);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:game_tracker/l10n/generated/app_localizations.dart';
|
||||
import 'package:game_tracker/presentation/widgets/tiles/info_tile.dart';
|
||||
|
||||
class StatisticsTile extends StatelessWidget {
|
||||
@@ -24,6 +25,7 @@ class StatisticsTile extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final maxBarWidth = MediaQuery.of(context).size.width * 0.65;
|
||||
final loc = AppLocalizations.of(context);
|
||||
|
||||
return InfoTile(
|
||||
width: width,
|
||||
@@ -33,9 +35,9 @@ class StatisticsTile extends StatelessWidget {
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20.0),
|
||||
child: Visibility(
|
||||
visible: values.isNotEmpty,
|
||||
replacement: const Center(
|
||||
replacement: Center(
|
||||
heightFactor: 4,
|
||||
child: Text('No data available.'),
|
||||
child: Text(loc.no_data_available),
|
||||
),
|
||||
child: Column(
|
||||
children: List.generate(min(values.length, itemCount), (index) {
|
||||
|
||||
@@ -54,7 +54,7 @@ class TitleDescriptionListTile extends StatelessWidget {
|
||||
if (badgeText != null) ...[
|
||||
const Spacer(),
|
||||
Container(
|
||||
constraints: const BoxConstraints(maxWidth: 100),
|
||||
constraints: const BoxConstraints(maxWidth: 115),
|
||||
margin: const EdgeInsets.only(top: 4),
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 2,
|
||||
|
||||
@@ -24,7 +24,9 @@ dependencies:
|
||||
json_schema: ^5.2.2
|
||||
file_saver: ^0.3.1
|
||||
clock: ^1.1.2
|
||||
intl: ^0.18.0
|
||||
intl: any
|
||||
flutter_localizations:
|
||||
sdk: flutter
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
@@ -35,5 +37,6 @@ dev_dependencies:
|
||||
|
||||
flutter:
|
||||
uses-material-design: true
|
||||
generate: true
|
||||
assets:
|
||||
- assets/schema.json
|
||||
|
||||
Datei alphabetisch sortieren