diff --git a/README.md b/README.md index 2e769d5..2442ea9 100644 --- a/README.md +++ b/README.md @@ -18,10 +18,10 @@ - ~~Ability to compare player with APM-PPS-VS stats~~ - ~~Ability to fetch Tetra League leaderboard~~ - ~~Average stats for ranks~~ -- ~~Ability to compare player with avgRank~~ *dev build are here* -- UI Animations -- i18n, EN and RU locales -- Talk with osk about CORS and EndContext in TL matches +- ~~Ability to compare player with avgRank~~ +- UI Animations *lol* +- ~~i18n, EN and RU locales~~ *dev build are here* +- Talk with osk about CORS and EndContext in TL matches *idk lol* - RELEASE ??? *that will be v1.0.0* --- diff --git a/lib/gen/strings.g.dart b/lib/gen/strings.g.dart index b7bade2..c125dd1 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: 828 (414 per locale) +/// Strings: 848 (424 per locale) /// -/// Built on 2023-07-14 at 20:17 UTC +/// Built on 2023-07-15 at 16:11 UTC // coverage:ignore-file // ignore_for_file: type=lint @@ -159,6 +159,8 @@ class _StringsEn implements BaseTranslations { String get other => 'Other'; String get zen => 'Zen'; String get bio => 'Bio'; + String get openSearch => 'Search player'; + String get closeSearch => 'Close search'; String get refresh => 'Refresh'; String get showStoredData => 'Show stored data'; String get statsCalc => 'Stats Calculator'; @@ -242,6 +244,11 @@ class _StringsEn implements BaseTranslations { String get fromBeginning => 'From beginning'; String get calc => 'Calc'; String get calcViewNoValues => 'Enter values to calculate the stats'; + String get rankAveragesViewTitle => 'Ranks cutoff and average stats'; + String get averages => 'Averages'; + String get lbViewZeroEntrys => 'Empty list. Looks like something is wrong...'; + String get lbViewOneEntry => 'There is only one player... What?'; + String lbViewManyEntrys({required Object numberOfPlayers}) => 'There are ${numberOfPlayers} ranked players.'; late final _StringsStatCellNumEn statCellNum = _StringsStatCellNumEn._(_root); Map get playerRole => { 'user': 'User', @@ -551,6 +558,7 @@ class _StringsStatCellNumEn { String get keys => 'Key\nPresses'; String get kpp => 'KP Per\nPiece'; String get kps => 'KP Per\nSecond'; + String get tr => 'Tetra Rating'; 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'; @@ -569,7 +577,9 @@ class _StringsStatCellNumEn { 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 estOfTRShort => 'Est. TR'; String get accOfEst => 'Accuracy'; + String get accOfEstShort => 'Acc.'; } // Path: numOfGameActions @@ -646,6 +656,8 @@ class _StringsRu implements _StringsEn { @override String get other => 'Другое'; @override String get zen => 'Дзен'; @override String get bio => 'Биография'; + @override String get openSearch => 'Искать игрока'; + @override String get closeSearch => 'Закрыть поиск'; @override String get refresh => 'Обновить'; @override String get showStoredData => 'Показать сохранённые данные'; @override String get statsCalc => 'Калькулятор статистики'; @@ -655,7 +667,7 @@ class _StringsRu implements _StringsEn { @override String get becameTracked => 'Добавлен в список отслеживания!'; @override String get stoppedBeingTracked => 'Удалён из списка отслеживания!'; @override String get compare => 'Сравнить'; - @override String get tlLeaderboard => 'Таблица лидеров Тетра Лиги'; + @override String get tlLeaderboard => 'Рейтинговая таблица'; @override String get noRecords => 'Нет записей'; @override String get noRecord => 'Нет рекорда'; @override String get notEnoughData => 'Недостаточно данных'; @@ -725,10 +737,15 @@ class _StringsRu implements _StringsEn { @override String get yes => 'Да'; @override String get no => 'Нет'; @override String get daysLater => 'дней позже'; - @override String get dayseBefore => 'дней до'; + @override String get dayseBefore => 'дней раньше'; @override String get fromBeginning => 'С начала'; @override String get calc => 'Считать'; @override String get calcViewNoValues => 'Введите значения, чтобы посчитать статистику'; + @override String get rankAveragesViewTitle => 'Требования рангов и средние значения'; + @override String get averages => 'Средние значения'; + @override String get lbViewZeroEntrys => 'Рейтинговая таблица пуста. Похоже, что-то здесь не так...'; + @override String get lbViewOneEntry => 'В рейтинговой таблице всего один игрок... Чего?'; + @override String lbViewManyEntrys({required Object numberOfPlayers}) => 'В рейтинговой таблице находится ${numberOfPlayers} игроков.'; @override late final _StringsStatCellNumRu statCellNum = _StringsStatCellNumRu._(_root); @override Map get playerRole => { 'user': 'Пользователь', @@ -1038,6 +1055,7 @@ class _StringsStatCellNumRu implements _StringsStatCellNumEn { @override String get keys => 'Нажатий\nКлавиш'; @override String get kpp => 'Нажатий\nна Фигуру'; @override String get kps => 'Нажатий\nв Секунду'; + @override String get tr => 'Тетра Рейтинг'; @override String get app => 'Атака на Фигуру'; @override String get appDescription => '(Сокращенно APP) Главный показатель эффективности. Показывает, сколько атаки приходится на одну фигуру'; @override String get vsapmDescription => 'В основном, показывает как много мусора игрок использует в своих атаках и насколько эффективно.'; @@ -1056,7 +1074,9 @@ class _StringsStatCellNumRu implements _StringsStatCellNumEn { @override String get area => 'Area'; @override String get areaDescription => 'Какую площадь занимает диаграмма, если не брать в расчёт индекс сыра и VS/APM'; @override String get estOfTR => 'Расчётный TR'; + @override String get estOfTRShort => 'Расч. TR'; @override String get accOfEst => 'Точность расчёта'; + @override String get accOfEstShort => 'Точность'; } // Path: numOfGameActions @@ -1112,6 +1132,8 @@ extension on _StringsEn { case 'other': return 'Other'; case 'zen': return 'Zen'; case 'bio': return 'Bio'; + case 'openSearch': return 'Search player'; + case 'closeSearch': return 'Close search'; case 'refresh': return 'Refresh'; case 'showStoredData': return 'Show stored data'; case 'statsCalc': return 'Stats Calculator'; @@ -1195,6 +1217,11 @@ extension on _StringsEn { case 'fromBeginning': return 'From beginning'; case 'calc': return 'Calc'; case 'calcViewNoValues': return 'Enter values to calculate the stats'; + case 'rankAveragesViewTitle': return 'Ranks cutoff and average stats'; + case 'averages': return 'Averages'; + case 'lbViewZeroEntrys': return 'Empty list. Looks like something is wrong...'; + case 'lbViewOneEntry': return 'There is only one player... What?'; + case 'lbViewManyEntrys': return ({required Object numberOfPlayers}) => 'There are ${numberOfPlayers} ranked players.'; 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'; @@ -1221,6 +1248,7 @@ 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.tr': return 'Tetra Rating'; 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'; @@ -1239,7 +1267,9 @@ extension on _StringsEn { 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.estOfTRShort': return 'Est. TR'; case 'statCellNum.accOfEst': return 'Accuracy'; + case 'statCellNum.accOfEstShort': return 'Acc.'; case 'playerRole.user': return 'User'; case 'playerRole.banned': return 'Banned'; case 'playerRole.bot': return 'Bot'; @@ -1534,6 +1564,8 @@ extension on _StringsRu { case 'other': return 'Другое'; case 'zen': return 'Дзен'; case 'bio': return 'Биография'; + case 'openSearch': return 'Искать игрока'; + case 'closeSearch': return 'Закрыть поиск'; case 'refresh': return 'Обновить'; case 'showStoredData': return 'Показать сохранённые данные'; case 'statsCalc': return 'Калькулятор статистики'; @@ -1543,7 +1575,7 @@ extension on _StringsRu { case 'becameTracked': return 'Добавлен в список отслеживания!'; case 'stoppedBeingTracked': return 'Удалён из списка отслеживания!'; case 'compare': return 'Сравнить'; - case 'tlLeaderboard': return 'Таблица лидеров Тетра Лиги'; + case 'tlLeaderboard': return 'Рейтинговая таблица'; case 'noRecords': return 'Нет записей'; case 'noRecord': return 'Нет рекорда'; case 'notEnoughData': return 'Недостаточно данных'; @@ -1613,10 +1645,15 @@ extension on _StringsRu { case 'yes': return 'Да'; case 'no': return 'Нет'; case 'daysLater': return 'дней позже'; - case 'dayseBefore': return 'дней до'; + case 'dayseBefore': return 'дней раньше'; case 'fromBeginning': return 'С начала'; case 'calc': return 'Считать'; case 'calcViewNoValues': return 'Введите значения, чтобы посчитать статистику'; + case 'rankAveragesViewTitle': return 'Требования рангов и средние значения'; + case 'averages': return 'Средние значения'; + case 'lbViewZeroEntrys': return 'Рейтинговая таблица пуста. Похоже, что-то здесь не так...'; + case 'lbViewOneEntry': return 'В рейтинговой таблице всего один игрок... Чего?'; + case 'lbViewManyEntrys': return ({required Object numberOfPlayers}) => 'В рейтинговой таблице находится ${numberOfPlayers} игроков.'; case 'statCellNum.xpLevel': return 'Уровень\nопыта'; case 'statCellNum.xpProgress': return 'Прогресс до следующего уровня'; case 'statCellNum.xpFrom0To5000': return 'Прогресс от 0 XP до 5000 уровня'; @@ -1643,6 +1680,7 @@ extension on _StringsRu { case 'statCellNum.keys': return 'Нажатий\nКлавиш'; case 'statCellNum.kpp': return 'Нажатий\nна Фигуру'; case 'statCellNum.kps': return 'Нажатий\nв Секунду'; + case 'statCellNum.tr': return 'Тетра Рейтинг'; case 'statCellNum.app': return 'Атака на Фигуру'; case 'statCellNum.appDescription': return '(Сокращенно APP) Главный показатель эффективности. Показывает, сколько атаки приходится на одну фигуру'; case 'statCellNum.vsapmDescription': return 'В основном, показывает как много мусора игрок использует в своих атаках и насколько эффективно.'; @@ -1661,7 +1699,9 @@ extension on _StringsRu { case 'statCellNum.area': return 'Area'; case 'statCellNum.areaDescription': return 'Какую площадь занимает диаграмма, если не брать в расчёт индекс сыра и VS/APM'; case 'statCellNum.estOfTR': return 'Расчётный TR'; + case 'statCellNum.estOfTRShort': return 'Расч. TR'; case 'statCellNum.accOfEst': return 'Точность расчёта'; + case 'statCellNum.accOfEstShort': return 'Точность'; case 'playerRole.user': return 'Пользователь'; case 'playerRole.banned': return 'Заблокированный пользователь'; case 'playerRole.bot': return 'Бот'; diff --git a/lib/main.dart b/lib/main.dart index 71679f0..8e3e915 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,5 +1,6 @@ import 'dart:io'; import 'package:flutter/material.dart'; +import 'package:shared_preferences/shared_preferences.dart'; import 'package:sqflite_common_ffi/sqflite_ffi.dart'; import 'package:tetra_stats/gen/strings.g.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; @@ -8,18 +9,27 @@ import 'package:tetra_stats/views/settings_view.dart'; import 'package:tetra_stats/views/tracked_players_view.dart'; import 'package:tetra_stats/views/calc_view.dart'; -void main() { +void main() async { if (Platform.isWindows || Platform.isLinux || Platform.isMacOS) { sqfliteFfiInit(); databaseFactory = databaseFactoryFfi; } - WidgetsFlutterBinding.ensureInitialized(); + WidgetsFlutterBinding.ensureInitialized(); + prefs = await SharedPreferences.getInstance(); + String? locale = prefs.getString("locale"); + if (locale == null){ + LocaleSettings.useDeviceLocale(); + }else{ + LocaleSettings.setLocaleRaw(locale); + } runApp(TranslationProvider( child: MyApp(), )); } class MyApp extends StatelessWidget { + const MyApp({super.key}); + @override Widget build(BuildContext context) { return MaterialApp( diff --git a/lib/views/calc_view.dart b/lib/views/calc_view.dart index d2edd56..2920ff4 100644 --- a/lib/views/calc_view.dart +++ b/lib/views/calc_view.dart @@ -297,8 +297,7 @@ class _ListEntry extends StatelessWidget { @override Widget build(BuildContext context) { - var f = NumberFormat("#,###.##"); - f.maximumFractionDigits = fractionDigits ?? 0; + NumberFormat f = NumberFormat.decimalPatternDigits(locale: LocaleSettings.currentLocale.languageCode, decimalDigits: fractionDigits ?? 0); return ListTile(title: Text(label), trailing: Text(f.format(value), style: const TextStyle(fontSize: 22))); } } diff --git a/lib/views/compare_view.dart b/lib/views/compare_view.dart index a7f4ce7..69440a2 100644 --- a/lib/views/compare_view.dart +++ b/lib/views/compare_view.dart @@ -593,7 +593,7 @@ class CompareState extends State { higherIsBetter: true, ), CompareThingy( - label: t.statCellNum.estOfTR, + label: t.statCellNum.estOfTRShort, greenSide: theGreenSide[2].estTr!.esttr, redSide: theRedSide[2].estTr!.esttr, fractionDigits: 2, @@ -604,7 +604,7 @@ class CompareState extends State { greenSideMode != Mode.stats && redSideMode != Mode.stats) CompareThingy( - label: t.statCellNum.accOfEst, + label: t.statCellNum.accOfEstShort, greenSide: theGreenSide[2].esttracc!, redSide: theRedSide[2].esttracc!, fractionDigits: 2, @@ -964,7 +964,7 @@ class CompareThingy extends StatelessWidget { @override Widget build(BuildContext context) { - var f = NumberFormat("#,###.##"); + var f = NumberFormat.decimalPattern(LocaleSettings.currentLocale.languageCode); f.maximumFractionDigits = fractionDigits ?? 0; return Padding( padding: const EdgeInsets.fromLTRB(16, 2, 16, 2), @@ -1278,7 +1278,7 @@ class CompareRegTimeThingy extends StatelessWidget { @override Widget build(BuildContext context) { - DateFormat f = DateFormat.yMMMd(); + DateFormat f = DateFormat.yMMMd(LocaleSettings.currentLocale.languageCode); return Padding( padding: const EdgeInsets.fromLTRB(16, 2, 16, 2), child: Row( diff --git a/lib/views/main_view.dart b/lib/views/main_view.dart index 948dbc3..0703955 100644 --- a/lib/views/main_view.dart +++ b/lib/views/main_view.dart @@ -27,8 +27,8 @@ const allowedHeightForPlayerIdInPixels = 40.0; const allowedHeightForPlayerBioInPixels = 30.0; const givenTextHeightByScreenPercentage = 0.3; final NumberFormat timeInSec = NumberFormat("#,###.###s."); -final NumberFormat f2 = NumberFormat.decimalPatternDigits(decimalDigits: 2); -final NumberFormat f4 = NumberFormat.decimalPatternDigits(decimalDigits: 4); +final NumberFormat f2 = NumberFormat.decimalPatternDigits(locale: LocaleSettings.currentLocale.languageCode, decimalDigits: 2); +final NumberFormat f4 = NumberFormat.decimalPatternDigits(locale: LocaleSettings.currentLocale.languageCode, decimalDigits: 4); final DateFormat dateFormat = DateFormat.yMMMd(LocaleSettings.currentLocale.languageCode).add_Hms(); class MainView extends StatefulWidget { @@ -132,25 +132,24 @@ class _MainState extends State with SingleTickerProviderStateMixin { }on RangeError { compareWith = null; } - chartsData = >>[ - DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.gamesPlayed > 9) FlSpot(tl.timestamp.millisecondsSinceEpoch.toDouble(), tl.rating)], child: const Text("Tetra Rating")), + DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.gamesPlayed > 9) FlSpot(tl.timestamp.millisecondsSinceEpoch.toDouble(), tl.rating)], child: Text(t.statCellNum.tr)), DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.gamesPlayed > 9) FlSpot(tl.timestamp.millisecondsSinceEpoch.toDouble(), tl.glicko!)], child: const Text("Glicko")), DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.gamesPlayed > 9) FlSpot(tl.timestamp.millisecondsSinceEpoch.toDouble(), tl.rd!)], child: const Text("Rating Deviation")), - DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.apm != null) FlSpot(tl.timestamp.millisecondsSinceEpoch.toDouble(), tl.apm!)], child: const Text("Attack Per Minute")), - DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.pps != null) FlSpot(tl.timestamp.millisecondsSinceEpoch.toDouble(), tl.pps!)], child: const Text("Pieces Per Second")), - DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.vs != null) FlSpot(tl.timestamp.millisecondsSinceEpoch.toDouble(), tl.vs!)], child: const Text("Versus Score")), - DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.nerdStats != null) FlSpot(tl.timestamp.millisecondsSinceEpoch.toDouble(), tl.nerdStats!.app)], child: const Text("Attack Per Piece")), - DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.nerdStats != null) FlSpot(tl.timestamp.millisecondsSinceEpoch.toDouble(), tl.nerdStats!.dss)], child: const Text("Downstack Per Second")), - DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.nerdStats != null) FlSpot(tl.timestamp.millisecondsSinceEpoch.toDouble(), tl.nerdStats!.dsp)], child: const Text("Downstack Per Piece")), + DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.apm != null) FlSpot(tl.timestamp.millisecondsSinceEpoch.toDouble(), tl.apm!)], child: Text(t.statCellNum.apm.replaceAll(RegExp(r'\n'), " "))), + DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.pps != null) FlSpot(tl.timestamp.millisecondsSinceEpoch.toDouble(), tl.pps!)], child: Text(t.statCellNum.pps.replaceAll(RegExp(r'\n'), " "))), + DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.vs != null) FlSpot(tl.timestamp.millisecondsSinceEpoch.toDouble(), tl.vs!)], child: Text(t.statCellNum.vs.replaceAll(RegExp(r'\n'), " "))), + DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.nerdStats != null) FlSpot(tl.timestamp.millisecondsSinceEpoch.toDouble(), tl.nerdStats!.app)], child: Text(t.statCellNum.app.replaceAll(RegExp(r'\n'), " "))), + DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.nerdStats != null) FlSpot(tl.timestamp.millisecondsSinceEpoch.toDouble(), tl.nerdStats!.dss)], child: Text(t.statCellNum.dss.replaceAll(RegExp(r'\n'), " "))), + DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.nerdStats != null) FlSpot(tl.timestamp.millisecondsSinceEpoch.toDouble(), tl.nerdStats!.dsp)], child: Text(t.statCellNum.dsp.replaceAll(RegExp(r'\n'), " "))), DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.nerdStats != null) FlSpot(tl.timestamp.millisecondsSinceEpoch.toDouble(), tl.nerdStats!.appdsp)], child: const Text("APP + DS/P")), DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.nerdStats != null) FlSpot(tl.timestamp.millisecondsSinceEpoch.toDouble(), tl.nerdStats!.vsapm)], child: const Text("VS/APM")), - DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.nerdStats != null) FlSpot(tl.timestamp.millisecondsSinceEpoch.toDouble(), tl.nerdStats!.cheese)], child: const Text("Cheese Index")), - DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.nerdStats != null) FlSpot(tl.timestamp.millisecondsSinceEpoch.toDouble(), tl.nerdStats!.gbe)], child: const Text("Garbage Efficiency")), - DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.nerdStats != null) FlSpot(tl.timestamp.millisecondsSinceEpoch.toDouble(), tl.nerdStats!.nyaapp)], child: const Text("Weighted APP")), - DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.nerdStats != null) FlSpot(tl.timestamp.millisecondsSinceEpoch.toDouble(), tl.nerdStats!.area)], child: const Text("Area")), - DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.estTr != null) FlSpot(tl.timestamp.millisecondsSinceEpoch.toDouble(), tl.estTr!.esttr)], child: const Text("Est. of TR")), - DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.esttracc != null) FlSpot(tl.timestamp.millisecondsSinceEpoch.toDouble(), tl.esttracc!)], child: const Text("Accuracy of Est.")), + DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.nerdStats != null) FlSpot(tl.timestamp.millisecondsSinceEpoch.toDouble(), tl.nerdStats!.cheese)], child: Text(t.statCellNum.cheese.replaceAll(RegExp(r'\n'), " "))), + DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.nerdStats != null) FlSpot(tl.timestamp.millisecondsSinceEpoch.toDouble(), tl.nerdStats!.gbe)], child: Text(t.statCellNum.gbe.replaceAll(RegExp(r'\n'), " "))), + DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.nerdStats != null) FlSpot(tl.timestamp.millisecondsSinceEpoch.toDouble(), tl.nerdStats!.nyaapp)], child: Text(t.statCellNum.nyaapp.replaceAll(RegExp(r'\n'), " "))), + DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.nerdStats != null) FlSpot(tl.timestamp.millisecondsSinceEpoch.toDouble(), tl.nerdStats!.area)], child: Text(t.statCellNum.area.replaceAll(RegExp(r'\n'), " "))), + DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.estTr != null) FlSpot(tl.timestamp.millisecondsSinceEpoch.toDouble(), tl.estTr!.esttr)], child: Text(t.statCellNum.estOfTR.replaceAll(RegExp(r'\n'), " "))), + DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.esttracc != null) FlSpot(tl.timestamp.millisecondsSinceEpoch.toDouble(), tl.esttracc!)], child: Text(t.statCellNum.accOfEst.replaceAll(RegExp(r'\n'), " "))), ]; tlMatches.addAll(await teto.getTLMatchesbyPlayerID(me.userId)); for (var match in tlStream.records) { @@ -208,7 +207,7 @@ class _MainState extends State with SingleTickerProviderStateMixin { }); }, icon: const Icon(Icons.search), - tooltip: "Search player", + tooltip: t.openSearch, ) : IconButton( onPressed: () { @@ -217,7 +216,7 @@ class _MainState extends State with SingleTickerProviderStateMixin { }); }, icon: const Icon(Icons.clear), - tooltip: "Close search", + tooltip: t.closeSearch, ), PopupMenuButton( itemBuilder: (BuildContext context) => [ @@ -564,7 +563,7 @@ class _HistoryChartThigy extends StatelessWidget{ bottomTitles: AxisTitles(sideTitles: SideTitles(interval: xInterval, showTitles: true, reservedSize: 30, getTitlesWidget: (double value, TitleMeta meta){ return value != meta.min && value != meta.max ? SideTitleWidget( axisSide: meta.axisSide, - child: Text(DateFormat(DateFormat.YEAR_ABBR_MONTH_DAY).format(DateTime.fromMillisecondsSinceEpoch(value.floor()))), + child: Text(DateFormat.yMMMd(LocaleSettings.currentLocale.languageCode) .format(DateTime.fromMillisecondsSinceEpoch(value.floor()))), ) : Container(); })), leftTitles: AxisTitles(sideTitles: SideTitles(showTitles: true, reservedSize: leftSpace, getTitlesWidget: (double value, TitleMeta meta){ diff --git a/lib/views/ranks_averages_view.dart b/lib/views/ranks_averages_view.dart index 8005e33..20a7edf 100644 --- a/lib/views/ranks_averages_view.dart +++ b/lib/views/ranks_averages_view.dart @@ -1,4 +1,6 @@ import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; +import 'package:tetra_stats/gen/strings.g.dart'; import 'package:tetra_stats/views/tl_leaderboard_view.dart'; class RankAveragesView extends StatefulWidget { @@ -23,9 +25,10 @@ class RanksAverages extends State { @override Widget build(BuildContext context) { + final NumberFormat f2 = NumberFormat.decimalPattern(LocaleSettings.currentLocale.languageCode)..maximumFractionDigits = 2; return Scaffold( appBar: AppBar( - title: const Text("Ranks averages"), + title: Text(t.rankAveragesViewTitle), ), backgroundColor: Colors.black, body: SafeArea( diff --git a/lib/views/settings_view.dart b/lib/views/settings_view.dart index a316ca7..8fbc9b2 100644 --- a/lib/views/settings_view.dart +++ b/lib/views/settings_view.dart @@ -221,7 +221,14 @@ class SettingsState extends State { trailing: DropdownButton( items: locales, value: LocaleSettings.currentLocale, - onChanged: (value) => LocaleSettings.setLocale(value!), + onChanged: (value){ + LocaleSettings.setLocale(value!); + if(value.languageCode == Platform.localeName.substring(0, 2)){ + prefs.remove('locale'); + }else{ + prefs.setString('locale', value.languageCode); + } + }, ), ), const Divider(), diff --git a/lib/views/tl_leaderboard_view.dart b/lib/views/tl_leaderboard_view.dart index 93d62e8..61b7b9e 100644 --- a/lib/views/tl_leaderboard_view.dart +++ b/lib/views/tl_leaderboard_view.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; +import 'package:tetra_stats/gen/strings.g.dart'; import 'package:tetra_stats/services/tetrio_crud.dart'; import 'package:tetra_stats/views/main_view.dart'; import 'package:tetra_stats/views/ranks_averages_view.dart'; @@ -13,15 +14,15 @@ class TLLeaderboardView extends StatefulWidget { State createState() => TLLeaderboardState(); } -final DateFormat dateFormat = DateFormat.yMMMd().add_Hms(); -final NumberFormat f2 = NumberFormat.decimalPatternDigits(decimalDigits: 2); class TLLeaderboardState extends State { @override Widget build(BuildContext context) { + final t = Translations.of(context); + final NumberFormat f2 = NumberFormat.decimalPattern(LocaleSettings.currentLocale.languageCode)..maximumFractionDigits = 2; return Scaffold( appBar: AppBar( - title: const Text("Tetra League Leaderboard"), + title: Text(t.tlLeaderboard), actions: [ IconButton( onPressed: () { @@ -34,7 +35,7 @@ class TLLeaderboardState extends State { ); }, icon: const Icon(Icons.compress), - tooltip: "Averages", + tooltip: t.averages, ), ], ), @@ -54,9 +55,9 @@ class TLLeaderboardState extends State { headerSliverBuilder: (context, value) { String howManyPlayers(int numberOfPlayers) => Intl.plural( numberOfPlayers, - zero: 'Empty list. Looks like something is wrong...', - one: 'There is only one player... What?', - other: 'There are $numberOfPlayers ranked players.', + zero: t.lbViewZeroEntrys, + one: t.lbViewOneEntry, + other: t.lbViewManyEntrys(numberOfPlayers: numberOfPlayers), name: 'howManyPeople', args: [numberOfPlayers], desc: 'Description of how many people are seen in a place.', diff --git a/lib/views/tl_match_view.dart b/lib/views/tl_match_view.dart index 54c6ffc..3d133a8 100644 --- a/lib/views/tl_match_view.dart +++ b/lib/views/tl_match_view.dart @@ -207,7 +207,7 @@ class TlMatchResultState extends State { higherIsBetter: true, ), CompareThingy( - label: t.statCellNum.estOfTR, + label: t.statCellNum.estOfTRShort, greenSide: widget.record.endContext.firstWhere((element) => element.userId == widget.initPlayerId).estTr.esttr, redSide: widget.record.endContext.firstWhere((element) => element.userId != widget.initPlayerId).estTr.esttr, fractionDigits: 2, @@ -475,8 +475,7 @@ class CompareThingy extends StatelessWidget { @override Widget build(BuildContext context) { - var f = NumberFormat("#,###.##"); - f.maximumFractionDigits = fractionDigits ?? 0; + NumberFormat f = NumberFormat.decimalPatternDigits(locale: LocaleSettings.currentLocale.languageCode, decimalDigits: fractionDigits ?? 0); return Padding( padding: const EdgeInsets.fromLTRB(16, 2, 16, 2), child: Row( diff --git a/lib/widgets/stat_sell_num.dart b/lib/widgets/stat_sell_num.dart index cebcf5a..6ddf718 100644 --- a/lib/widgets/stat_sell_num.dart +++ b/lib/widgets/stat_sell_num.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; +import 'package:tetra_stats/gen/strings.g.dart'; class StatCellNum extends StatelessWidget { const StatCellNum( @@ -24,8 +25,7 @@ class StatCellNum extends StatelessWidget { @override Widget build(BuildContext context) { - NumberFormat f = - NumberFormat.decimalPatternDigits(decimalDigits: fractionDigits ?? 0); + NumberFormat f = NumberFormat.decimalPatternDigits(locale: LocaleSettings.currentLocale.languageCode, decimalDigits: fractionDigits ?? 0); return Column( children: [ Text( @@ -54,7 +54,7 @@ class StatCellNum extends StatelessWidget { showDialog( context: context, builder: (BuildContext context) => AlertDialog( - title: Text(playerStatLabel, + title: Text(playerStatLabel.replaceAll(RegExp(r'\n'), " "), style: const TextStyle( fontFamily: "Eurostile Round Extended")), content: SingleChildScrollView( diff --git a/lib/widgets/tl_thingy.dart b/lib/widgets/tl_thingy.dart index adedad3..a31acdc 100644 --- a/lib/widgets/tl_thingy.dart +++ b/lib/widgets/tl_thingy.dart @@ -7,8 +7,6 @@ import 'package:tetra_stats/gen/strings.g.dart'; import 'package:tetra_stats/widgets/stat_sell_num.dart'; var fDiff = NumberFormat("+#,###.###;-#,###.###"); -final NumberFormat f2 = NumberFormat.decimalPatternDigits(decimalDigits: 2); -final NumberFormat f3 = NumberFormat.decimalPatternDigits(decimalDigits: 3); class TLThingy extends StatelessWidget { final TetraLeagueAlpha tl; @@ -20,6 +18,8 @@ class TLThingy extends StatelessWidget { Widget build(BuildContext context) { final t = Translations.of(context); final DateFormat dateFormat = DateFormat.yMMMd(LocaleSettings.currentLocale.languageCode).add_Hms(); + final NumberFormat f2 = NumberFormat.decimalPatternDigits(locale: LocaleSettings.currentLocale.languageCode, decimalDigits: 2); + final NumberFormat f3 = NumberFormat.decimalPatternDigits(locale: LocaleSettings.currentLocale.languageCode, decimalDigits: 3); return LayoutBuilder(builder: (context, constraints) { bool bigScreen = constraints.maxWidth > 768; return ListView.builder( @@ -315,7 +315,7 @@ class TLThingy extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( - "${t.statCellNum.estOfTR}:", + "${bigScreen ? t.statCellNum.estOfTR : t.statCellNum.estOfTRShort}:", style: const TextStyle(fontSize: 24), ), Text( @@ -329,7 +329,7 @@ class TLThingy extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( - "${t.statCellNum.accOfEst}:", + "${bigScreen ? t.statCellNum.accOfEst : t.statCellNum.accOfEstShort}:", style: const TextStyle(fontSize: 24), ), Text( diff --git a/lib/widgets/user_thingy.dart b/lib/widgets/user_thingy.dart index 17bed01..751b0d2 100644 --- a/lib/widgets/user_thingy.dart +++ b/lib/widgets/user_thingy.dart @@ -170,7 +170,7 @@ 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("${t.statCellNum.xpProgress}: ${((player.level - player.level.floor()) * 100).toStringAsFixed(2)} %"), Text("${t.statCellNum.xpFrom0To5000}: ${((player.xp / 67009017.7589378) * 100).toStringAsFixed(2)} %")], + alertWidgets: [Text("${NumberFormat.decimalPatternDigits(locale: LocaleSettings.currentLocale.languageCode, 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, ), diff --git a/res/i18n/strings.i18n.json b/res/i18n/strings.i18n.json index d4c95a6..9d1aefa 100644 --- a/res/i18n/strings.i18n.json +++ b/res/i18n/strings.i18n.json @@ -11,6 +11,8 @@ "other": "Other", "zen": "Zen", "bio": "Bio", + "openSearch": "Search player", + "closeSearch": "Close search", "refresh": "Refresh", "showStoredData": "Show stored data", "statsCalc": "Stats Calculator", @@ -94,6 +96,11 @@ "fromBeginning": "From beginning", "calc": "Calc", "calcViewNoValues": "Enter values to calculate the stats", + "rankAveragesViewTitle": "Ranks cutoff and average stats", + "averages": "Averages", + "lbViewZeroEntrys": "Empty list. Looks like something is wrong...", + "lbViewOneEntry": "There is only one player... What?", + "lbViewManyEntrys": "There are ${numberOfPlayers} ranked players.", "statCellNum":{ "xpLevel": "XP Level", "xpProgress": "Progress to next level", @@ -121,6 +128,7 @@ "keys": "Key\nPresses", "kpp": "KP Per\nPiece", "kps": "KP Per\nSecond", + "tr": "Tetra Rating", "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", @@ -139,7 +147,9 @@ "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" + "estOfTRShort": "Est. TR", + "accOfEst": "Accuracy", + "accOfEstShort": "Acc." }, "playerRole(map)": { "user": "User", diff --git a/res/i18n/strings_ru.i18n.json b/res/i18n/strings_ru.i18n.json index 562fae6..742e46f 100644 --- a/res/i18n/strings_ru.i18n.json +++ b/res/i18n/strings_ru.i18n.json @@ -11,6 +11,8 @@ "other": "Другое", "zen": "Дзен", "bio": "Биография", + "openSearch": "Искать игрока", + "closeSearch": "Закрыть поиск", "refresh": "Обновить", "showStoredData": "Показать сохранённые данные", "statsCalc": "Калькулятор статистики", @@ -20,7 +22,7 @@ "becameTracked": "Добавлен в список отслеживания!", "stoppedBeingTracked": "Удалён из списка отслеживания!", "compare": "Сравнить", - "tlLeaderboard": "Таблица лидеров Тетра Лиги", + "tlLeaderboard": "Рейтинговая таблица", "noRecords": "Нет записей", "noRecord": "Нет рекорда", "notEnoughData": "Недостаточно данных", @@ -90,10 +92,15 @@ "yes": "Да", "no": "Нет", "daysLater": "дней позже", - "dayseBefore": "дней до", + "dayseBefore": "дней раньше", "fromBeginning": "С начала", "calc": "Считать", "calcViewNoValues": "Введите значения, чтобы посчитать статистику", + "rankAveragesViewTitle": "Требования рангов и средние значения", + "averages": "Средние значения", + "lbViewZeroEntrys": "Рейтинговая таблица пуста. Похоже, что-то здесь не так...", + "lbViewOneEntry": "В рейтинговой таблице всего один игрок... Чего?", + "lbViewManyEntrys": "В рейтинговой таблице находится ${numberOfPlayers} игроков.", "statCellNum": { "xpLevel": "Уровень\nопыта", "xpProgress": "Прогресс до следующего уровня", @@ -121,6 +128,7 @@ "keys": "Нажатий\nКлавиш", "kpp": "Нажатий\nна Фигуру", "kps": "Нажатий\nв Секунду", + "tr": "Тетра Рейтинг", "app": "Атака на Фигуру", "appDescription": "(Сокращенно APP) Главный показатель эффективности. Показывает, сколько атаки приходится на одну фигуру", "vsapmDescription": "В основном, показывает как много мусора игрок использует в своих атаках и насколько эффективно.", @@ -139,7 +147,9 @@ "area": "Area", "areaDescription": "Какую площадь занимает диаграмма, если не брать в расчёт индекс сыра и VS/APM", "estOfTR": "Расчётный TR", - "accOfEst": "Точность расчёта" + "estOfTRShort": "Расч. TR", + "accOfEst": "Точность расчёта", + "accOfEstShort": "Точность" }, "playerRole(map)": { "user": "Пользователь",