diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 608f6c2..ffa989f 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -59,7 +59,7 @@ jobs: prerelease: true allowUpdates: true discussionCategory: autobuilded-releases - artifacts: "build/app/outputs/apk/debug/*" + artifacts: "build/app/outputs/apk/flutter-apk/*" tag: Auto-${{ github.run_number }} body: This build was builded with GitHub Action workflow token: ${{ secrets.TOKEN }} \ No newline at end of file diff --git a/lib/gen/strings.g.dart b/lib/gen/strings.g.dart index c12c794..664ffb1 100644 --- a/lib/gen/strings.g.dart +++ b/lib/gen/strings.g.dart @@ -1,9 +1,9 @@ /// Generated file. Do not edit. /// /// Locales: 2 -/// Strings: 680 (340 per locale) +/// Strings: 788 (394 per locale) /// -/// Built on 2023-07-11 at 16:43 UTC +/// Built on 2023-07-12 at 15:02 UTC // coverage:ignore-file // ignore_for_file: type=lint @@ -193,6 +193,37 @@ class _StringsEn implements BaseTranslations { String gamesUntilRanked({required Object left}) => '${left} games until being ranked'; String get nerdStats => 'Nerd Stats'; String get playersYouTrack => 'Players you track'; + String get formula => 'Formula'; + String get exactValue => 'Exact value'; + String get neverPlayedTL => 'That user never played Tetra League'; + String get exportDB => 'Export local database'; + String get exportDBDescription => 'It contains states and Tetra League records of the tracked players and list of tracked players.'; + String get desktopExportAlertTitle => 'Desktop export'; + String get desktopExportText => 'It seems like you using this app on desktop. Check your documents folder, you should find "TetraStats.db". Copy it somewhere'; + String get androidExportAlertTitle => 'Android export'; + String androidExportText({required Object exportedDB}) => 'Exported.\n${exportedDB}'; + String get importDB => 'Import local database'; + String get importDBDescription => 'Restore your backup. Notice that already stored database will be overwritten.'; + String get importWrongFileType => 'Wrong file type'; + String get importCancelled => 'Operation was cancelled'; + String get importSuccess => 'Import successful'; + String get yourID => 'Your TETR.IO account'; + String get yourIDAlertTitle => 'Your TETR.IO account nickname or ID'; + String get yourIDText => 'Every time when app loads, stats of that player will be fetched. Please prefer ID over nickname because nickname can be changed.'; + String get language => 'Language'; + String get aboutApp => 'About app'; + String aboutAppText({required Object appName, required Object packageName, required Object version, required Object buildNumber}) => '${appName} (${packageName}) Version ${version} Build ${buildNumber}\n\nDeveloped by dan63047\nFormulas provided by kerrmunism'; + String stateViewTitle({required Object nickname, required Object date}) => '${nickname} account on ${date}'; + String statesViewTitle({required Object number, required Object nickname}) => '${number} states of ${nickname} account'; + String statesViewEntry({required Object level, required Object gameTime, required Object friends, required Object rd}) => 'Level ${level}, ${gameTime} of gametime, ${friends} friends, ${rd} RD'; + String stateRemoved({required Object date}) => '${date} state was removed from database!'; + String get trackedPlayersViewTitle => 'Stored data'; + String get trackedPlayersZeroEntrys => 'Empty list. Press "Track" button in previous view to add current player here'; + String get trackedPlayersOneEntry => 'There is only one player'; + String trackedPlayersManyEntrys({required Object numberOfPlayers}) => 'There are ${numberOfPlayers} players'; + String trackedPlayersEntry({required Object nickname, required Object numberOfStates}) => '${nickname}: ${numberOfStates} states'; + String trackedPlayersDescription({required Object firstStateDate, required Object lastStateDate}) => 'From ${firstStateDate} until ${lastStateDate}'; + String trackedPlayersStatesDeleted({required Object nickname}) => '${nickname} states was removed from database!'; late final _StringsStatCellNumEn statCellNum = _StringsStatCellNumEn._(_root); Map get playerRole => { 'user': 'User', @@ -477,6 +508,8 @@ class _StringsStatCellNumEn { // Translations String get xpLevel => 'XP Level'; + String get xpProgress => 'Progress to next level'; + String get xpFrom0To5000 => 'Progress from 0 XP to level 5000'; String get hoursPlayed => 'Hours\nPlayed'; String get onlineGames => 'Online\nGames'; String get gamesWon => 'Games\nWon'; @@ -498,6 +531,25 @@ class _StringsStatCellNumEn { String get keys => 'Key\nPresses'; String get kpp => 'KP Per\nPiece'; String get kps => 'KP Per\nSecond'; + String get app => 'Attack Per Piece'; + String get appDescription => '(Abbreviated as APP) Main efficiency metric. Tells how many attack you producing per piece'; + String get vsapmDescription => 'Basically, tells how much and how efficient you using garbage in your attacks'; + String get dss => 'Downstack\nPer Second'; + String get dssDescription => 'Downstack per Second measures how many garbage lines you clear in a second.'; + String get dsp => 'Downstack\nPer Piece'; + String get dspDescription => 'Downstack per Piece measures how many garbage lines you clear per piece.'; + String get appdsp => 'APP + DS/P'; + String get appdspDescription => 'Just a sum of Attack per Piece and Downstack per Piece.'; + String get cheese => 'Cheese\nIndex'; + String get cheeseDescription => 'Cheese Index is an approximation how much clean / cheese garbage player sends. Lower = more clean. Higher = more cheese.\nInvented by kerrmunism'; + String get gbe => 'Garbage\nEfficiency'; + String get gbeDescription => 'Garbage Efficiency measures how well player uses their garbage. Higher = better or they use their garbage more. Lower = they mostly send their garbage back at cheese or rarely clear garbage.\nInvented by Zepheniah and Dragonboy.'; + String get nyaapp => 'Weighted\nAPP'; + String get nyaappDescription => 'Essentially, a measure of your ability to send cheese while still maintaining a high APP.\nInvented by Wertj.'; + String get area => 'Area'; + String get areaDescription => 'How much space your shape takes up on the graph, if you exclude the cheese and vs/apm sections'; + String get estOfTR => 'Est. of TR'; + String get accOfEst => 'Accuracy'; } // Path: numOfGameActions @@ -520,6 +572,8 @@ class _StringsPopupActionsEn { final _StringsEn _root; // ignore: unused_field // Translations + String get cancel => 'Cancel'; + String get submit => 'Submit'; String get ok => 'OK'; } @@ -606,6 +660,37 @@ class _StringsRu implements _StringsEn { @override String gamesUntilRanked({required Object left}) => '${left} матчей до получения рейтинга'; @override String get nerdStats => 'Для задротов'; @override String get playersYouTrack => 'Отслеживаемые игроки'; + @override String get formula => 'Формула'; + @override String get exactValue => 'Точное значение'; + @override String get neverPlayedTL => 'Этот игрок никогда не играл в Тетра Лигу'; + @override String get exportDB => 'Экспортировать локальную базу данных'; + @override String get exportDBDescription => 'Она содержит состояния аккаунтов и их матчей в Тетра Лиге для отслеживаемых игроков и список таких игроков.'; + @override String get desktopExportAlertTitle => 'Экспорт на десктопе'; + @override String get desktopExportText => 'Похоже, вы используете десктопную версию. Проверьте папку "Документы", там вы должны найти файл "TetraStats.db". Скопируйте его куда-нибудь'; + @override String get androidExportAlertTitle => 'Экспорт на Android'; + @override String androidExportText({required Object exportedDB}) => 'Экспортировано.\n${exportedDB}'; + @override String get importDB => 'Импортировать локальную базу данных'; + @override String get importDBDescription => 'Восстановите свою резеврную копию. Обратите внимание, что текущая база данных будет перезаписана.'; + @override String get importWrongFileType => 'Неверный тип файла'; + @override String get importCancelled => 'Операция была отменена'; + @override String get importSuccess => 'Успешно импортировано'; + @override String get yourID => 'Ваш аккаунт в TETR.IO'; + @override String get yourIDAlertTitle => 'Никнейм или ID вашего аккаунта в TETR.IO'; + @override String get yourIDText => 'Каждый раз, когда приложение запускается, приложение будет получать статистику этого игрока. Пожалуйста, отдайте предпочтение ID, так как никнейм можно изменить.'; + @override String get language => 'Язык (Language)'; + @override String get aboutApp => 'О приложении'; + @override String aboutAppText({required Object appName, required Object packageName, required Object version, required Object buildNumber}) => '${appName} (${packageName}) Версия ${version} Сборка ${buildNumber}\n\nРазработал dan63047\nФормулы предоставил kerrmunism'; + @override String stateViewTitle({required Object nickname, required Object date}) => 'Аккаунт ${nickname} ${date}'; + @override String statesViewTitle({required Object number, required Object nickname}) => '${number} состояний аккаунта ${nickname}'; + @override String statesViewEntry({required Object level, required Object gameTime, required Object friends, required Object rd}) => '${level} уровень, ${gameTime} сыграно, ${friends} друзей, ${rd} RD'; + @override String stateRemoved({required Object }) => '${}Состояние от {date} было удалено из локальной базы данных!'; + @override String get trackedPlayersViewTitle => 'Сохранённые данные'; + @override String get trackedPlayersZeroEntrys => 'Пустой список. Вернитесь на предыдущий экран и нажмите кнопку "Отслеживать", чтобы текущий игрок появился здесь'; + @override String get trackedPlayersOneEntry => 'В списке только один игрок'; + @override String trackedPlayersManyEntrys({required Object numberOfPlayers}) => 'В списке ${numberOfPlayers} игроков'; + @override String trackedPlayersEntry({required Object nickname, required Object numberOfStates}) => '${nickname}: ${numberOfStates} состояний'; + @override String trackedPlayersDescription({required Object firstStateDate, required Object lastStateDate}) => 'Начиная с ${firstStateDate} и заканчивая ${lastStateDate}'; + @override String trackedPlayersStatesDeleted({required Object nickname}) => 'Состояния аккаунта ${nickname} были удалены из локальной базы данных!'; @override late final _StringsStatCellNumRu statCellNum = _StringsStatCellNumRu._(_root); @override Map get playerRole => { 'user': 'Пользователь', @@ -890,6 +975,8 @@ class _StringsStatCellNumRu implements _StringsStatCellNumEn { // Translations @override String get xpLevel => 'Уровень\nопыта'; + @override String get xpProgress => 'Прогресс до следующего уровня'; + @override String get xpFrom0To5000 => 'Прогресс от 0 XP до 5000 уровня'; @override String get hoursPlayed => 'Часов\nСыграно'; @override String get onlineGames => 'Онлайн\nИгр'; @override String get gamesWon => 'Онлайн\nПобед'; @@ -911,6 +998,25 @@ class _StringsStatCellNumRu implements _StringsStatCellNumEn { @override String get keys => 'Нажатий\nКлавиш'; @override String get kpp => 'Нажатий\nна Фигуру'; @override String get kps => 'Нажатий\nв Секунду'; + @override String get app => 'Атака на Фигуру'; + @override String get appDescription => '(Сокращенно APP) Главный показатель эффективности. Показывает, сколько атаки приходится на одну фигуру'; + @override String get vsapmDescription => 'В основном, показывает как много мусора игрок использует в своих атаках и насколько эффективно.'; + @override String get dss => 'Downstack\nв Секунду'; + @override String get dssDescription => '(Сокращенно DS/S) Downstack (спуск вниз) в Секунду показывает как много мусорных линий в среднем игрок убирает за одну секунду.'; + @override String get dsp => 'Downstack\nна Фигуру'; + @override String get dspDescription => '(Сокращенно DS/P) Downstack (спуск вниз) на Фигуру показывает как много мусорных линий в среднем игрок убирает одну фигуру.'; + @override String get appdsp => 'APP + DS/P'; + @override String get appdspDescription => 'Просто сумма Атаки на Фигуру и Downstack на Фигуру.'; + @override String get cheese => 'Индекс сыра'; + @override String get cheeseDescription => '(Сокращенно Cheese) Индекс сыра является аппроксимацией того, насколько чистый / дырявый мусор игрок отправляет. Меньше = более чистый. Больше = более дырявый.\nПридумал kerrmunism'; + @override String get gbe => 'Garbage\nEfficiency'; + @override String get gbeDescription => '(Сокращенно Gb Eff.) Garbage Efficiency показывает насколько хорошо игрок использует свой мусор. Больше = лучше (или он использует больше мусора). Меньше = в основном отправляют сыр (или он редко чистит мусор).\nПридумали Zepheniah и Dragonboy.'; + @override String get nyaapp => 'Взвешенный\nAPP'; + @override String get nyaappDescription => '(Сокращенно wAPP) По сути, показывает способность отправлять сыр, сохраняя при этом высокую эффективность.\nПридумал Wertj.'; + @override String get area => 'Area'; + @override String get areaDescription => 'Какую площадь занимает диаграмма, если не брать в расчёт индекс сыра и VS/APM'; + @override String get estOfTR => 'Расчётный TR'; + @override String get accOfEst => 'Точность расчёта'; } // Path: numOfGameActions @@ -933,6 +1039,8 @@ class _StringsPopupActionsRu implements _StringsPopupActionsEn { @override final _StringsRu _root; // ignore: unused_field // Translations + @override String get cancel => 'Отменить'; + @override String get submit => 'Подтвердить'; @override String get ok => 'OK'; } @@ -998,7 +1106,40 @@ extension on _StringsEn { case 'gamesUntilRanked': return ({required Object left}) => '${left} games until being ranked'; case 'nerdStats': return 'Nerd Stats'; case 'playersYouTrack': return 'Players you track'; + case 'formula': return 'Formula'; + case 'exactValue': return 'Exact value'; + case 'neverPlayedTL': return 'That user never played Tetra League'; + case 'exportDB': return 'Export local database'; + case 'exportDBDescription': return 'It contains states and Tetra League records of the tracked players and list of tracked players.'; + case 'desktopExportAlertTitle': return 'Desktop export'; + case 'desktopExportText': return 'It seems like you using this app on desktop. Check your documents folder, you should find "TetraStats.db". Copy it somewhere'; + case 'androidExportAlertTitle': return 'Android export'; + case 'androidExportText': return ({required Object exportedDB}) => 'Exported.\n${exportedDB}'; + case 'importDB': return 'Import local database'; + case 'importDBDescription': return 'Restore your backup. Notice that already stored database will be overwritten.'; + case 'importWrongFileType': return 'Wrong file type'; + case 'importCancelled': return 'Operation was cancelled'; + case 'importSuccess': return 'Import successful'; + case 'yourID': return 'Your TETR.IO account'; + case 'yourIDAlertTitle': return 'Your TETR.IO account nickname or ID'; + case 'yourIDText': return 'Every time when app loads, stats of that player will be fetched. Please prefer ID over nickname because nickname can be changed.'; + case 'language': return 'Language'; + case 'aboutApp': return 'About app'; + case 'aboutAppText': return ({required Object appName, required Object packageName, required Object version, required Object buildNumber}) => '${appName} (${packageName}) Version ${version} Build ${buildNumber}\n\nDeveloped by dan63047\nFormulas provided by kerrmunism'; + case 'stateViewTitle': return ({required Object nickname, required Object date}) => '${nickname} account on ${date}'; + case 'statesViewTitle': return ({required Object number, required Object nickname}) => '${number} states of ${nickname} account'; + case 'statesViewEntry': return ({required Object level, required Object gameTime, required Object friends, required Object rd}) => 'Level ${level}, ${gameTime} of gametime, ${friends} friends, ${rd} RD'; + case 'stateRemoved': return ({required Object date}) => '${date} state was removed from database!'; + case 'trackedPlayersViewTitle': return 'Stored data'; + case 'trackedPlayersZeroEntrys': return 'Empty list. Press "Track" button in previous view to add current player here'; + case 'trackedPlayersOneEntry': return 'There is only one player'; + case 'trackedPlayersManyEntrys': return ({required Object numberOfPlayers}) => 'There are ${numberOfPlayers} players'; + case 'trackedPlayersEntry': return ({required Object nickname, required Object numberOfStates}) => '${nickname}: ${numberOfStates} states'; + case 'trackedPlayersDescription': return ({required Object firstStateDate, required Object lastStateDate}) => 'From ${firstStateDate} until ${lastStateDate}'; + case 'trackedPlayersStatesDeleted': return ({required Object nickname}) => '${nickname} states was removed from database!'; case 'statCellNum.xpLevel': return 'XP Level'; + case 'statCellNum.xpProgress': return 'Progress to next level'; + case 'statCellNum.xpFrom0To5000': return 'Progress from 0 XP to level 5000'; case 'statCellNum.hoursPlayed': return 'Hours\nPlayed'; case 'statCellNum.onlineGames': return 'Online\nGames'; case 'statCellNum.gamesWon': return 'Games\nWon'; @@ -1020,6 +1161,25 @@ extension on _StringsEn { case 'statCellNum.keys': return 'Key\nPresses'; case 'statCellNum.kpp': return 'KP Per\nPiece'; case 'statCellNum.kps': return 'KP Per\nSecond'; + case 'statCellNum.app': return 'Attack Per Piece'; + case 'statCellNum.appDescription': return '(Abbreviated as APP) Main efficiency metric. Tells how many attack you producing per piece'; + case 'statCellNum.vsapmDescription': return 'Basically, tells how much and how efficient you using garbage in your attacks'; + case 'statCellNum.dss': return 'Downstack\nPer Second'; + case 'statCellNum.dssDescription': return 'Downstack per Second measures how many garbage lines you clear in a second.'; + case 'statCellNum.dsp': return 'Downstack\nPer Piece'; + case 'statCellNum.dspDescription': return 'Downstack per Piece measures how many garbage lines you clear per piece.'; + case 'statCellNum.appdsp': return 'APP + DS/P'; + case 'statCellNum.appdspDescription': return 'Just a sum of Attack per Piece and Downstack per Piece.'; + case 'statCellNum.cheese': return 'Cheese\nIndex'; + case 'statCellNum.cheeseDescription': return 'Cheese Index is an approximation how much clean / cheese garbage player sends. Lower = more clean. Higher = more cheese.\nInvented by kerrmunism'; + case 'statCellNum.gbe': return 'Garbage\nEfficiency'; + case 'statCellNum.gbeDescription': return 'Garbage Efficiency measures how well player uses their garbage. Higher = better or they use their garbage more. Lower = they mostly send their garbage back at cheese or rarely clear garbage.\nInvented by Zepheniah and Dragonboy.'; + case 'statCellNum.nyaapp': return 'Weighted\nAPP'; + case 'statCellNum.nyaappDescription': return 'Essentially, a measure of your ability to send cheese while still maintaining a high APP.\nInvented by Wertj.'; + case 'statCellNum.area': return 'Area'; + case 'statCellNum.areaDescription': return 'How much space your shape takes up on the graph, if you exclude the cheese and vs/apm sections'; + case 'statCellNum.estOfTR': return 'Est. of TR'; + case 'statCellNum.accOfEst': return 'Accuracy'; case 'playerRole.user': return 'User'; case 'playerRole.banned': return 'Banned'; case 'playerRole.bot': return 'Bot'; @@ -1032,6 +1192,8 @@ extension on _StringsEn { case 'numOfGameActions.hold': return 'Holds'; case 'numOfGameActions.tspinsTotal': return 'T-spins total'; case 'numOfGameActions.lineClears': return 'Line clears'; + case 'popupActions.cancel': return 'Cancel'; + case 'popupActions.submit': return 'Submit'; case 'popupActions.ok': return 'OK'; case 'errors.connection': return ({required Object code, required Object message}) => 'Some issue with connection: ${code} ${message}'; case 'errors.noSuchUser': return 'No such user'; @@ -1346,7 +1508,40 @@ extension on _StringsRu { case 'gamesUntilRanked': return ({required Object left}) => '${left} матчей до получения рейтинга'; case 'nerdStats': return 'Для задротов'; case 'playersYouTrack': return 'Отслеживаемые игроки'; + case 'formula': return 'Формула'; + case 'exactValue': return 'Точное значение'; + case 'neverPlayedTL': return 'Этот игрок никогда не играл в Тетра Лигу'; + case 'exportDB': return 'Экспортировать локальную базу данных'; + case 'exportDBDescription': return 'Она содержит состояния аккаунтов и их матчей в Тетра Лиге для отслеживаемых игроков и список таких игроков.'; + case 'desktopExportAlertTitle': return 'Экспорт на десктопе'; + case 'desktopExportText': return 'Похоже, вы используете десктопную версию. Проверьте папку "Документы", там вы должны найти файл "TetraStats.db". Скопируйте его куда-нибудь'; + case 'androidExportAlertTitle': return 'Экспорт на Android'; + case 'androidExportText': return ({required Object exportedDB}) => 'Экспортировано.\n${exportedDB}'; + case 'importDB': return 'Импортировать локальную базу данных'; + case 'importDBDescription': return 'Восстановите свою резеврную копию. Обратите внимание, что текущая база данных будет перезаписана.'; + case 'importWrongFileType': return 'Неверный тип файла'; + case 'importCancelled': return 'Операция была отменена'; + case 'importSuccess': return 'Успешно импортировано'; + case 'yourID': return 'Ваш аккаунт в TETR.IO'; + case 'yourIDAlertTitle': return 'Никнейм или ID вашего аккаунта в TETR.IO'; + case 'yourIDText': return 'Каждый раз, когда приложение запускается, приложение будет получать статистику этого игрока. Пожалуйста, отдайте предпочтение ID, так как никнейм можно изменить.'; + case 'language': return 'Язык (Language)'; + case 'aboutApp': return 'О приложении'; + case 'aboutAppText': return ({required Object appName, required Object packageName, required Object version, required Object buildNumber}) => '${appName} (${packageName}) Версия ${version} Сборка ${buildNumber}\n\nРазработал dan63047\nФормулы предоставил kerrmunism'; + case 'stateViewTitle': return ({required Object nickname, required Object date}) => 'Аккаунт ${nickname} ${date}'; + case 'statesViewTitle': return ({required Object number, required Object nickname}) => '${number} состояний аккаунта ${nickname}'; + case 'statesViewEntry': return ({required Object level, required Object gameTime, required Object friends, required Object rd}) => '${level} уровень, ${gameTime} сыграно, ${friends} друзей, ${rd} RD'; + case 'stateRemoved': return ({required Object }) => '${}Состояние от {date} было удалено из локальной базы данных!'; + case 'trackedPlayersViewTitle': return 'Сохранённые данные'; + case 'trackedPlayersZeroEntrys': return 'Пустой список. Вернитесь на предыдущий экран и нажмите кнопку "Отслеживать", чтобы текущий игрок появился здесь'; + case 'trackedPlayersOneEntry': return 'В списке только один игрок'; + case 'trackedPlayersManyEntrys': return ({required Object numberOfPlayers}) => 'В списке ${numberOfPlayers} игроков'; + case 'trackedPlayersEntry': return ({required Object nickname, required Object numberOfStates}) => '${nickname}: ${numberOfStates} состояний'; + case 'trackedPlayersDescription': return ({required Object firstStateDate, required Object lastStateDate}) => 'Начиная с ${firstStateDate} и заканчивая ${lastStateDate}'; + case 'trackedPlayersStatesDeleted': return ({required Object nickname}) => 'Состояния аккаунта ${nickname} были удалены из локальной базы данных!'; case 'statCellNum.xpLevel': return 'Уровень\nопыта'; + case 'statCellNum.xpProgress': return 'Прогресс до следующего уровня'; + case 'statCellNum.xpFrom0To5000': return 'Прогресс от 0 XP до 5000 уровня'; case 'statCellNum.hoursPlayed': return 'Часов\nСыграно'; case 'statCellNum.onlineGames': return 'Онлайн\nИгр'; case 'statCellNum.gamesWon': return 'Онлайн\nПобед'; @@ -1368,6 +1563,25 @@ extension on _StringsRu { case 'statCellNum.keys': return 'Нажатий\nКлавиш'; case 'statCellNum.kpp': return 'Нажатий\nна Фигуру'; case 'statCellNum.kps': return 'Нажатий\nв Секунду'; + case 'statCellNum.app': return 'Атака на Фигуру'; + case 'statCellNum.appDescription': return '(Сокращенно APP) Главный показатель эффективности. Показывает, сколько атаки приходится на одну фигуру'; + case 'statCellNum.vsapmDescription': return 'В основном, показывает как много мусора игрок использует в своих атаках и насколько эффективно.'; + case 'statCellNum.dss': return 'Downstack\nв Секунду'; + case 'statCellNum.dssDescription': return '(Сокращенно DS/S) Downstack (спуск вниз) в Секунду показывает как много мусорных линий в среднем игрок убирает за одну секунду.'; + case 'statCellNum.dsp': return 'Downstack\nна Фигуру'; + case 'statCellNum.dspDescription': return '(Сокращенно DS/P) Downstack (спуск вниз) на Фигуру показывает как много мусорных линий в среднем игрок убирает одну фигуру.'; + case 'statCellNum.appdsp': return 'APP + DS/P'; + case 'statCellNum.appdspDescription': return 'Просто сумма Атаки на Фигуру и Downstack на Фигуру.'; + case 'statCellNum.cheese': return 'Индекс сыра'; + case 'statCellNum.cheeseDescription': return '(Сокращенно Cheese) Индекс сыра является аппроксимацией того, насколько чистый / дырявый мусор игрок отправляет. Меньше = более чистый. Больше = более дырявый.\nПридумал kerrmunism'; + case 'statCellNum.gbe': return 'Garbage\nEfficiency'; + case 'statCellNum.gbeDescription': return '(Сокращенно Gb Eff.) Garbage Efficiency показывает насколько хорошо игрок использует свой мусор. Больше = лучше (или он использует больше мусора). Меньше = в основном отправляют сыр (или он редко чистит мусор).\nПридумали Zepheniah и Dragonboy.'; + case 'statCellNum.nyaapp': return 'Взвешенный\nAPP'; + case 'statCellNum.nyaappDescription': return '(Сокращенно wAPP) По сути, показывает способность отправлять сыр, сохраняя при этом высокую эффективность.\nПридумал Wertj.'; + case 'statCellNum.area': return 'Area'; + case 'statCellNum.areaDescription': return 'Какую площадь занимает диаграмма, если не брать в расчёт индекс сыра и VS/APM'; + case 'statCellNum.estOfTR': return 'Расчётный TR'; + case 'statCellNum.accOfEst': return 'Точность расчёта'; case 'playerRole.user': return 'Пользователь'; case 'playerRole.banned': return 'Заблокированный пользователь'; case 'playerRole.bot': return 'Бот'; @@ -1380,6 +1594,8 @@ extension on _StringsRu { case 'numOfGameActions.hold': return 'В запас'; case 'numOfGameActions.tspinsTotal': return 'T-spins всего'; case 'numOfGameActions.lineClears': return 'Линий очищено'; + case 'popupActions.cancel': return 'Отменить'; + case 'popupActions.submit': return 'Подтвердить'; case 'popupActions.ok': return 'OK'; case 'errors.connection': return ({required Object code, required Object message}) => 'Проблема с подключением: ${code} ${message}'; case 'errors.noSuchUser': return 'Нет такого пользователя'; diff --git a/lib/views/settings_view.dart b/lib/views/settings_view.dart index 1a22c3c..a316ca7 100644 --- a/lib/views/settings_view.dart +++ b/lib/views/settings_view.dart @@ -74,33 +74,31 @@ class SettingsState extends State { } return Scaffold( appBar: AppBar( - title: const Text("Settings"), + title: Text(t.settings), ), backgroundColor: Colors.black, body: SafeArea( child: ListView( children: [ ListTile( - title: const Text("Export local database"), - subtitle: const Text( - "It contains states and Tetra League records of the tracked players and list of tracked players."), + title: Text(t.exportDB), + subtitle: Text(t.exportDBDescription), onTap: () { if (Platform.isLinux || Platform.isWindows) { showDialog( context: context, builder: (BuildContext context) => AlertDialog( - title: const Text("Desktop export", - style: TextStyle( + title: Text(t.desktopExportAlertTitle, + style: const TextStyle( fontFamily: "Eurostile Round Extended")), - content: const SingleChildScrollView( + content: SingleChildScrollView( child: ListBody(children: [ - Text( - "It seems like you using this app on desktop. Check your documents folder, you should find \"TetraStats.db\". Copy it somewhere") + Text(t.desktopExportText) ]), ), actions: [ TextButton( - child: const Text('OK'), + child: Text(t.popupActions.ok), onPressed: () { Navigator.of(context).pop(); }, @@ -116,15 +114,15 @@ class SettingsState extends State { showDialog( context: context, builder: (BuildContext context) => AlertDialog( - title: const Text("Android export", - style: TextStyle( + title: Text(t.androidExportAlertTitle, + style: const TextStyle( fontFamily: "Eurostile Round Extended")), content: SingleChildScrollView( - child: ListBody(children: [Text("Exported.\n$exportedDB")]), + child: ListBody(children: [Text(t.androidExportText(exportedDB: exportedDB))]), ), actions: [ TextButton( - child: const Text('OK'), + child: Text(t.popupActions.ok), onPressed: () { Navigator.of(context).pop(); }, @@ -136,8 +134,8 @@ class SettingsState extends State { }, ), ListTile( - title: const Text("Import local database"), - subtitle: const Text("Restore your backup. Notice that already stored database will be overwritten."), + title: Text(t.importDB), + subtitle: Text(t.importDBDescription), onTap: () { if(Platform.isAndroid){ FilePicker.platform.pickFiles( @@ -147,18 +145,18 @@ class SettingsState extends State { var newDB = value.paths[0]!; teto.close().then((value){ if(!newDB.endsWith("db")){ - return ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text("Wrong file type"))); + return ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(t.importWrongFileType))); } getApplicationDocumentsDirectory().then((value){ var oldDB = File("${value.path}/TetraStats.db"); oldDB.writeAsBytes(File(newDB).readAsBytesSync(), flush: true).then((value){ teto.open(); - ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text("Import successful"))); + ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(t.importSuccess))); }); }); }); } else { - ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text("Operation was cancelled"))); + ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(t.importCancelled))); } }); }else{ @@ -174,42 +172,41 @@ class SettingsState extends State { var oldDB = File("${value.path}/TetraStats.db"); oldDB.writeAsBytes(File(newDB).readAsBytesSync()).then((value){ teto.open(); - ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text("Import successful"))); + ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(t.importSuccess))); }); }); }); } else { - ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text("Operation was cancelled"))); + ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(t.importCancelled))); } }); } }, ), ListTile( - title: const Text("Your TETR.IO account"), + title: Text(t.yourID), trailing: Text(defaultNickname), onTap: () => showDialog( context: context, builder: (BuildContext context) => AlertDialog( - title: const Text("Your TETR.IO account nickname or ID", - style: TextStyle( + title: Text(t.yourIDAlertTitle, + style: const TextStyle( fontFamily: "Eurostile Round Extended")), content: SingleChildScrollView( child: ListBody(children: [ - const Text( - "Every time when app loads, stats of that player will be fetched. Please prefer ID over nickname because nickname can be changed."), + Text(t.yourIDText), TextField(controller: _playertext, maxLength: 25) ]), ), actions: [ TextButton( - child: const Text('Cancel'), + child: Text(t.popupActions.cancel), onPressed: () { Navigator.of(context).pop(); }, ), TextButton( - child: const Text('Submit'), + child: Text(t.popupActions.submit), onPressed: () { _setPlayer(_playertext.text.toLowerCase().trim()); Navigator.of(context).pop(); @@ -220,7 +217,7 @@ class SettingsState extends State { )), ), ListTile( - title: const Text("Language"), + title: Text(t.language), trailing: DropdownButton( items: locales, value: LocaleSettings.currentLocale, @@ -229,13 +226,8 @@ class SettingsState extends State { ), const Divider(), ListTile( - title: const Text("About app"), - subtitle: Text(""" -${_packageInfo.appName} (${_packageInfo.packageName}) Version ${_packageInfo.version} Build ${_packageInfo.buildNumber} - -Developed by dan63047 -Formulas provided by kerrmunism -"""), + title: Text(t.aboutApp), + subtitle: Text(t.aboutAppText(appName: _packageInfo.appName, packageName: _packageInfo.packageName, version: _packageInfo.version, buildNumber: _packageInfo.buildNumber)), ), ], )), diff --git a/lib/views/state_view.dart b/lib/views/state_view.dart index 038a3d3..f7bab8b 100644 --- a/lib/views/state_view.dart +++ b/lib/views/state_view.dart @@ -1,8 +1,12 @@ import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; import 'package:tetra_stats/data_objects/tetrio.dart'; +import 'package:tetra_stats/gen/strings.g.dart'; import 'package:tetra_stats/widgets/tl_thingy.dart'; import 'package:tetra_stats/widgets/user_thingy.dart'; +final DateFormat dateFormat = DateFormat.yMMMd(LocaleSettings.currentLocale.languageCode).add_Hms(); + class StateView extends StatefulWidget { final TetrioPlayer state; const StateView({Key? key, required this.state}) : super(key: key); @@ -26,9 +30,10 @@ class StateState extends State { @override Widget build(BuildContext context) { + final t = Translations.of(context); return Scaffold( appBar: AppBar( - title: Text("${widget.state.username.toUpperCase()} account on ${widget.state.state}"), + title: Text(t.stateViewTitle(nickname: widget.state.username.toUpperCase(), date: dateFormat.format(widget.state.state))), ), backgroundColor: Colors.black, body: SafeArea( diff --git a/lib/views/states_view.dart b/lib/views/states_view.dart index c769895..0cb0e45 100644 --- a/lib/views/states_view.dart +++ b/lib/views/states_view.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; import 'package:tetra_stats/data_objects/tetrio.dart'; +import 'package:tetra_stats/gen/strings.g.dart'; import 'package:tetra_stats/views/compare_view.dart'; import 'package:tetra_stats/views/state_view.dart'; @@ -12,14 +13,14 @@ class StatesView extends StatefulWidget { State createState() => StatesState(); } -final DateFormat dateFormat = DateFormat.yMMMd().add_Hms(); - class StatesState extends State { @override Widget build(BuildContext context) { + final t = Translations.of(context); + final DateFormat dateFormat = DateFormat.yMMMd(LocaleSettings.currentLocale.languageCode).add_Hms(); return Scaffold( appBar: AppBar( - title: Text("${widget.states.length} states of ${widget.states.last.username.toUpperCase()} account"), + title: Text(t.statesViewTitle(number: widget.states.length, nickname: widget.states.last.username.toUpperCase())), ), backgroundColor: Colors.black, body: SafeArea( @@ -27,14 +28,14 @@ class StatesState extends State { itemCount: widget.states.length, itemBuilder: (context, index) { return ListTile( - title: Text("On ${dateFormat.format(widget.states[index].state)}"), - subtitle: Text("Level ${widget.states[index].level.toStringAsFixed(2)}, ${widget.states[index].gameTime} of gametime, ${widget.states[index].friendCount} friends, ${NumberFormat.compact().format(widget.states[index].tlSeason1.rd)} RD"), + title: Text(dateFormat.format(widget.states[index].state)), + subtitle: Text(t.statesViewEntry(level: widget.states[index].level.toStringAsFixed(2), gameTime: widget.states[index].gameTime, friends: widget.states[index].friendCount, rd: NumberFormat.compact().format(widget.states[index].tlSeason1.rd))), trailing: IconButton( icon: const Icon(Icons.delete_forever), onPressed: () { DateTime nn = widget.states[index].state; teto.deleteState(widget.states[index]).then((value) => setState(() { - ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text("${dateFormat.format(nn)} state was removed from database!"))); + ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(t.stateRemoved(date: dateFormat.format(nn))))); })); }, ), diff --git a/lib/views/tracked_players_view.dart b/lib/views/tracked_players_view.dart index d721018..ed7ca5d 100644 --- a/lib/views/tracked_players_view.dart +++ b/lib/views/tracked_players_view.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; import 'package:tetra_stats/data_objects/tetrio.dart'; +import 'package:tetra_stats/gen/strings.g.dart'; import 'package:tetra_stats/services/tetrio_crud.dart'; import 'package:tetra_stats/views/states_view.dart'; @@ -13,14 +14,14 @@ class TrackedPlayersView extends StatefulWidget { State createState() => TrackedPlayersState(); } -final DateFormat dateFormat = DateFormat.yMMMd().add_Hms(); - class TrackedPlayersState extends State { @override Widget build(BuildContext context) { + final t = Translations.of(context); + final DateFormat dateFormat = DateFormat.yMMMd(LocaleSettings.currentLocale.languageCode).add_Hms(); return Scaffold( appBar: AppBar( - title: const Text("Stored data"), + title: Text(t.trackedPlayersViewTitle), ), backgroundColor: Colors.black, body: SafeArea( @@ -38,9 +39,9 @@ class TrackedPlayersState extends State { headerSliverBuilder: (context, value) { String howManyPlayers(int numberOfPlayers) => Intl.plural( numberOfPlayers, - zero: 'Empty list. Press "Track" button in previous view to add current player here', - one: 'There is only one player', - other: 'There are $numberOfPlayers players', + zero: t.trackedPlayersZeroEntrys, + one: t.trackedPlayersOneEntry, + other: t.trackedPlayersManyEntrys(numberOfPlayers: numberOfPlayers), name: 'howManyPeople', args: [numberOfPlayers], desc: 'Description of how many people are seen in a place.', @@ -62,15 +63,14 @@ class TrackedPlayersState extends State { itemCount: allPlayers.length, itemBuilder: (context, index) { return ListTile( - title: Text("${allPlayers[keys[index]]?.last.username}: ${allPlayers[keys[index]]?.length} states"), - subtitle: Text( - "From ${dateFormat.format(allPlayers[keys[index]]!.first.state)} until ${dateFormat.format(allPlayers[keys[index]]!.last.state)}"), + title: Text(t.trackedPlayersEntry(nickname: allPlayers[keys[index]]!.last.username, numberOfStates: allPlayers[keys[index]]!.length)), + subtitle: Text(t.trackedPlayersDescription(firstStateDate: dateFormat.format(allPlayers[keys[index]]!.first.state), lastStateDate: dateFormat.format(allPlayers[keys[index]]!.last.state))), trailing: IconButton( icon: const Icon(Icons.delete_forever), onPressed: () { String nn = allPlayers[keys[index]]!.last.username; teto.deletePlayer(keys[index]); - ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text("$nn states was removed from database!"))); + ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(t.trackedPlayersStatesDeleted(nickname: nn)))); }, ), onTap: () { diff --git a/lib/widgets/stat_sell_num.dart b/lib/widgets/stat_sell_num.dart index afa33b7..cebcf5a 100644 --- a/lib/widgets/stat_sell_num.dart +++ b/lib/widgets/stat_sell_num.dart @@ -8,12 +8,16 @@ class StatCellNum extends StatelessWidget { required this.playerStatLabel, required this.isScreenBig, this.alertWidgets, - this.fractionDigits, this.oldPlayerStat, required this.higherIsBetter}); + this.fractionDigits, + this.oldPlayerStat, + required this.higherIsBetter, + this.okText}); final num playerStat; final num? oldPlayerStat; final bool higherIsBetter; final String playerStatLabel; + final String? okText; final bool isScreenBig; final List? alertWidgets; final int? fractionDigits; @@ -58,10 +62,8 @@ class StatCellNum extends StatelessWidget { ), actions: [ TextButton( - child: const Text('OK'), - onPressed: () { - Navigator.of(context).pop(); - }, + child: Text(okText??"OK"), + onPressed: () {Navigator.of(context).pop();} ) ], )); diff --git a/lib/widgets/tl_thingy.dart b/lib/widgets/tl_thingy.dart index 0f3c552..9653d9e 100644 --- a/lib/widgets/tl_thingy.dart +++ b/lib/widgets/tl_thingy.dart @@ -121,7 +121,7 @@ class TLThingy extends StatelessWidget { width: 200, height: 120, child: SfRadialGauge( - title: const GaugeTitle(text: "Attack Per Piece"), + title: GaugeTitle(text: t.statCellNum.app), axes: [RadialAxis( startAngle: 180, endAngle: 360, @@ -155,18 +155,18 @@ class TLThingy extends StatelessWidget { showDialog( context: context, builder: (BuildContext context) => AlertDialog( - title: const Text("Attack Per Piece", - style: TextStyle( + title: Text(t.statCellNum.app, + style: const TextStyle( fontFamily: "Eurostile Round Extended")), content: SingleChildScrollView( child: ListBody(children: [ - const Text("Main efficiency metric. Tells how many attack you producing per piece"), - Text("Raw value: ${tl.nerdStats!.app}") + Text(t.statCellNum.appDescription), + Text("${t.exactValue}: ${tl.nerdStats!.app}") ]), ), actions: [ TextButton( - child: const Text('OK'), + child: Text(t.popupActions.ok), onPressed: () { Navigator.of(context).pop(); }, @@ -222,13 +222,13 @@ class TLThingy extends StatelessWidget { fontFamily: "Eurostile Round Extended")), content: SingleChildScrollView( child: ListBody(children: [ - const Text("Basically, tells how much and how efficient you using garbage in your attacks"), - Text("Raw value: ${tl.nerdStats!.vsapm}") + Text(t.statCellNum.vsapmDescription), + Text("${t.exactValue}: ${tl.nerdStats!.vsapm}") ]), ), actions: [ TextButton( - child: const Text('OK'), + child: Text(t.popupActions.ok), onPressed: () { Navigator.of(context).pop(); }, @@ -251,46 +251,53 @@ class TLThingy extends StatelessWidget { crossAxisAlignment: WrapCrossAlignment.start, clipBehavior: Clip.hardEdge, children: [ - StatCellNum(playerStat: tl.nerdStats!.dss, isScreenBig: bigScreen, fractionDigits: 3, playerStatLabel: "Downstack\nPer Second", - alertWidgets: [const Text("Downstack per Second measures how many garbage lines you clear in a second."), - const Text("Formula: (VS / 100) - (APM / 60)"), - Text("Raw value: ${tl.nerdStats!.dss}"),], + StatCellNum(playerStat: tl.nerdStats!.dss, isScreenBig: bigScreen, fractionDigits: 3, playerStatLabel: t.statCellNum.dss, + alertWidgets: [Text(t.statCellNum.dssDescription), + Text("${t.formula}: (VS / 100) - (APM / 60)"), + Text("${t.exactValue}: ${tl.nerdStats!.dss}"),], + okText: t.popupActions.ok, higherIsBetter: true, oldPlayerStat: oldTl?.nerdStats?.dss,), - StatCellNum(playerStat: tl.nerdStats!.dsp, isScreenBig: bigScreen, fractionDigits: 3, playerStatLabel: "Downstack\nPer Piece", - alertWidgets: [const Text("Downstack per Piece measures how many garbage lines you clear per piece."), - const Text("Formula: DS/S / PPS"), - Text("Raw value: ${tl.nerdStats!.dsp}"),], + StatCellNum(playerStat: tl.nerdStats!.dsp, isScreenBig: bigScreen, fractionDigits: 3, playerStatLabel: t.statCellNum.dsp, + alertWidgets: [Text(t.statCellNum.dspDescription), + Text("${t.formula}: DS/S / PPS"), + Text("${t.exactValue}: ${tl.nerdStats!.dsp}"),], + okText: t.popupActions.ok, higherIsBetter: true, oldPlayerStat: oldTl?.nerdStats?.dsp,), - StatCellNum(playerStat: tl.nerdStats!.appdsp, isScreenBig: bigScreen, fractionDigits: 3, playerStatLabel: "APP + DS/P", - alertWidgets: [const Text("Just a sum of Attack per Piece and Downstack per Piece."), - const Text("Formula: APP + DS/P"), - Text("Raw value: ${tl.nerdStats!.appdsp}"),], + StatCellNum(playerStat: tl.nerdStats!.appdsp, isScreenBig: bigScreen, fractionDigits: 3, playerStatLabel: t.statCellNum.appdsp, + alertWidgets: [Text(t.statCellNum.appdspDescription), + Text("${t.formula}: APP + DS/P"), + Text("${t.exactValue}: ${tl.nerdStats!.appdsp}"),], + okText: t.popupActions.ok, higherIsBetter: true, oldPlayerStat: oldTl?.nerdStats?.appdsp,), - StatCellNum(playerStat: tl.nerdStats!.cheese, isScreenBig: bigScreen, fractionDigits: 2, playerStatLabel: "Cheese\nIndex", - alertWidgets: [const Text("Cheese Index is an approximation how much clean / cheese garbage player sends. Lower = more clean. Higher = more cheese.\nInvented by kerrmunism"), - const Text("Formula: (DS/P * 150) + ((VS/APM - 2) * 50) + (0.6 - APP) * 125"), - Text("Raw value: ${tl.nerdStats!.cheese}"),], + StatCellNum(playerStat: tl.nerdStats!.cheese, isScreenBig: bigScreen, fractionDigits: 2, playerStatLabel: t.statCellNum.cheese, + alertWidgets: [Text(t.statCellNum.cheeseDescription), + Text("${t.formula}: (DS/P * 150) + ((VS/APM - 2) * 50) + (0.6 - APP) * 125"), + Text("${t.exactValue}: ${tl.nerdStats!.cheese}"),], + okText: t.popupActions.ok, higherIsBetter: true, oldPlayerStat: oldTl?.nerdStats?.cheese,), - StatCellNum(playerStat: tl.nerdStats!.gbe, isScreenBig: bigScreen, fractionDigits: 3, playerStatLabel: "Garbage\nEfficiency", - alertWidgets: [const Text("Garbage Efficiency measures how well player uses their garbage. Higher = better or they use their garbage more. Lower = they mostly send their garbage back at cheese or rarely clear garbage.\nInvented by Zepheniah and Dragonboy."), - const Text("Formula: ((APP * DS/S) / PPS) * 2"), - Text("Raw value: ${tl.nerdStats!.gbe}"),], + StatCellNum(playerStat: tl.nerdStats!.gbe, isScreenBig: bigScreen, fractionDigits: 3, playerStatLabel: t.statCellNum.gbe, + alertWidgets: [Text(t.statCellNum.gbeDescription), + Text("${t.formula}: ((APP * DS/S) / PPS) * 2"), + Text("${t.exactValue}: ${tl.nerdStats!.gbe}"),], + okText: t.popupActions.ok, higherIsBetter: true, oldPlayerStat: oldTl?.nerdStats?.gbe,), - StatCellNum(playerStat: tl.nerdStats!.nyaapp, isScreenBig: bigScreen, fractionDigits: 3, playerStatLabel: "Weighted\nAPP", - alertWidgets: [const Text("Essentially, a measure of your ability to send cheese while still maintaining a high APP.\nInvented by Wertj."), - const Text("Formula: APP - 5 * tan(radians((Cheese Index / -30) + 1))"), - Text("Raw value: ${tl.nerdStats!.nyaapp}"),], + StatCellNum(playerStat: tl.nerdStats!.nyaapp, isScreenBig: bigScreen, fractionDigits: 3, playerStatLabel: t.statCellNum.nyaapp, + alertWidgets: [Text(t.statCellNum.nyaappDescription), + Text("${t.formula}: APP - 5 * tan(radians((Cheese Index / -30) + 1))"), + Text("${t.exactValue}: ${tl.nerdStats!.nyaapp}"),], + okText: t.popupActions.ok, higherIsBetter: true, oldPlayerStat: oldTl?.nerdStats?.nyaapp,), - StatCellNum(playerStat: tl.nerdStats!.area, isScreenBig: bigScreen, fractionDigits: 1, playerStatLabel: "Area", - alertWidgets: [const Text("How much space your shape takes up on the graph, if you exclude the cheese and vs/apm sections"), - const Text("Formula: APM * 1 + PPS * 45 + VS * 0.444 + APP * 185 + DS/S * 175 + DS/P * 450 + Garbage Effi * 315"), - Text("Raw value: ${tl.nerdStats!.area}"),], + StatCellNum(playerStat: tl.nerdStats!.area, isScreenBig: bigScreen, fractionDigits: 1, playerStatLabel: t.statCellNum.area, + alertWidgets: [Text(t.statCellNum.areaDescription), + Text("${t.formula}: APM * 1 + PPS * 45 + VS * 0.444 + APP * 185 + DS/S * 175 + DS/P * 450 + Garbage Effi * 315"), + Text("${t.exactValue}: ${tl.nerdStats!.area}"),], + okText: t.popupActions.ok, higherIsBetter: true, oldPlayerStat: oldTl?.nerdStats?.area,) ]) @@ -307,9 +314,9 @@ class TLThingy extends StatelessWidget { Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - const Text( - "Est. of TR:", - style: TextStyle(fontSize: 24), + Text( + "${t.statCellNum.estOfTR}:", + style: const TextStyle(fontSize: 24), ), Text( f2.format(tl.estTr!.esttr), @@ -321,9 +328,9 @@ class TLThingy extends StatelessWidget { Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - const Text( - "Accuracy:", - style: TextStyle(fontSize: 24), + Text( + "${t.statCellNum.accOfEst}:", + style: const TextStyle(fontSize: 24), ), Text( fDiff.format(tl.esttracc!), @@ -499,7 +506,7 @@ class TLThingy extends StatelessWidget { ) ] : [ - const Text("That user never played Tetra League", style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 28)), + Text(t.neverPlayedTL, style: const TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 28)), ], ); }, diff --git a/lib/widgets/user_thingy.dart b/lib/widgets/user_thingy.dart index 24fa3cf..17bed01 100644 --- a/lib/widgets/user_thingy.dart +++ b/lib/widgets/user_thingy.dart @@ -170,7 +170,8 @@ class UserThingy extends StatelessWidget { playerStat: player.level, playerStatLabel: t.statCellNum.xpLevel, isScreenBig: bigScreen, - alertWidgets: [Text("${NumberFormat.decimalPatternDigits(decimalDigits: 2).format(player.xp)} XP", style: const TextStyle(fontFamily: "Eurostile Round Extended"),), Text("Progress to next level: ${((player.level - player.level.floor()) * 100).toStringAsFixed(2)} %"), Text("Progress from 0 XP to level 5000: ${((player.xp / 67009017.7589378) * 100).toStringAsFixed(2)} %")], + alertWidgets: [Text("${NumberFormat.decimalPatternDigits(decimalDigits: 2).format(player.xp)} XP", style: const TextStyle(fontFamily: "Eurostile Round Extended"),), Text("${t.statCellNum.xpProgress}: ${((player.level - player.level.floor()) * 100).toStringAsFixed(2)} %"), Text("${t.statCellNum.xpFrom0To5000}: ${((player.xp / 67009017.7589378) * 100).toStringAsFixed(2)} %")], + okText: t.popupActions.ok, higherIsBetter: true, ), if (player.gameTime >= Duration.zero) diff --git a/res/i18n/strings.i18n.json b/res/i18n/strings.i18n.json index 1d04599..52ff411 100644 --- a/res/i18n/strings.i18n.json +++ b/res/i18n/strings.i18n.json @@ -45,8 +45,41 @@ "gamesUntilRanked": "${left} games until being ranked", "nerdStats": "Nerd Stats", "playersYouTrack": "Players you track", + "formula": "Formula", + "exactValue": "Exact value", + "neverPlayedTL": "That user never played Tetra League", + "exportDB": "Export local database", + "exportDBDescription": "It contains states and Tetra League records of the tracked players and list of tracked players.", + "desktopExportAlertTitle": "Desktop export", + "desktopExportText": "It seems like you using this app on desktop. Check your documents folder, you should find \"TetraStats.db\". Copy it somewhere", + "androidExportAlertTitle": "Android export", + "androidExportText": "Exported.\n${exportedDB}", + "importDB": "Import local database", + "importDBDescription": "Restore your backup. Notice that already stored database will be overwritten.", + "importWrongFileType": "Wrong file type", + "importCancelled": "Operation was cancelled", + "importSuccess": "Import successful", + "yourID": "Your TETR.IO account", + "yourIDAlertTitle": "Your TETR.IO account nickname or ID", + "yourIDText": "Every time when app loads, stats of that player will be fetched. Please prefer ID over nickname because nickname can be changed.", + "language": "Language", + "aboutApp": "About app", + "aboutAppText": "${appName} (${packageName}) Version ${version} Build ${buildNumber}\n\nDeveloped by dan63047\nFormulas provided by kerrmunism", + "stateViewTitle": "${nickname} account on ${date}", + "statesViewTitle": "${number} states of ${nickname} account", + "statesViewEntry": "Level ${level}, ${gameTime} of gametime, ${friends} friends, ${rd} RD", + "stateRemoved": "${date} state was removed from database!", + "trackedPlayersViewTitle": "Stored data", + "trackedPlayersZeroEntrys": "Empty list. Press \"Track\" button in previous view to add current player here", + "trackedPlayersOneEntry": "There is only one player", + "trackedPlayersManyEntrys": "There are ${numberOfPlayers} players", + "trackedPlayersEntry": "${nickname}: ${numberOfStates} states", + "trackedPlayersDescription": "From ${firstStateDate} until ${lastStateDate}", + "trackedPlayersStatesDeleted": "${nickname} states was removed from database!", "statCellNum":{ "xpLevel": "XP Level", + "xpProgress": "Progress to next level", + "xpFrom0To5000": "Progress from 0 XP to level 5000", "hoursPlayed": "Hours\nPlayed", "onlineGames": "Online\nGames", "gamesWon": "Games\nWon", @@ -67,7 +100,26 @@ "finessePercentage": "Finesse\nPercentage", "keys": "Key\nPresses", "kpp": "KP Per\nPiece", - "kps": "KP Per\nSecond" + "kps": "KP Per\nSecond", + "app": "Attack Per Piece", + "appDescription": "(Abbreviated as APP) Main efficiency metric. Tells how many attack you producing per piece", + "vsapmDescription": "Basically, tells how much and how efficient you using garbage in your attacks", + "dss": "Downstack\nPer Second", + "dssDescription": "Downstack per Second measures how many garbage lines you clear in a second.", + "dsp": "Downstack\nPer Piece", + "dspDescription": "Downstack per Piece measures how many garbage lines you clear per piece.", + "appdsp": "APP + DS/P", + "appdspDescription": "Just a sum of Attack per Piece and Downstack per Piece.", + "cheese": "Cheese\nIndex", + "cheeseDescription": "Cheese Index is an approximation how much clean / cheese garbage player sends. Lower = more clean. Higher = more cheese.\nInvented by kerrmunism", + "gbe": "Garbage\nEfficiency", + "gbeDescription": "Garbage Efficiency measures how well player uses their garbage. Higher = better or they use their garbage more. Lower = they mostly send their garbage back at cheese or rarely clear garbage.\nInvented by Zepheniah and Dragonboy.", + "nyaapp": "Weighted\nAPP", + "nyaappDescription": "Essentially, a measure of your ability to send cheese while still maintaining a high APP.\nInvented by Wertj.", + "area": "Area", + "areaDescription": "How much space your shape takes up on the graph, if you exclude the cheese and vs/apm sections", + "estOfTR": "Est. of TR", + "accOfEst": "Accuracy" }, "playerRole(map)": { "user": "User", @@ -86,6 +138,8 @@ "lineClears": "Line clears" }, "popupActions":{ + "cancel": "Cancel", + "submit": "Submit", "ok": "OK" }, "errors":{ diff --git a/res/i18n/strings_ru.i18n.json b/res/i18n/strings_ru.i18n.json index 63103a5..f97fe44 100644 --- a/res/i18n/strings_ru.i18n.json +++ b/res/i18n/strings_ru.i18n.json @@ -45,8 +45,41 @@ "gamesUntilRanked": "${left} матчей до получения рейтинга", "nerdStats": "Для задротов", "playersYouTrack": "Отслеживаемые игроки", + "formula": "Формула", + "exactValue": "Точное значение", + "neverPlayedTL": "Этот игрок никогда не играл в Тетра Лигу", + "exportDB": "Экспортировать локальную базу данных", + "exportDBDescription": "Она содержит состояния аккаунтов и их матчей в Тетра Лиге для отслеживаемых игроков и список таких игроков.", + "desktopExportAlertTitle": "Экспорт на десктопе", + "desktopExportText": "Похоже, вы используете десктопную версию. Проверьте папку \"Документы\", там вы должны найти файл \"TetraStats.db\". Скопируйте его куда-нибудь", + "androidExportAlertTitle": "Экспорт на Android", + "androidExportText": "Экспортировано.\n${exportedDB}", + "importDB": "Импортировать локальную базу данных", + "importDBDescription": "Восстановите свою резеврную копию. Обратите внимание, что текущая база данных будет перезаписана.", + "importWrongFileType": "Неверный тип файла", + "importCancelled": "Операция была отменена", + "importSuccess": "Успешно импортировано", + "yourID": "Ваш аккаунт в TETR.IO", + "yourIDAlertTitle": "Никнейм или ID вашего аккаунта в TETR.IO", + "yourIDText": "Каждый раз, когда приложение запускается, приложение будет получать статистику этого игрока. Пожалуйста, отдайте предпочтение ID, так как никнейм можно изменить.", + "language": "Язык (Language)", + "aboutApp": "О приложении", + "aboutAppText": "${appName} (${packageName}) Версия ${version} Сборка ${buildNumber}\n\nРазработал dan63047\nФормулы предоставил kerrmunism", + "stateViewTitle": "Аккаунт ${nickname} ${date}", + "statesViewTitle": "${number} состояний аккаунта ${nickname}", + "statesViewEntry": "${level} уровень, ${gameTime} сыграно, ${friends} друзей, ${rd} RD", + "stateRemoved": "$Состояние от {date} было удалено из локальной базы данных!", + "trackedPlayersViewTitle": "Сохранённые данные", + "trackedPlayersZeroEntrys": "Пустой список. Вернитесь на предыдущий экран и нажмите кнопку \"Отслеживать\", чтобы текущий игрок появился здесь", + "trackedPlayersOneEntry": "В списке только один игрок", + "trackedPlayersManyEntrys": "В списке ${numberOfPlayers} игроков", + "trackedPlayersEntry": "${nickname}: ${numberOfStates} состояний", + "trackedPlayersDescription": "Начиная с ${firstStateDate} и заканчивая ${lastStateDate}", + "trackedPlayersStatesDeleted": "Состояния аккаунта ${nickname} были удалены из локальной базы данных!", "statCellNum": { "xpLevel": "Уровень\nопыта", + "xpProgress": "Прогресс до следующего уровня", + "xpFrom0To5000": "Прогресс от 0 XP до 5000 уровня", "hoursPlayed": "Часов\nСыграно", "onlineGames": "Онлайн\nИгр", "gamesWon": "Онлайн\nПобед", @@ -67,7 +100,26 @@ "finessePercentage": "% Качества\nТехники", "keys": "Нажатий\nКлавиш", "kpp": "Нажатий\nна Фигуру", - "kps": "Нажатий\nв Секунду" + "kps": "Нажатий\nв Секунду", + "app": "Атака на Фигуру", + "appDescription": "(Сокращенно APP) Главный показатель эффективности. Показывает, сколько атаки приходится на одну фигуру", + "vsapmDescription": "В основном, показывает как много мусора игрок использует в своих атаках и насколько эффективно.", + "dss": "Downstack\nв Секунду", + "dssDescription": "(Сокращенно DS/S) Downstack (спуск вниз) в Секунду показывает как много мусорных линий в среднем игрок убирает за одну секунду.", + "dsp": "Downstack\nна Фигуру", + "dspDescription": "(Сокращенно DS/P) Downstack (спуск вниз) на Фигуру показывает как много мусорных линий в среднем игрок убирает одну фигуру.", + "appdsp": "APP + DS/P", + "appdspDescription": "Просто сумма Атаки на Фигуру и Downstack на Фигуру.", + "cheese": "Индекс сыра", + "cheeseDescription": "(Сокращенно Cheese) Индекс сыра является аппроксимацией того, насколько чистый / дырявый мусор игрок отправляет. Меньше = более чистый. Больше = более дырявый.\nПридумал kerrmunism", + "gbe": "Garbage\nEfficiency", + "gbeDescription": "(Сокращенно Gb Eff.) Garbage Efficiency показывает насколько хорошо игрок использует свой мусор. Больше = лучше (или он использует больше мусора). Меньше = в основном отправляют сыр (или он редко чистит мусор).\nПридумали Zepheniah и Dragonboy.", + "nyaapp": "Взвешенный\nAPP", + "nyaappDescription": "(Сокращенно wAPP) По сути, показывает способность отправлять сыр, сохраняя при этом высокую эффективность.\nПридумал Wertj.", + "area": "Area", + "areaDescription": "Какую площадь занимает диаграмма, если не брать в расчёт индекс сыра и VS/APM", + "estOfTR": "Расчётный TR", + "accOfEst": "Точность расчёта" }, "playerRole(map)": { "user": "Пользователь", @@ -86,6 +138,8 @@ "lineClears": "Линий очищено" }, "popupActions":{ + "cancel": "Отменить", + "submit": "Подтвердить", "ok": "OK" }, "errors":{