From 1c33b44e522883d04dd067f4aa20cac6edf068bb Mon Sep 17 00:00:00 2001 From: Demitry235 Date: Fri, 30 Jan 2026 15:34:08 +0300 Subject: [PATCH] Feat: Add Launcher Localization support (i18n) & Russian language --- Launcher/l10n.yaml | 5 + Launcher/lib/core/routing/app_router.dart | 32 +- .../screens/download_manager.dart | 54 +- .../screens/create_frosty_collection.dart | 64 +- Launcher/lib/features/kyber/models/modes.dart | 46 ++ .../features/maxima/screens/maxima_login.dart | 129 ++-- .../widgets/maxima_navigation_bar_widget.dart | 53 +- .../mod_browser/screens/mod_browser.dart | 17 +- .../mod_browser/screens/mod_details.dart | 86 +-- .../mod_browser/screens/nexus_profile.dart | 113 ++- .../mod_browser/widgets/filter_dropdown.dart | 37 +- .../screens/collection_import.dart | 27 +- .../mods/dialogs/move_directory_dialog.dart | 41 +- Launcher/lib/features/mods/screens/mods.dart | 43 +- .../collection_box/collection_box.dart | 76 ++- .../collection_list/collection_entry.dart | 22 +- .../screens/navigation_bar.dart | 11 +- .../nexusmods/dialogs/nexusmods_login.dart | 66 +- .../nexusmods/screens/nexus_auth_screen.dart | 21 +- .../features/reports/screens/report_view.dart | 101 ++- .../lib/features/reports/screens/reports.dart | 69 +- .../server_browser/screens/ingame_view.dart | 33 +- .../screens/server_browser.dart | 35 +- .../server_browser/widgets/event_list.dart | 38 +- .../server_list/server_list_header.dart | 30 +- .../server_host/screens/server_host.dart | 24 +- .../create_server/map_rotation_page.dart | 42 +- .../settings/server_settings.dart | 26 +- .../screens/moderation_server_list.dart | 20 +- .../screens/server_moderation.dart | 156 +++-- .../settings/screens/background_selector.dart | 48 +- .../screens/pages/accounts_and_updates.dart | 85 +-- .../pages/language_and_accessibility.dart | 226 +++++- .../screens/pages/logs_and_activity.dart | 30 +- .../settings/screens/pages/mod_support.dart | 19 +- .../screens/pages/proximity_chat.dart | 43 +- .../settings/screens/settings_list.dart | 93 +-- .../setup/screens/walk_through_setup.dart | 21 +- .../features/social/screens/social_home.dart | 72 +- .../screens/personal/stats_overview.dart | 115 ++-- .../stats/widgets/stats_player_search.dart | 25 +- Launcher/lib/l10n/app_de.arb | 643 ++++++++++++++++++ Launcher/lib/l10n/app_en.arb | 643 ++++++++++++++++++ Launcher/lib/l10n/app_es.arb | 643 ++++++++++++++++++ Launcher/lib/l10n/app_fr.arb | 643 ++++++++++++++++++ Launcher/lib/l10n/app_nl.arb | 643 ++++++++++++++++++ Launcher/lib/l10n/app_pl.arb | 643 ++++++++++++++++++ Launcher/lib/l10n/app_pt.arb | 643 ++++++++++++++++++ Launcher/lib/l10n/app_ru.arb | 643 ++++++++++++++++++ Launcher/lib/l10n/app_sv.arb | 643 ++++++++++++++++++ Launcher/lib/l10n/app_uk.arb | 643 ++++++++++++++++++ Launcher/lib/main.dart | 209 +++--- .../shared/ui/elements/filter_dropdown.dart | 33 +- .../table_elements/kyber_table_switch.dart | 59 +- .../navigation_bar/navigation_bar_list.dart | 45 +- Launcher/pubspec.yaml | 2 + 56 files changed, 8050 insertions(+), 1022 deletions(-) create mode 100644 Launcher/l10n.yaml create mode 100644 Launcher/lib/l10n/app_de.arb create mode 100644 Launcher/lib/l10n/app_en.arb create mode 100644 Launcher/lib/l10n/app_es.arb create mode 100644 Launcher/lib/l10n/app_fr.arb create mode 100644 Launcher/lib/l10n/app_nl.arb create mode 100644 Launcher/lib/l10n/app_pl.arb create mode 100644 Launcher/lib/l10n/app_pt.arb create mode 100644 Launcher/lib/l10n/app_ru.arb create mode 100644 Launcher/lib/l10n/app_sv.arb create mode 100644 Launcher/lib/l10n/app_uk.arb diff --git a/Launcher/l10n.yaml b/Launcher/l10n.yaml new file mode 100644 index 00000000..764d06d0 --- /dev/null +++ b/Launcher/l10n.yaml @@ -0,0 +1,5 @@ +arb-dir: lib/l10n +template-arb-file: app_en.arb +output-localization-file: app_localizations.dart +output-class: AppLocalizations +output-dir: lib/gen/l10n \ No newline at end of file diff --git a/Launcher/lib/core/routing/app_router.dart b/Launcher/lib/core/routing/app_router.dart index 0042aec6..39c47a75 100644 --- a/Launcher/lib/core/routing/app_router.dart +++ b/Launcher/lib/core/routing/app_router.dart @@ -31,6 +31,8 @@ import 'package:kyber_launcher/features/social/screens/social_home.dart'; import 'package:kyber_launcher/features/stats/providers/stats_cubit.dart'; import 'package:kyber_launcher/features/stats/screens/personal/stats_overview.dart'; import 'package:kyber_launcher/features/stats/screens/stats.dart'; +import 'package:kyber_launcher/gen/fonts.gen.dart'; +import 'package:kyber_launcher/gen/l10n/app_localizations.dart'; import 'package:kyber_launcher/injection_container.dart'; import 'package:kyber_launcher/main.dart'; import 'package:sentry_flutter/sentry_flutter.dart'; @@ -189,22 +191,33 @@ final router = GoRouter( initialLocation: '/home', observers: [SentryNavigatorObserver()], errorBuilder: (context, state) { + final l10n = AppLocalizations.of(context)!; + final isEn = Localizations.localeOf(context).languageCode == 'en'; + final currentFont = isEn ? FontFamily.battlefrontUI : 'BattlefrontGlobal'; + return SafeArea( child: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - const Text( - 'Page Not Found', - style: TextStyle(fontWeight: FontWeight.bold), + Text( + l10n.pageNotFoundTitle, + style: TextStyle( + fontFamily: currentFont, + fontWeight: FontWeight.bold, + ), ), const SizedBox(height: 16), - Text(state.error?.toString() ?? 'page not found'), + Text( + state.error?.toString() ?? l10n.pageNotFoundFallback, + style: TextStyle(fontFamily: currentFont), + ), const SizedBox(height: 16), Button( onPressed: () => context.go('/'), - child: const Text( - 'Go to home page', + child: Text( + l10n.goToHomePage, + style: TextStyle(fontFamily: currentFont), ), ), ], @@ -370,7 +383,8 @@ final router = GoRouter( //TODO: check for unsaved changes return true; }, - pageBuilder: (_, state) { + pageBuilder: (context, state) { + final l10n = AppLocalizations.of(context)!; return buildCustomPage( state: state, child: MultiBlocProvider( @@ -393,7 +407,7 @@ final router = GoRouter( if (collectionQuery == 'new') { collection = ModCollectionMetaData( localId: const Uuid().v4(), - title: 'New Collection', + title: l10n.newCollectionTitle, mods: [], ); } else { @@ -522,4 +536,4 @@ final router = GoRouter( ], ), ], -); +); \ No newline at end of file diff --git a/Launcher/lib/features/download_manager/screens/download_manager.dart b/Launcher/lib/features/download_manager/screens/download_manager.dart index 0b9f8f83..1c9878c1 100644 --- a/Launcher/lib/features/download_manager/screens/download_manager.dart +++ b/Launcher/lib/features/download_manager/screens/download_manager.dart @@ -12,6 +12,7 @@ import 'package:kyber_launcher/features/nexusmods/services/nexusmods_service.dar import 'package:kyber_launcher/features/settings/dialogs/chromium_download_dialog.dart'; import 'package:kyber_launcher/gen/assets.gen.dart'; import 'package:kyber_launcher/gen/fonts.gen.dart'; +import 'package:kyber_launcher/gen/l10n/app_localizations.dart'; import 'package:kyber_launcher/injection_container.dart'; import 'package:kyber_launcher/shared/ui/buttons/custom_icon_button.dart'; import 'package:kyber_launcher/shared/ui/cards/kyber_container.dart'; @@ -120,16 +121,20 @@ class _DownloadManagerHeader extends StatelessWidget { @override Widget build(BuildContext context) { + final l10n = AppLocalizations.of(context)!; + final isEn = Localizations.localeOf(context).languageCode == 'en'; + final currentFont = isEn ? FontFamily.battlefrontUI : 'BattlefrontGlobal'; + return Padding( padding: const EdgeInsets.all(15), child: Row( children: [ - const Expanded( + Expanded( child: Column( crossAxisAlignment: .start, children: [ - Text( - 'DOWNLOAD MANAGER', + const Text( + 'DOWNLOAD MANAGER', style: TextStyle( fontFamily: FontFamily.aurebesh, fontSize: 14, @@ -138,9 +143,9 @@ class _DownloadManagerHeader extends StatelessWidget { ), ), Text( - 'DOWNLOAD MANAGER', + l10n.downloadManager.toUpperCase(), style: TextStyle( - fontFamily: FontFamily.battlefrontUI, + fontFamily: currentFont, fontSize: 24, height: 1, ), @@ -196,15 +201,19 @@ class _PausedDownloadsSection extends StatelessWidget { @override Widget build(BuildContext context) { + final l10n = AppLocalizations.of(context)!; + final isEn = Localizations.localeOf(context).languageCode == 'en'; + final currentFont = isEn ? FontFamily.battlefrontUI : 'BattlefrontGlobal'; + return Column( crossAxisAlignment: .start, children: [ - const Padding( - padding: .symmetric(horizontal: 15, vertical: 10), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 10), child: Text( - 'PAUSED DOWNLOADS', + l10n.pausedDownloads.toUpperCase(), style: TextStyle( - fontFamily: FontFamily.battlefrontUI, + fontFamily: currentFont, fontSize: 20, color: kWhiteColor, ), @@ -251,7 +260,7 @@ class _DownloadTaskItem extends StatelessWidget { children: [ _buildLeadingIcon(), const VCardSection(), - Expanded(child: _buildTaskInfo()), + Expanded(child: _buildTaskInfo(context)), if (task.status.isNotFinalState) ...[ const VCardSection(), _buildCancelButton(), @@ -301,7 +310,10 @@ class _DownloadTaskItem extends StatelessWidget { ); } - Widget _buildTaskInfo() { + Widget _buildTaskInfo(BuildContext context) { + final isEn = Localizations.localeOf(context).languageCode == 'en'; + final currentFont = isEn ? FontFamily.battlefrontUI : 'BattlefrontGlobal'; + return Stack( children: [ Positioned.fill( @@ -312,8 +324,8 @@ class _DownloadTaskItem extends StatelessWidget { children: [ Text( task.task.displayName, - style: const TextStyle( - fontFamily: FontFamily.battlefrontUI, + style: TextStyle( + fontFamily: currentFont, fontSize: 18, height: 1, color: kWhiteColor, @@ -426,25 +438,29 @@ class _NexusPremiumBanner extends StatelessWidget { @override Widget build(BuildContext context) { + final l10n = AppLocalizations.of(context)!; + final isEn = Localizations.localeOf(context).languageCode == 'en'; + final currentFont = isEn ? FontFamily.battlefrontUI : 'BattlefrontGlobal'; + return Center( child: Container( padding: const EdgeInsets.all(15), child: RichText( text: TextSpan( children: [ - const TextSpan( - text: 'UN-CAP DOWNLOAD SPEEDS WITH ', + TextSpan( + text: l10n.unCapSpeedPrefix, style: TextStyle( color: kGrayColor, - fontFamily: FontFamily.battlefrontUI, + fontFamily: currentFont, fontSize: 16, ), ), TextSpan( - text: 'NEXUS MODS PREMIUM', + text: l10n.nexusModsPremium, style: TextStyle( color: kActiveColor, - fontFamily: FontFamily.battlefrontUI, + fontFamily: currentFont, fontSize: 16, ), recognizer: TapGestureRecognizer() @@ -629,4 +645,4 @@ class _DownloadProgressIndicator extends StatelessWidget { ), ); } -} +} \ No newline at end of file diff --git a/Launcher/lib/features/frosty/screens/create_frosty_collection.dart b/Launcher/lib/features/frosty/screens/create_frosty_collection.dart index f2b6bb25..246e7494 100644 --- a/Launcher/lib/features/frosty/screens/create_frosty_collection.dart +++ b/Launcher/lib/features/frosty/screens/create_frosty_collection.dart @@ -10,6 +10,7 @@ import 'package:kyber_launcher/core/config/colors.dart'; import 'package:kyber_launcher/core/services/notification_service.dart'; import 'package:kyber_launcher/features/frosty/helper/frosty_collection_writer.dart'; import 'package:kyber_launcher/gen/fonts.gen.dart'; +import 'package:kyber_launcher/gen/l10n/app_localizations.dart'; import 'package:kyber_launcher/shared/ui/buttons/button.dart'; import 'package:kyber_launcher/shared/ui/buttons/custom_icon_button.dart'; import 'package:kyber_launcher/shared/ui/cards/kyber_container.dart'; @@ -38,6 +39,10 @@ class _CreateFrostyCollectionState extends State { @override Widget build(BuildContext context) { + final l10n = AppLocalizations.of(context)!; + final isEn = Localizations.localeOf(context).languageCode == 'en'; + final currentFont = isEn ? FontFamily.battlefrontUI : 'BattlefrontGlobal'; + return Row( spacing: 15, children: [ @@ -47,17 +52,17 @@ class _CreateFrostyCollectionState extends State { child: Column( children: [ Padding( - padding: EdgeInsets.all(14), + padding: const EdgeInsets.all(14), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ KyberButton( - text: 'LOAD MODS', + text: l10n.loadMods, onPressed: () async { final result = await FilePicker.platform.pickFiles( allowedExtensions: ['fbmod'], allowMultiple: true, - dialogTitle: 'Select mods', + dialogTitle: l10n.selectMods, type: FileType.custom, ); @@ -73,8 +78,7 @@ class _CreateFrostyCollectionState extends State { if (mods.map((e) => e.filename).contains(path)) { paths.remove(path); NotificationService.error( - message: - 'Ignoring ${p.basename(path)}. (Duplicate)', + message: l10n.ignoringDuplicate(p.basename(path)), ); } } @@ -91,9 +95,9 @@ class _CreateFrostyCollectionState extends State { .toList(); final categoryCount = categories .fold>({}, (prev, element) { - prev[element] = (prev[element] ?? 0) + 1; - return prev; - }); + prev[element] = (prev[element] ?? 0) + 1; + return prev; + }); final sortedCategories = categoryCount.entries.toList() ..sort((a, b) => b.value.compareTo(a.value)); @@ -111,7 +115,7 @@ class _CreateFrostyCollectionState extends State { }, ), KyberButton( - text: 'EXPORT', + text: l10n.export, onPressed: () async { //final targetFile = await FilePicker.platform.saveFile( // dialogTitle: 'Save collection', @@ -122,10 +126,10 @@ class _CreateFrostyCollectionState extends State { final targetFileZip = await FilePicker.platform .saveFile( dialogTitle: 'Save collection', - allowedExtensions: ['zip'], - fileName: 'collection.zip', - type: FileType.custom, - ); + allowedExtensions: ['zip'], + fileName: 'collection.zip', + type: FileType.custom, + ); final paths = mods.map((e) => e.filename).toList(); final data = FrostyCollectionWriter( @@ -309,7 +313,7 @@ class _CreateFrostyCollectionState extends State { onClick: () async { final result = await FilePicker.platform.pickFiles( allowedExtensions: ['png', 'jpg', 'jpeg'], - dialogTitle: 'Select icon', + dialogTitle: l10n.selectIcon, type: FileType.custom, ); @@ -352,8 +356,8 @@ class _CreateFrostyCollectionState extends State { if (icon == null) Center( child: Text( - 'ICON', - style: TextStyle( + l10n.icon, + style: const TextStyle( color: kButtonBorder, fontSize: 12, ), @@ -385,22 +389,22 @@ class _CreateFrostyCollectionState extends State { ), Expanded( child: KyberInput( - placeholder: 'Name', + placeholder: l10n.name, controller: nameController, ), ), ], ), KyberInput( - placeholder: 'Author', + placeholder: l10n.author, controller: authorController, ), KyberInput( - placeholder: 'Version', + placeholder: l10n.version, controller: versionController, ), KyberInput( - placeholder: 'Category', + placeholder: l10n.category, controller: categoryController, ), ], @@ -481,31 +485,33 @@ class _ExportCollectionDialogState extends State<_ExportCollectionDialog> { for (final file in widget.filePaths) { print('Adding file: $file'); await encoder.addFile(File(file)); - setState(() { - progress = (progress.$1 + 1, progress.$2); - }); + setState(() { + progress = (progress.$1 + 1, progress.$2); + }); } await encoder.close(); - Navigator.of(context).pop(); + Navigator.of(context).pop(); }); super.initState(); } @override Widget build(BuildContext context) { + final l10n = AppLocalizations.of(context)!; + return KyberContentDialog( - title: Text('EXPORT COLLECTION'), - constraints: BoxConstraints(maxWidth: 600, maxHeight: 400), + title: Text(l10n.exportCollection), + constraints: const BoxConstraints(maxWidth: 600, maxHeight: 400), content: Column( children: [ - Text('Exporting collection...'), + Text(l10n.exportingCollection), ProgressBar( value: ((progress.$1) / progress.$2) * 100, ), - Text(p.basename(widget.filePaths[progress.$1])), + Text(p.basename(widget.filePaths[progress.$1])), ], ), ); } -} +} \ No newline at end of file diff --git a/Launcher/lib/features/kyber/models/modes.dart b/Launcher/lib/features/kyber/models/modes.dart index 27e02cb8..a5550d9a 100644 --- a/Launcher/lib/features/kyber/models/modes.dart +++ b/Launcher/lib/features/kyber/models/modes.dart @@ -1,4 +1,7 @@ +import 'package:fluent_ui/fluent_ui.dart'; import 'package:kyber_launcher/features/kyber/models/mode.dart'; +import 'package:kyber_launcher/gen/l10n/app_localizations.dart'; +import 'package:kyber/kyber.dart'; final List modes = modesFromJson([ { @@ -337,3 +340,46 @@ final List modes = modesFromJson([ ], }, ]); + +/// Расширение для локализации игровых режимов в UI. +extension ModeLocalization on Mode { + String getLocalizedName(BuildContext context) { + final l10n = AppLocalizations.of(context)!; + return switch (mode) { + 'HeroesVersusVillains' => l10n.modeHeroesVersusVillains, + 'PlanetaryBattles' => l10n.modeGalacticAssault, + 'Mode1' => l10n.modeSupremacy, + 'Mode9' => l10n.modeCoOpAttack, + 'ModeDefend' => l10n.modeCoOpDefend, + 'PlanetaryMissions' => l10n.modeStrike, + 'Mode5' => l10n.modeExtraction, + 'Blast' => l10n.modeBlast, + 'Mode3' => l10n.modeEwokHunt, + 'ModeC' => l10n.modeJetpackCargo, + 'SpaceBattle' => l10n.modeStarfighterAssault, + 'Mode7' => l10n.modeHeroStarfighters, + 'Mode6' => l10n.modeHeroShowdown, + _ => name, + }; + } +} + +extension MapLocalization on KyberMap { + String getLocalizedName(BuildContext context) { + final l10n = AppLocalizations.of(context)!; + return switch (name) { + 'Tatooine - Mos Eisley' => l10n.mapTatooineMosEisley, + "Tatooine - Jabba's Palace" => l10n.mapTatooineJabbasPalace, + 'Republic Venator' => l10n.mapRepublicVenator, + 'Separatist Dreadnought' => l10n.mapSeparatistDreadnought, + 'Resurgent Star Destroyer' => l10n.mapResurgentStarDestroyer, + 'MC85 Star Cruiser' => l10n.mapMc85StarCruiser, + 'Naboo - Palace Hangar' => l10n.mapNabooPalaceHangar, + 'Endor - Research Station 9' => l10n.mapEndorResearchStation9, + 'Endor - Ewok Village' => l10n.mapEndorEwokVillage, + 'Naboo - Theed Palace' => l10n.mapNabooTheedPalace, + 'Crait' => l10n.mapCrait, + _ => name, + }; + } +} \ No newline at end of file diff --git a/Launcher/lib/features/maxima/screens/maxima_login.dart b/Launcher/lib/features/maxima/screens/maxima_login.dart index dbae99ac..02b92e0b 100644 --- a/Launcher/lib/features/maxima/screens/maxima_login.dart +++ b/Launcher/lib/features/maxima/screens/maxima_login.dart @@ -11,6 +11,7 @@ import 'package:kyber_launcher/core/services/notification_service.dart'; import 'package:kyber_launcher/features/maxima/providers/maxima_cubit.dart'; import 'package:kyber_launcher/features/patreon/services/patreon_service.dart'; import 'package:kyber_launcher/gen/fonts.gen.dart'; +import 'package:kyber_launcher/gen/l10n/app_localizations.dart'; import 'package:kyber_launcher/shared/ui/buttons/button.dart'; import 'package:kyber_launcher/shared/ui/elements/kyber_input.dart'; import 'package:kyber_launcher/shared/ui/utils/background_blur.dart'; @@ -89,9 +90,11 @@ class _MaximaLoginState extends State { } Widget _buildContent(BuildContext context, MaximaState state) { + final l10n = AppLocalizations.of(context)!; + if (_loading) { return _StatusRow( - text: _whitelistPrompt ? 'Loading...' : 'Waiting for response...', + text: _whitelistPrompt ? l10n.loading : l10n.waitingForResponse, ); } @@ -107,16 +110,17 @@ class _MaximaLoginState extends State { if (errorBlock != null) return errorBlock; return switch (state.status) { - .starting => const _StatusRow(text: 'Maxima is starting...'), - .loading => const _StatusRow(text: 'Fetching data...'), + MaximaStatus.starting => _StatusRow(text: l10n.maximaStarting), + MaximaStatus.loading => _StatusRow(text: l10n.fetchingData), _ => _LoginIntro(onLogin: () => _requestLogin(context)), }; } Widget? _buildErrorBlock(BuildContext context, MaximaState state) { + final l10n = AppLocalizations.of(context)!; if (state.status != MaximaStatus.error) return null; - final error = state.error ?? 'An error occurred'; + final error = state.error ?? l10n.genericError; if (error.contains('GameNotOwned')) { return _GameNotOwned( @@ -141,6 +145,7 @@ class _MaximaLoginState extends State { } Future _requestLogin(BuildContext context) { + final l10n = AppLocalizations.of(context)!; return context.read().requestLogin().onError(( error, stackTrace, @@ -151,47 +156,50 @@ class _MaximaLoginState extends State { ? error.message : (error! as AnyhowException).message; - final message = _mapLoginError(raw); + final message = _mapLoginError(context, raw); return NotificationService.showNotification( - title: 'Login failed', + title: l10n.loginFailed, message: message, severity: InfoBarSeverity.error, ); }); } - String _mapLoginError(String err) { + String _mapLoginError(BuildContext context, String err) { + final l10n = AppLocalizations.of(context)!; if (err.contains('unknown variant `NO_SUCH_USER`')) { - return 'The specified user does not exist'; + return l10n.userDoesNotExist; } if (err.contains('InvalidPassword')) { - return 'The specified password is incorrect'; + return l10n.passwordIncorrect; } if (err.contains('failed to find auth code')) { - return 'Failed to find auth code. Please try another browser'; + return l10n.failedToFindAuthCode; } return err; } void _copyPath(BuildContext context) { + final l10n = AppLocalizations.of(context)!; Clipboard.setData(ClipboardData(text: Directory.current.path)); NotificationService.success( - message: 'The path has been copied to your clipboard', + message: l10n.pathCopied, ); } Future _handleAuthorizePatreon(BuildContext context) async { + final l10n = AppLocalizations.of(context)!; try { setState(() => _loading = true); final code = await PatreonService.requestOAuthLogin(); if (code == null) { NotificationService.error( - message: 'No authorization code was received', + message: l10n.noAuthCodeReceived, ); return; } @@ -205,8 +213,8 @@ class _MaximaLoginState extends State { }); NotificationService.success( - title: 'Authorization successful', - message: 'You have been successfully authorized as a Patreon member', + title: l10n.authorizationSuccessful, + message: l10n.patreonAuthorizedMessage, ); } catch (e, s) { String? message; @@ -216,7 +224,7 @@ class _MaximaLoginState extends State { Logger.root.warning('Failed to authorize Patreon', e, s); NotificationService.showNotification( - title: 'Authorization failed', + title: l10n.authorizationFailed, message: message ?? e.toString(), severity: InfoBarSeverity.error, ); @@ -235,7 +243,7 @@ class _MaximaLoginState extends State { await context.read().requestLogin(skipMaximaCheck: true); } catch (e, s) { final message = switch (e) { - GrpcError() => e.message ?? 'An error occurred', + GrpcError() => e.message ?? AppLocalizations.of(context)!.genericError, PatreonException() => e.message, _ => e.toString(), }; @@ -253,10 +261,14 @@ class _MaximaLoginState extends State { class _Header extends StatelessWidget { @override Widget build(BuildContext context) { + final l10n = AppLocalizations.of(context)!; + final isEn = Localizations.localeOf(context).languageCode == 'en'; + final currentFont = isEn ? FontFamily.battlefrontUI : 'BattlefrontGlobal'; + return Text( - 'EA Login', + l10n.eaLogin, style: FluentTheme.of(context).typography.subtitle?.copyWith( - fontFamily: FontFamily.battlefrontUI, + fontFamily: currentFont, fontSize: 26, color: kActiveColor, shadows: [ @@ -278,8 +290,11 @@ class _StatusRow extends StatelessWidget { @override Widget build(BuildContext context) { + final isEn = Localizations.localeOf(context).languageCode == 'en'; + final currentFont = isEn ? FontFamily.battlefrontUI : 'BattlefrontGlobal'; + return Column( - mainAxisSize: .min, + mainAxisSize: MainAxisSize.min, children: [ const SizedBox(height: 5), Row( @@ -288,8 +303,8 @@ class _StatusRow extends StatelessWidget { const SizedBox(width: 8), Text( text, - style: const TextStyle( - fontFamily: FontFamily.battlefrontUI, + style: TextStyle( + fontFamily: currentFont, fontSize: 18, ), ), @@ -307,19 +322,21 @@ class _LoginIntro extends StatelessWidget { @override Widget build(BuildContext context) { + final l10n = AppLocalizations.of(context)!; + return Column( - mainAxisSize: .min, - crossAxisAlignment: .start, + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - 'In order to use this launcher, you need to login to Maxima. This is required to launch and interact with Battlefront 2.\nYou will be redirected to the EA login page and after logging in, you will be redirected back to the launcher.', + l10n.maximaIntroText, style: FluentTheme.of(context).typography.body, ), const SizedBox(height: 16), Row( children: [ KyberButton( - text: 'Login with EA', + text: l10n.loginWithEa, onPressed: onLogin, ), ], @@ -342,21 +359,23 @@ class _WhitelistRequired extends StatelessWidget { @override Widget build(BuildContext context) { + final l10n = AppLocalizations.of(context)!; + return Column( mainAxisSize: .min, crossAxisAlignment: .start, children: [ DefaultTextStyle.merge( style: const TextStyle(fontSize: 16), - child: Row(children: [Text('EA-ID: $eaId')]), + child: Row(children: [Text(l10n.eaIdDisplay(eaId ?? ''))]), ), const SizedBox(height: 10), Row( mainAxisAlignment: .spaceBetween, children: [ - KyberButton(text: 'Log out', onPressed: onLogout), + KyberButton(text: l10n.logout, onPressed: onLogout), KyberButton( - text: 'Authorize Patreon', + text: l10n.authorizePatreon, onPressed: onAuthorizePatreon, ), ], @@ -379,24 +398,26 @@ class _WhitelistPrompt extends StatelessWidget { @override Widget build(BuildContext context) { + final l10n = AppLocalizations.of(context)!; + return DefaultTextStyle.merge( style: const TextStyle(fontSize: 16), child: Column( mainAxisSize: .min, crossAxisAlignment: .start, children: [ - const Text( - 'Are you sure you want to add your current account to the whitelist?', + Text( + l10n.whitelistConfirmation, ), const SizedBox(height: 10), - Text('Current account: $displayName'), + Text(l10n.currentAccountDisplay(displayName ?? '')), const SizedBox(height: 10), Row( - mainAxisAlignment: .spaceBetween, + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - KyberButton(text: 'Log out', onPressed: onLogout), + KyberButton(text: l10n.logout, onPressed: onLogout), KyberButton( - text: 'Add to whitelist', + text: l10n.addToWhitelist, onPressed: onAddToWhitelist, ), ], @@ -415,22 +436,24 @@ class _GameNotOwned extends StatelessWidget { @override Widget build(BuildContext context) { + final l10n = AppLocalizations.of(context)!; + return Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ const SizedBox(height: 10), DefaultTextStyle.merge( - style: const .new(fontSize: 16), - child: Row(children: [Text('EA-ID: $eaId')]), + style: const TextStyle(fontSize: 16), + child: Row(children: [Text(l10n.eaIdDisplay(eaId ?? ''))]), ), const SizedBox(height: 10), - const Text( - 'It seems like you do not own Battlefront 2. Please purchase the game or use another account.', + Text( + l10n.gameNotOwnedMessage, ), const SizedBox(height: 10), Row( - children: [KyberButton(text: 'Log out', onPressed: onLogout)], + children: [KyberButton(text: l10n.logout, onPressed: onLogout)], ), ], ); @@ -450,6 +473,7 @@ class _MaximaGenericError extends StatelessWidget { @override Widget build(BuildContext context) { + final l10n = AppLocalizations.of(context)!; final bodyStyle = FluentTheme.of( context, ).typography.body?.copyWith(fontSize: 16); @@ -462,19 +486,19 @@ class _MaximaGenericError extends StatelessWidget { builder: (context) { switch (error) { case 'MaximaFailedBackgroundService': - return const Text( - 'Maxima failed to start the background service. This is usually caused by an antivirus program blocking the service. Please add an exception for the launcher and reinstall it.', + return Text( + l10n.maximaBackgroundServiceError, ); case 'MissingMaximaFiles': return Column( crossAxisAlignment: .start, children: [ - const Text( - 'Some files required to run Maxima are missing. This is usually caused by an antivirus program deleting the files. Please add an exception for the launcher and reinstall it.', + Text( + l10n.maximaMissingFilesError, ), const SizedBox(height: 10), - const Text( - "Please exclude the following folders from your antivirus' real-time protection:", + Text( + l10n.antivirusExclusionInstruction, ), const SizedBox(height: 5), KyberInput( @@ -483,9 +507,9 @@ class _MaximaGenericError extends StatelessWidget { ), const SizedBox(height: 10), Row( - mainAxisAlignment: .spaceBetween, + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - KyberButton(text: 'COPY PATH', onPressed: onCopyPath), + KyberButton(text: l10n.copyPath, onPressed: onCopyPath), ], ), ], @@ -496,7 +520,7 @@ class _MaximaGenericError extends StatelessWidget { }, ), Row( - children: [KyberButton(text: 'Log out', onPressed: onLogout)], + children: [KyberButton(text: l10n.logout, onPressed: onLogout)], ), ], ), @@ -511,6 +535,7 @@ class MaximaErrorWidget extends StatelessWidget { @override Widget build(BuildContext context) { + final l10n = AppLocalizations.of(context)!; final error = state.error ?? ''; if (error.contains('GameNotOwned')) { @@ -522,15 +547,15 @@ class MaximaErrorWidget extends StatelessWidget { if (state.status == MaximaStatus.error && !error.contains('whitelist')) { return _MaximaGenericError( - error: error.isEmpty ? 'An error occurred' : error, + error: error.isEmpty ? l10n.genericError : error, onLogout: () => context.read().logout(), onCopyPath: () async { - await Clipboard.setData(.new(text: Directory.current.path)); + await Clipboard.setData(ClipboardData(text: Directory.current.path)); if (!context.mounted) return; NotificationService.success( - message: 'The path has been copied to your clipboard', + message: l10n.pathCopied, ); }, ); @@ -538,4 +563,4 @@ class MaximaErrorWidget extends StatelessWidget { return const SizedBox.shrink(); } -} +} \ No newline at end of file diff --git a/Launcher/lib/features/maxima/widgets/maxima_navigation_bar_widget.dart b/Launcher/lib/features/maxima/widgets/maxima_navigation_bar_widget.dart index 66247b6c..5e96b714 100644 --- a/Launcher/lib/features/maxima/widgets/maxima_navigation_bar_widget.dart +++ b/Launcher/lib/features/maxima/widgets/maxima_navigation_bar_widget.dart @@ -12,6 +12,7 @@ import 'package:kyber_launcher/features/navigation_bar/widgets/reports.dart'; import 'package:kyber_launcher/features/settings/dialogs/chromium_download_dialog.dart'; import 'package:kyber_launcher/gen/assets.gen.dart'; import 'package:kyber_launcher/gen/fonts.gen.dart'; +import 'package:kyber_launcher/gen/l10n/app_localizations.dart'; import 'package:kyber_launcher/shared/ui/dialog/kyber_dialog.dart'; import 'package:kyber_launcher/shared/ui/utils/button_builder.dart'; @@ -28,6 +29,10 @@ class _MaximaNavigationBarWidgetState extends State { @override Widget build(BuildContext context) { + final l10n = AppLocalizations.of(context)!; + final isEn = Localizations.localeOf(context).languageCode == 'en'; + final currentFont = isEn ? FontFamily.battlefrontUI : 'BattlefrontGlobal'; + return BlocBuilder( buildWhen: (previous, current) { return previous.status != current.status; @@ -87,7 +92,7 @@ class _MaximaNavigationBarWidgetState extends State { milliseconds: 150, ), style: TextStyle( - fontFamily: FontFamily.battlefrontUI, + fontFamily: currentFont, color: hovered ? kActiveColor : Colors.white, @@ -97,7 +102,7 @@ class _MaximaNavigationBarWidgetState extends State { children: [ const Icon(FluentIcons.game), // when a user clicks on it a page opens with a list of all ingame users and an event chat - Text('INGAME PANEL'), + Text(l10n.ingamePanel), ], ), ), @@ -154,7 +159,7 @@ class _MaximaNavigationBarWidgetState extends State { milliseconds: 150, ), style: TextStyle( - fontFamily: FontFamily.battlefrontUI, + fontFamily: currentFont, color: hovered ? kActiveColor : Colors.white, @@ -270,15 +275,15 @@ class _MaximaNavigationBarWidgetState extends State { milliseconds: 150, ), style: TextStyle( - fontFamily: FontFamily.battlefrontUI, + fontFamily: currentFont, color: hovered ? kActiveColor : Colors.white, ), child: Text( '${context.read().state.servicePlayer?.displayName}', - style: const TextStyle( - fontFamily: FontFamily.battlefrontUI, + style: TextStyle( + fontFamily: currentFont, fontSize: 15, ), ), @@ -323,6 +328,10 @@ class _DownloadItem extends StatelessWidget { @override Widget build(BuildContext context) { + final l10n = AppLocalizations.of(context)!; + final isEn = Localizations.localeOf(context).languageCode == 'en'; + final currentFont = isEn ? FontFamily.battlefrontUI : 'BattlefrontGlobal'; + return BlocBuilder( builder: (context, state) { final currentDownload = state is DownloadLoaded @@ -367,7 +376,7 @@ class _DownloadItem extends StatelessWidget { child: AnimatedDefaultTextStyle( duration: const Duration(milliseconds: 150), style: TextStyle( - fontFamily: FontFamily.battlefrontUI, + fontFamily: currentFont, color: hovered ? kActiveColor : Colors.white, ), child: RepaintBoundary( @@ -388,13 +397,18 @@ class _DownloadItem extends StatelessWidget { ? (extractingProgress.extracted / extractingProgress.total) : 1.0; - displayText = - 'EXTRACTING FILE (${extractingProgress?.extracted ?? '?'}/${extractingProgress?.total ?? '?'})'; + displayText = l10n.extractingFileWithProgress( + extractingProgress.extracted.toString(), + extractingProgress.total.toString(), + ); } else if (xProgress >= 1) { - displayText = 'EXTRACTING FILE'; + displayText = l10n.extractingFile; } else { - displayText = - 'DOWNLOADING (${(progress * 100).toInt()}% ${formatBytes((expectedFileSize * progress).toInt(), 1)}/${formatBytes(expectedFileSize, 1)})'; + displayText = l10n.downloadingWithProgress( + (progress * 100).toInt().toString(), + formatBytes((expectedFileSize * progress).toInt(), 1), + formatBytes(expectedFileSize, 1), + ); } return Stack( children: [ @@ -424,9 +438,8 @@ class _DownloadItem extends StatelessWidget { ), Text( displayText, - style: const TextStyle( - fontFamily: - FontFamily.battlefrontUI, + style: TextStyle( + fontFamily: currentFont, fontSize: 14, ), ), @@ -458,19 +471,19 @@ class _DownloadItem extends StatelessWidget { ); } - return const Row( + return Row( mainAxisAlignment: MainAxisAlignment.center, spacing: 6, children: [ - Icon( + const Icon( FluentIcons.download, color: kWhiteColor, size: 15, ), Text( - 'DOWNLOAD MANAGER', + l10n.downloadManager, style: TextStyle( - fontFamily: FontFamily.battlefrontUI, + fontFamily: currentFont, fontSize: 14, ), ), @@ -489,4 +502,4 @@ class _DownloadItem extends StatelessWidget { }, ); } -} +} \ No newline at end of file diff --git a/Launcher/lib/features/mod_browser/screens/mod_browser.dart b/Launcher/lib/features/mod_browser/screens/mod_browser.dart index 1eb0a193..54f5472f 100644 --- a/Launcher/lib/features/mod_browser/screens/mod_browser.dart +++ b/Launcher/lib/features/mod_browser/screens/mod_browser.dart @@ -6,6 +6,7 @@ import 'package:kyber_launcher/features/mod_browser/widgets/nmb_mod_tile.dart'; import 'package:kyber_launcher/features/nexusmods/dialogs/nexusmods_login.dart'; import 'package:kyber_launcher/features/nexusmods/services/nexusmods_service.dart'; import 'package:kyber_launcher/gen/fonts.gen.dart'; +import 'package:kyber_launcher/gen/l10n/app_localizations.dart'; import 'package:kyber_launcher/injection_container.dart'; import 'package:kyber_launcher/shared/ui/buttons/button.dart'; import 'package:kyber_launcher/shared/ui/dialog/kyber_dialog.dart'; @@ -21,6 +22,10 @@ class CategorizedModList extends StatefulWidget { class _CategorizedModListState extends State { @override Widget build(BuildContext context) { + final l10n = AppLocalizations.of(context)!; + final isEn = Localizations.localeOf(context).languageCode == 'en'; + final currentFont = isEn ? FontFamily.battlefrontUI : 'BattlefrontGlobal'; + return FutureBuilder( future: sl.isReady(), builder: (context, snapshot) { @@ -37,12 +42,12 @@ class _CategorizedModListState extends State { mainAxisAlignment: MainAxisAlignment.center, spacing: 20, children: [ - const Text('Please sign in to Nexus Mods to view mods'), + Text(l10n.signInNexusPrompt), Row( mainAxisAlignment: MainAxisAlignment.center, children: [ KyberButton( - text: 'LOGIN', + text: l10n.login, onPressed: () async { await showKyberDialog( context: context, @@ -71,9 +76,9 @@ class _CategorizedModListState extends State { if (searchState.results.isEmpty) { return Center( child: Text( - 'No mods found'.toUpperCase(), - style: const TextStyle( - fontFamily: FontFamily.battlefrontUI, + l10n.noModsFound.toUpperCase(), + style: TextStyle( + fontFamily: currentFont, fontSize: 17, ), ), @@ -163,4 +168,4 @@ class _CategorizedModListState extends State { }, ); } -} +} \ No newline at end of file diff --git a/Launcher/lib/features/mod_browser/screens/mod_details.dart b/Launcher/lib/features/mod_browser/screens/mod_details.dart index 490b96da..eb887b31 100644 --- a/Launcher/lib/features/mod_browser/screens/mod_details.dart +++ b/Launcher/lib/features/mod_browser/screens/mod_details.dart @@ -16,6 +16,7 @@ import 'package:kyber_launcher/features/nexusmods/services/nexusmods_service.dar import 'package:kyber_launcher/features/nexusmods/widgets/graphql_provider.dart'; import 'package:kyber_launcher/features/settings/dialogs/chromium_download_dialog.dart'; import 'package:kyber_launcher/gen/fonts.gen.dart'; +import 'package:kyber_launcher/gen/l10n/app_localizations.dart'; import 'package:kyber_launcher/injection_container.dart'; import 'package:kyber_launcher/shared/ui/cards/kyber_container.dart'; import 'package:kyber_launcher/shared/ui/dialog/kyber_dialog.dart'; @@ -60,6 +61,10 @@ class _ModInfoState extends State { @override Widget build(BuildContext context) { + final l10n = AppLocalizations.of(context)!; + final isEn = Localizations.localeOf(context).languageCode == 'en'; + final currentFont = isEn ? FontFamily.battlefrontUI : 'BattlefrontGlobal'; + return Query$modByUID$Widget( options: .new( variables: .new(uid: .parse(widget.id)), @@ -69,7 +74,7 @@ class _ModInfoState extends State { if (result.hasException) { late String error; if (result.exception == null) { - error = 'Unknown error'; + error = l10n.unknownError; } else if (result.exception!.linkException != null) { error = result.exception!.linkException!.originalStackTrace .toString(); @@ -88,8 +93,8 @@ class _ModInfoState extends State { final isLoading = result.isLoading; if (!isLoading && data == null) { - return const Center( - child: Text('No data found'), + return Center( + child: Text(l10n.noDataFound), ); } @@ -215,23 +220,23 @@ class _ModInfoState extends State { ), child: Row( children: [ - const Expanded( + Expanded( child: Column( crossAxisAlignment: .start, children: [ Text( - 'MOD INFORMATION', - style: .new( + l10n.modInformation, + style: TextStyle( fontSize: 20, - fontFamily: FontFamily.battlefrontUI, + fontFamily: currentFont, height: 1, ), ), Text( - 'VIEW MOD FILES, SHARE AND ENDORSE', - style: .new( + l10n.viewModFilesShareEndorse, + style: TextStyle( color: kWhiteColor, - fontFamily: FontFamily.battlefrontUI, + fontFamily: currentFont, fontSize: 15, ), ), @@ -272,9 +277,9 @@ class _ModInfoState extends State { children: [ Expanded( child: KyberTabBar( - tabs: const [ - Text('FILES'), - Text('POSTS'), + tabs: [ + Text(l10n.files), + Text(l10n.posts), ], onChanged: (value) { setState(() { @@ -330,18 +335,15 @@ class _ModInfoState extends State { var message = error.message; if (error.extensions?['code'] == 'NOT_DOWNLOADED_MOD') { - message = - 'You must download the mod before endorsing it'; + message = l10n.endorseMustDownload; } else if (error .extensions?['code'] == 'TOO_SOON_AFTER_DOWNLOAD') { - message = - 'You must wait some time after downloading the mod before endorsing it'; + message = l10n.endorseWaitTime; } else if (error .extensions?['code'] == 'UNAUTHORIZED') { - message = - 'You must be logged in to endorse mods'; + message = l10n.endorseLoginRequired; } Logger.root.severe( @@ -367,8 +369,7 @@ class _ModInfoState extends State { ), ); NotificationService.success( - message: - 'Link copied to clipboard', + message: l10n.linkCopied, ); } }, @@ -382,11 +383,11 @@ class _ModInfoState extends State { Row( // FORMAT LIKE THIS: 08/08/2021 12:00:00 children: [ - const Text( - 'Updated: ', + Text( + l10n.updatedAt, style: TextStyle( color: kWhiteColor, - fontFamily: FontFamily.battlefrontUI, + fontFamily: currentFont, fontSize: 15, ), ), @@ -395,9 +396,9 @@ class _ModInfoState extends State { .tryParse(data?.updatedAt ?? '') ?? DateTime.now(), ), - style: const TextStyle( + style: TextStyle( color: kWhiteColor, - fontFamily: FontFamily.battlefrontUI, + fontFamily: currentFont, fontSize: 15, ), ), @@ -445,23 +446,23 @@ class _ModInfoState extends State { children: [ const ContainerSeparator(), _FileList( - title: 'MAIN FILES', + title: l10n.mainFiles, initialExpanded: true, files: mainFiles, ), if (miscFiles.isNotEmpty) _FileList( - title: 'MISCELLANEOUS FILES', + title: l10n.miscellaneousFiles, files: miscFiles, ), if (optionalFiles.isNotEmpty) _FileList( - title: 'OPTIONAL FILES', + title: l10n.optionalFiles, files: optionalFiles, ), if (archivedFiles.isNotEmpty) _FileList( - title: 'ARCHIVED FILES', + title: l10n.archivedFiles, files: archivedFiles, ), ], @@ -509,6 +510,10 @@ class _FileListState extends State<_FileList> { @override Widget build(BuildContext context) { + final l10n = AppLocalizations.of(context)!; + final isEn = Localizations.localeOf(context).languageCode == 'en'; + final currentFont = isEn ? FontFamily.battlefrontUI : 'BattlefrontGlobal'; + return Column( children: [ ColoredBox( @@ -542,9 +547,9 @@ class _FileListState extends State<_FileList> { vertical: 2.5, ), child: Text( - '${widget.title.toUpperCase()} (${widget.files.length})', - style: const .new( - fontFamily: FontFamily.battlefrontUI, + l10n.fileListTitle(widget.title, widget.files.length), + style: TextStyle( + fontFamily: currentFont, fontSize: 15, ), ), @@ -590,7 +595,7 @@ class _FileListState extends State<_FileList> { child: AnimatedDefaultTextStyle( duration: const .new(milliseconds: 150), style: TextStyle( - fontFamily: FontFamily.battlefrontUI, + fontFamily: currentFont, color: hovered ? kActiveColor : kWhiteColor, ), child: Container( @@ -681,14 +686,17 @@ class SidebarItem extends StatelessWidget { @override Widget build(BuildContext context) { + final isEn = Localizations.localeOf(context).languageCode == 'en'; + final currentFont = isEn ? FontFamily.battlefrontUI : 'BattlefrontGlobal'; + return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( title.toUpperCase(), - style: const .new( + style: TextStyle( height: 1, - fontFamily: FontFamily.battlefrontUI, + fontFamily: currentFont, fontSize: 16, color: kInactiveColor, ), @@ -697,12 +705,12 @@ class SidebarItem extends StatelessWidget { subtitleWidget ?? Text( subtitle, - style: const .new( - fontFamily: FontFamily.battlefrontUI, + style: TextStyle( + fontFamily: currentFont, fontSize: 19, ), ), ], ); } -} +} \ No newline at end of file diff --git a/Launcher/lib/features/mod_browser/screens/nexus_profile.dart b/Launcher/lib/features/mod_browser/screens/nexus_profile.dart index ab842f49..23c3d037 100644 --- a/Launcher/lib/features/mod_browser/screens/nexus_profile.dart +++ b/Launcher/lib/features/mod_browser/screens/nexus_profile.dart @@ -9,6 +9,7 @@ import 'package:kyber_launcher/core/utils/transparent_image.dart'; import 'package:kyber_launcher/features/mod_browser/screens/mod_details.dart'; import 'package:kyber_launcher/features/mod_browser/widgets/mod_tile.dart'; import 'package:kyber_launcher/gen/fonts.gen.dart'; +import 'package:kyber_launcher/gen/l10n/app_localizations.dart'; import 'package:kyber_launcher/shared/ui/cards/kyber_container.dart'; import 'package:kyber_launcher/shared/ui/elements/kyber_tab_bar.dart'; import 'package:kyber_launcher/shared/ui/utils/background_blur.dart'; @@ -34,6 +35,10 @@ class _NexusProfileState extends State { @override Widget build(BuildContext context) { + final l10n = AppLocalizations.of(context)!; + final isEn = Localizations.localeOf(context).languageCode == 'en'; + final currentFont = isEn ? FontFamily.battlefrontUI : 'BattlefrontGlobal'; + return Query$userById$Widget( options: .new( variables: .new( @@ -45,7 +50,7 @@ class _NexusProfileState extends State { if (result.hasException) { late String error; if (result.exception == null) { - error = 'Unknown error'; + error = l10n.unknownError; } else if (result.exception!.linkException != null) { error = result.exception!.linkException!.originalStackTrace .toString(); @@ -64,8 +69,8 @@ class _NexusProfileState extends State { final isLoading = result.isLoading; if (!isLoading && data == null) { - return const Center( - child: Text('No data found'), + return Center( + child: Text(l10n.noDataFound), ); } @@ -132,9 +137,8 @@ class _NexusProfileState extends State { children: [ Text( data?.user?.name ?? '...', - style: const TextStyle( - fontFamily: - FontFamily.battlefrontUI, + style: TextStyle( + fontFamily: currentFont, fontSize: 19, color: kWhiteColor, ), @@ -150,12 +154,11 @@ class _NexusProfileState extends State { ), const SizedBox(width: 5), Text( - 'Verified Mod Author' + l10n.verifiedModAuthor .toUpperCase(), - style: const TextStyle( + style: TextStyle( color: kGrayColor, - fontFamily: FontFamily - .battlefrontUI, + fontFamily: currentFont, fontSize: 15, ), ), @@ -181,16 +184,15 @@ class _NexusProfileState extends State { color: hovered ? kActiveColor : kWhiteColor, - fontFamily: FontFamily.battlefrontUI, + fontFamily: currentFont, fontSize: 15, ), child: Row( children: [ Text( - 'VIEWING PROFILE'.toUpperCase(), - style: const TextStyle( - fontFamily: - FontFamily.battlefrontUI, + l10n.viewingProfile.toUpperCase(), + style: TextStyle( + fontFamily: currentFont, fontSize: 15, ), ), @@ -289,12 +291,11 @@ class _NexusProfileState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Text( - 'PUBLISHED MODS', + Text( + l10n.publishedMods, style: TextStyle( fontSize: 24, - fontFamily: - FontFamily.battlefrontUI, + fontFamily: currentFont, height: 1, ), ), @@ -316,10 +317,9 @@ class _NexusProfileState extends State { ?.uniqueModDownloads ?? 0, ), - style: const TextStyle( + style: TextStyle( color: kWhiteColor, - fontFamily: FontFamily - .battlefrontUI, + fontFamily: currentFont, fontSize: 15, ), ), @@ -341,10 +341,9 @@ class _NexusProfileState extends State { ?.modCount ?? 0, ), - style: const TextStyle( + style: TextStyle( color: kWhiteColor, - fontFamily: FontFamily - .battlefrontUI, + fontFamily: currentFont, fontSize: 15, ), ), @@ -504,24 +503,24 @@ class _NexusProfileState extends State { ), child: Row( children: [ - const Expanded( + Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - 'USER INFORMATION', + l10n.userInformation, style: TextStyle( fontSize: 20, - fontFamily: FontFamily.battlefrontUI, + fontFamily: currentFont, height: 1, ), ), Text( - 'VIEW USER STATS', + l10n.viewUserStats, style: TextStyle( color: kWhiteColor, - fontFamily: FontFamily.battlefrontUI, + fontFamily: currentFont, fontSize: 15, ), ), @@ -570,9 +569,9 @@ class _NexusProfileState extends State { children: [ Row( children: [ - const Text( - 'Joined: ', - style: TextStyle(color: kWhiteColor), + Text( + l10n.joinedLabel, + style: const TextStyle(color: kWhiteColor), ), Text( DateFormat.yMd().format( @@ -583,9 +582,9 @@ class _NexusProfileState extends State { ), Row( children: [ - const Text( - 'Mods: ', - style: TextStyle(color: kWhiteColor), + Text( + l10n.modsLabel, + style: const TextStyle(color: kWhiteColor), ), Text( NumberFormat.decimalPattern().format( @@ -596,9 +595,9 @@ class _NexusProfileState extends State { ), Row( children: [ - const Text( - 'Posts: ', - style: TextStyle(color: kWhiteColor), + Text( + l10n.postsLabel, + style: const TextStyle(color: kWhiteColor), ), Text( NumberFormat.decimalPattern().format( @@ -609,9 +608,9 @@ class _NexusProfileState extends State { ), Row( children: [ - const Text( - 'Unique-DLs: ', - style: TextStyle(color: kWhiteColor), + Text( + l10n.uniqueDlsLabel, + style: const TextStyle(color: kWhiteColor), ), Text( NumberFormat.decimalPattern().format( @@ -622,9 +621,9 @@ class _NexusProfileState extends State { ), Row( children: [ - const Text( - 'Views: ', - style: TextStyle(color: kWhiteColor), + Text( + l10n.viewsLabel, + style: const TextStyle(color: kWhiteColor), ), Text( NumberFormat.decimalPattern().format( @@ -635,9 +634,9 @@ class _NexusProfileState extends State { ), Row( children: [ - const Text( - 'Kudos: ', - style: TextStyle(color: kWhiteColor), + Text( + l10n.kudosLabel, + style: const TextStyle(color: kWhiteColor), ), Text( NumberFormat.decimalPattern().format( @@ -648,9 +647,9 @@ class _NexusProfileState extends State { ), Row( children: [ - const Text( - 'Contributed: ', - style: TextStyle(color: kWhiteColor), + Text( + l10n.contributedLabel, + style: const TextStyle(color: kWhiteColor), ), Text( NumberFormat.decimalPattern().format( @@ -672,18 +671,16 @@ class _NexusProfileState extends State { color: hovered ? kActiveColor : Colors.white, - fontFamily: - FontFamily.battlefrontUI, + fontFamily: currentFont, fontSize: 15, ), child: Row( children: [ Text( - 'VIEW ON NEXUS MODS' + l10n.viewOnNexusMods .toUpperCase(), - style: const TextStyle( - fontFamily: - FontFamily.battlefrontUI, + style: TextStyle( + fontFamily: currentFont, fontSize: 15, ), ), @@ -713,4 +710,4 @@ class _NexusProfileState extends State { }, ); } -} +} \ No newline at end of file diff --git a/Launcher/lib/features/mod_browser/widgets/filter_dropdown.dart b/Launcher/lib/features/mod_browser/widgets/filter_dropdown.dart index f3e5aa9d..16c8b36f 100644 --- a/Launcher/lib/features/mod_browser/widgets/filter_dropdown.dart +++ b/Launcher/lib/features/mod_browser/widgets/filter_dropdown.dart @@ -7,6 +7,7 @@ import 'package:kyber_launcher/features/mod_browser/screens/mod_details.dart'; import 'package:kyber_launcher/features/mod_browser/widgets/mod_search_dropdown.dart'; import 'package:kyber_launcher/gen/assets.gen.dart'; import 'package:kyber_launcher/gen/fonts.gen.dart'; +import 'package:kyber_launcher/gen/l10n/app_localizations.dart'; import 'package:kyber_launcher/shared/ui/cards/kyber_container.dart'; import 'package:kyber_launcher/shared/ui/elements/kyber_dropdown.dart'; import 'package:kyber_launcher/shared/ui/utils/background_blur.dart'; @@ -209,6 +210,10 @@ class _FilterDropdownState extends State<_FilterDropdown> with WindowListener { @override Widget build(BuildContext context) { + final l10n = AppLocalizations.of(context)!; + final isEn = Localizations.localeOf(context).languageCode == 'en'; + final currentFont = isEn ? FontFamily.battlefrontUI : 'BattlefrontGlobal'; + final size = widget.renderBox.size; final position = widget.renderBox.localToGlobal(Offset.zero); return Stack( @@ -257,7 +262,7 @@ class _FilterDropdownState extends State<_FilterDropdown> with WindowListener { return SuperListView( children: [ KyberSectionDropdown( - title: 'SORT BY', + title: l10n.sortByTitle, initialExpanded: true, child: Column( crossAxisAlignment: @@ -293,7 +298,7 @@ class _FilterDropdownState extends State<_FilterDropdown> with WindowListener { ), ), KyberSectionDropdown( - title: 'TIME FILTER', + title: l10n.timeFilterTitle, initialExpanded: true, child: Column( crossAxisAlignment: @@ -313,17 +318,17 @@ class _FilterDropdownState extends State<_FilterDropdown> with WindowListener { ], items: [1, 7, 14, 30, 365, 0].reversed .map((e) { - var text = 'All time'; + var text = l10n.allTime; if (e == 365) { - text = '1 year'; + text = l10n.oneYear; } else if (e == 30) { - text = '1 month'; + text = l10n.oneMonth; } else if (e == 14) { - text = '2 weeks'; + text = l10n.twoWeeks; } else if (e == 7) { - text = '1 week'; + text = l10n.oneWeek; } else if (e == 1) { - text = '1 day'; + text = l10n.oneDay; } return _SelectorItem( @@ -347,7 +352,7 @@ class _FilterDropdownState extends State<_FilterDropdown> with WindowListener { ), // items per page 20, 30, 40 KyberSectionDropdown( - title: 'ITEMS PER PAGE', + title: l10n.itemsPerPageTitle, initialExpanded: true, child: Column( crossAxisAlignment: @@ -427,6 +432,10 @@ class _FilterSelector extends StatelessWidget { @override Widget build(BuildContext context) { + final l10n = AppLocalizations.of(context)!; + final isEn = Localizations.localeOf(context).languageCode == 'en'; + final currentFont = isEn ? FontFamily.battlefrontUI : 'BattlefrontGlobal'; + return GridView.builder( shrinkWrap: true, gridDelegate: mt.SliverGridDelegateWithMaxCrossAxisExtent( @@ -483,10 +492,10 @@ class _FilterSelector extends StatelessWidget { ? Colors.black : Colors.white, fontSize: 14, - fontFamily: FontFamily.battlefrontUI, + fontFamily: currentFont, ), child: Text( - 'ALL', + l10n.all, style: TextStyle( color: hovered || @@ -501,7 +510,7 @@ class _FilterSelector extends StatelessWidget { ? Colors.black : Colors.white, fontSize: 14, - fontFamily: FontFamily.battlefrontUI, + fontFamily: currentFont, ), textAlign: TextAlign.center, ), @@ -554,7 +563,7 @@ class _FilterSelector extends StatelessWidget { ? Colors.black : Colors.white, fontSize: 14, - fontFamily: FontFamily.battlefrontUI, + fontFamily: currentFont, ), child: Text( mode.title.toUpperCase(), @@ -570,4 +579,4 @@ class _FilterSelector extends StatelessWidget { itemCount: includeAll && !removeAll ? items.length + 1 : items.length, ); } -} +} \ No newline at end of file diff --git a/Launcher/lib/features/mod_collections/screens/collection_import.dart b/Launcher/lib/features/mod_collections/screens/collection_import.dart index 9dde0969..a450e3ff 100644 --- a/Launcher/lib/features/mod_collections/screens/collection_import.dart +++ b/Launcher/lib/features/mod_collections/screens/collection_import.dart @@ -8,6 +8,7 @@ import 'package:kyber_launcher/core/routing/app_router.dart'; import 'package:kyber_launcher/features/mods/helper/mod_helper.dart'; import 'package:kyber_launcher/features/settings/dialogs/chromium_download_dialog.dart'; import 'package:kyber_launcher/gen/fonts.gen.dart'; +import 'package:kyber_launcher/gen/l10n/app_localizations.dart'; import 'package:kyber_launcher/shared/ui/buttons/button.dart'; import 'package:kyber_launcher/shared/ui/elements/kyber_event_container.dart'; import 'package:kyber_launcher/shared/ui/elements/list/kyber_list.dart'; @@ -38,6 +39,10 @@ class _CollectionImportState extends State { @override Widget build(BuildContext context) { + final l10n = AppLocalizations.of(context)!; + final isEn = Localizations.localeOf(context).languageCode == 'en'; + final currentFont = isEn ? FontFamily.battlefrontUI : 'BattlefrontGlobal'; + if (_metaData == null) { return const Center(child: ProgressRing()); } @@ -63,10 +68,10 @@ class _CollectionImportState extends State { mainAxisAlignment: MainAxisAlignment.center, children: [ Text( - 'Collection Mods'.toUpperCase(), - style: const TextStyle( + l10n.collectionMods.toUpperCase(), + style: TextStyle( fontSize: 15, - fontFamily: FontFamily.battlefrontUI, + fontFamily: currentFont, fontWeight: FontWeight.bold, ), ), @@ -104,12 +109,12 @@ class _CollectionImportState extends State { const SizedBox(width: 10), Expanded( child: AutoSizeText( - '${mod.name} (${mod.version})', - style: const TextStyle( + l10n.modNameVersion(mod.name, mod.version), + style: TextStyle( fontSize: 18, height: 1, color: kWhiteColor, - fontFamily: FontFamily.battlefrontUI, + fontFamily: currentFont, ), maxLines: 1, ), @@ -117,10 +122,10 @@ class _CollectionImportState extends State { const SizedBox(width: 10), Text( formatBytes(200, 2), - style: const TextStyle( + style: TextStyle( fontSize: 14, color: kWhiteColor1, - fontFamily: FontFamily.battlefrontUI, + fontFamily: currentFont, ), ), ], @@ -142,11 +147,11 @@ class _CollectionImportState extends State { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ KyberButton( - text: 'CANCEL', + text: l10n.cancel, onPressed: router.pop, ), KyberButton( - text: 'DOWNLOAD COLLECTION MODS', + text: l10n.downloadCollectionMods, onPressed: () {}, ), ], @@ -154,4 +159,4 @@ class _CollectionImportState extends State { ], ); } -} +} \ No newline at end of file diff --git a/Launcher/lib/features/mods/dialogs/move_directory_dialog.dart b/Launcher/lib/features/mods/dialogs/move_directory_dialog.dart index fc43f0bd..8ff200ea 100644 --- a/Launcher/lib/features/mods/dialogs/move_directory_dialog.dart +++ b/Launcher/lib/features/mods/dialogs/move_directory_dialog.dart @@ -7,6 +7,7 @@ import 'package:kyber_launcher/core/config/colors.dart'; import 'package:kyber_launcher/core/services/notification_service.dart'; import 'package:kyber_launcher/features/mods/services/mod_service.dart'; import 'package:kyber_launcher/gen/fonts.gen.dart'; +import 'package:kyber_launcher/gen/l10n/app_localizations.dart'; import 'package:kyber_launcher/injection_container.dart'; import 'package:kyber_launcher/shared/ui/buttons/button.dart'; import 'package:kyber_launcher/shared/ui/dialog/kyber_dialog.dart'; @@ -51,9 +52,13 @@ class _MoveModsDirectoryDialogState extends State { @override Widget build(BuildContext context) { + final l10n = AppLocalizations.of(context)!; + final isEn = Localizations.localeOf(context).languageCode == 'en'; + final currentFont = isEn ? FontFamily.battlefrontUI : 'BattlefrontGlobal'; + return KyberContentDialog( constraints: const BoxConstraints(maxWidth: 700, maxHeight: 500), - title: Text('Move Mods Directory'.toUpperCase()), + title: Text(l10n.moveModsDirectory), content: Column( children: [ if (widget.isInvalid) ...[ @@ -66,12 +71,12 @@ class _MoveModsDirectoryDialogState extends State { ), border: Border.all(color: kActiveColor, width: 2), ), - child: const Padding( - padding: EdgeInsets.symmetric(horizontal: 7, vertical: 5), + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 7, vertical: 5), child: Text( - 'The current mods directory is invalid because it contains non-ASCII characters. ', + l10n.invalidDirectoryWarning, style: TextStyle( - fontFamily: FontFamily.battlefrontUI, + fontFamily: currentFont, fontSize: 15, ), ), @@ -82,7 +87,7 @@ class _MoveModsDirectoryDialogState extends State { ], Center( child: Text( - 'Please select the new directory where you want to move the mods directory. Mods will not be moved. Only the directory will be changed.', + l10n.selectNewDirectoryDesc, style: FluentTheme.of( context, ).typography.body?.copyWith(color: kWhiteColor), @@ -92,7 +97,7 @@ class _MoveModsDirectoryDialogState extends State { const SizedBox(height: 15), Center( child: Text( - 'Non-ASCII characters in the path are not allowed (e.g. Korean, Greek, etc.).', + l10n.nonAsciiWarning, style: FluentTheme.of( context, ).typography.body?.copyWith(color: kWhiteColor), @@ -122,8 +127,7 @@ class _MoveModsDirectoryDialogState extends State { final requiresAdmin = await requiresAdminPermissions(path); if (requiresAdmin) { NotificationService.error( - message: - "You can't select a directory that requires admin permissions. Please select another directory.", + message: l10n.adminPermissionError, ); return; } @@ -133,15 +137,14 @@ class _MoveModsDirectoryDialogState extends State { ); if (containsNonAscii) { NotificationService.error( - message: - 'The selected directory contains non-ASCII characters. Please select a different directory.', + message: l10n.selectedDirNonAsciiError, ); return; } setState(() => controller.text = path!); }, - text: 'Browse', + text: l10n.browse, ), ], ), @@ -152,10 +155,10 @@ class _MoveModsDirectoryDialogState extends State { onPressed: () { Navigator.of(context).pop(); }, - text: 'Cancel', + text: l10n.cancel, ), KyberButton( - text: 'Reset to default', + text: l10n.resetToDefault, onPressed: () async { if (!FileHelper.getModsDirectory().existsSync()) { await FileHelper.getModsDirectory().create(recursive: true); @@ -164,8 +167,7 @@ class _MoveModsDirectoryDialogState extends State { final path = FileHelper.getModsDirectory().path; if (path.codeUnits.any((element) => element > 127)) { NotificationService.error( - message: - 'The default mods directory contains non-ASCII characters. Please select a different directory.', + message: l10n.defaultDirNonAsciiError, ); return; } @@ -182,8 +184,7 @@ class _MoveModsDirectoryDialogState extends State { onPressed: () async { if (controller.text.codeUnits.any((element) => element > 127)) { NotificationService.error( - message: - 'The default mods directory contains non-ASCII characters. Please select a different directory.', + message: l10n.defaultDirNonAsciiError, ); return; } @@ -193,9 +194,9 @@ class _MoveModsDirectoryDialogState extends State { sl.registerSingletonAsync(ModService.getInstance); Navigator.of(context).pop(); }, - text: 'Change', + text: l10n.change, ), ], ); } -} +} \ No newline at end of file diff --git a/Launcher/lib/features/mods/screens/mods.dart b/Launcher/lib/features/mods/screens/mods.dart index 4ff7fad3..bccd5f5c 100644 --- a/Launcher/lib/features/mods/screens/mods.dart +++ b/Launcher/lib/features/mods/screens/mods.dart @@ -25,6 +25,7 @@ import 'package:kyber_launcher/features/mods/widgets/mod_list/mod_list_header.da import 'package:kyber_launcher/features/nexusmods/services/nexusmods_service.dart'; import 'package:kyber_launcher/gen/assets.gen.dart'; import 'package:kyber_launcher/gen/fonts.gen.dart'; +import 'package:kyber_launcher/gen/l10n/app_localizations.dart'; import 'package:kyber_launcher/injection_container.dart'; import 'package:kyber_launcher/main.dart'; import 'package:kyber_launcher/shared/ui/elements/filter_dropdown.dart'; @@ -299,19 +300,23 @@ class _LoadingIndicator extends StatelessWidget { @override Widget build(BuildContext context) { - return const Center( + final l10n = AppLocalizations.of(context)!; + final isEn = Localizations.localeOf(context).languageCode == 'en'; + final currentFont = isEn ? FontFamily.battlefrontUI : 'BattlefrontGlobal'; + + return Center( child: Row( mainAxisAlignment: MainAxisAlignment.center, spacing: 15, children: [ - SizedBox( + const SizedBox( height: 20, width: 20, child: ProgressRing(), ), Text( - 'LOADING MODS...', - style: TextStyle(fontFamily: FontFamily.battlefrontUI), + l10n.loadingMods, + style: TextStyle(fontFamily: currentFont), ), ], ), @@ -398,24 +403,28 @@ class _CollectionsHeader extends StatelessWidget { @override Widget build(BuildContext context) { + final l10n = AppLocalizations.of(context)!; + final isEn = Localizations.localeOf(context).languageCode == 'en'; + final currentFont = isEn ? FontFamily.battlefrontUI : 'BattlefrontGlobal'; + return Container( height: 61, padding: const EdgeInsets.all(13), - child: const Column( + child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - 'COLLECTIONS', + l10n.collections, style: TextStyle( - fontFamily: FontFamily.battlefrontUI, + fontFamily: currentFont, fontSize: 21, height: 1, ), ), Text( - 'MANAGE MOD COLLECTIONS & CURATE COSMETIC MODS', + l10n.manageCollectionsSub, style: TextStyle( - fontFamily: FontFamily.battlefrontUI, + fontFamily: currentFont, fontSize: 14, color: kWhiteColor, height: 0.9, @@ -529,6 +538,8 @@ class _TabSelector extends StatelessWidget { @override Widget build(BuildContext context) { + final l10n = AppLocalizations.of(context)!; + return SizedBox( width: 220, child: KyberTabBar( @@ -540,8 +551,8 @@ class _TabSelector extends StatelessWidget { onPageChanged(value); }, tabs: [ - Text('Mods'.toUpperCase()), - Text('Browser'.toUpperCase()), + Text(l10n.mods.toUpperCase()), + Text(l10n.browser.toUpperCase()), ], ), ); @@ -673,6 +684,8 @@ class _BrowserPagination extends StatelessWidget { @override Widget build(BuildContext context) { + final l10n = AppLocalizations.of(context)!; + return SizedBox( width: 150, child: BlocBuilder( @@ -684,7 +697,7 @@ class _BrowserPagination extends StatelessWidget { onChanged: (value) => _handlePageChange(context, value), tabs: [ const Icon(mt.Icons.arrow_back_ios_new_rounded), - Text('$page/$totalPages'.toUpperCase()), + Text(l10n.paginationInfo(page, totalPages).toUpperCase()), const Icon(mt.Icons.arrow_forward_ios_rounded), ], ); @@ -731,13 +744,15 @@ class _ModsFilterDropdown extends StatelessWidget { @override Widget build(BuildContext context) { final cubit = context.read(); + final l10n = AppLocalizations.of(context)!; + return KyberSearchFilterDropdown( dropdownContent: BlocBuilder( builder: (context, state) { return SuperListView( children: [ KyberFilterSection( - title: 'MOD SCOPE', + title: l10n.modScope, selectedItems: [state.filter.scope], items: toSelectorItems( ModScope.values, @@ -756,4 +771,4 @@ class _ModsFilterDropdown extends StatelessWidget { onSearchChanged: onSearchChanged, ); } -} +} \ No newline at end of file diff --git a/Launcher/lib/features/mods/widgets/collection_box/collection_box.dart b/Launcher/lib/features/mods/widgets/collection_box/collection_box.dart index fd2638dd..7dbdcb82 100644 --- a/Launcher/lib/features/mods/widgets/collection_box/collection_box.dart +++ b/Launcher/lib/features/mods/widgets/collection_box/collection_box.dart @@ -19,6 +19,7 @@ import 'package:kyber_launcher/features/mods/widgets/collection_list/collection_ import 'package:kyber_launcher/features/plugin_manager/services/plugin_manager.dart'; import 'package:kyber_launcher/gen/assets.gen.dart'; import 'package:kyber_launcher/gen/fonts.gen.dart'; +import 'package:kyber_launcher/gen/l10n/app_localizations.dart'; import 'package:kyber_launcher/injection_container.dart'; import 'package:kyber_launcher/shared/ui/buttons/button.dart'; import 'package:kyber_launcher/shared/ui/buttons/custom_icon_button.dart'; @@ -49,6 +50,7 @@ class _CollectionBoxState extends State { } void pickIcon() async { + final l10n = AppLocalizations.of(context)!; final currentCollection = context .read() .state @@ -60,7 +62,7 @@ class _CollectionBoxState extends State { final result = await FilePicker.platform.pickFiles( allowedExtensions: ['png', 'jpg', 'jpeg', 'webp', 'gif'], - dialogTitle: 'Select Collection Icon', + dialogTitle: l10n.selectCollectionIcon, type: FileType.custom, ); @@ -89,6 +91,10 @@ class _CollectionBoxState extends State { @override Widget build(BuildContext context) { + final l10n = AppLocalizations.of(context)!; + final isEn = Localizations.localeOf(context).languageCode == 'en'; + final currentFont = isEn ? FontFamily.battlefrontUI : 'BattlefrontGlobal'; + final collection = context .watch() .state @@ -108,22 +114,22 @@ class _CollectionBoxState extends State { padding: const EdgeInsets.all(13), child: Row( children: [ - const Expanded( + Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - 'COLLECTION', + l10n.collectionLabel, style: TextStyle( - fontFamily: FontFamily.battlefrontUI, + fontFamily: currentFont, fontSize: 21, height: 1, ), ), Text( - 'A COLLECTION IS A READY TO PLAY MOD LIST', + l10n.collectionDescription, style: TextStyle( - fontFamily: FontFamily.battlefrontUI, + fontFamily: currentFont, fontSize: 14, color: kWhiteColor, height: 0.9, @@ -153,7 +159,7 @@ class _CollectionBoxState extends State { height: 45, width: 45, child: ClipRRect( - borderRadius: .circular( + borderRadius: BorderRadius.circular( kDefaultInnerBorderRadius, ), child: Stack( @@ -191,16 +197,16 @@ class _CollectionBoxState extends State { if (!state.editing) ...[ Text( collection.title, - style: const TextStyle( - fontFamily: FontFamily.battlefrontUI, + style: TextStyle( + fontFamily: currentFont, fontSize: 19, height: 1, ), ), Text( - '${collection.mods.length} Mods', - style: const TextStyle( - fontFamily: FontFamily.battlefrontUI, + l10n.modsCount(collection.mods.length), + style: TextStyle( + fontFamily: currentFont, fontSize: 15, color: kWhiteColor, ), @@ -211,7 +217,7 @@ class _CollectionBoxState extends State { width: 250, child: KyberInput( initialValue: collection.title, - placeholder: 'Collection Name', + placeholder: l10n.collectionNamePlaceholder, onFieldSubmitted: (_) => context .read() .saveCollection(), @@ -230,7 +236,7 @@ class _CollectionBoxState extends State { children: [ if (!state.editing) KyberButton( - text: 'PLAY', + text: l10n.playButton, icon: const Icon(mt.Icons.play_arrow_rounded), onPressed: () => MaximaHelper.requestGameLaunch( context, @@ -239,7 +245,7 @@ class _CollectionBoxState extends State { ), if (state.editing) KyberButton( - text: 'SAVE', + text: l10n.saveButton, icon: const Icon(mt.Icons.save), onPressed: () => context .read() @@ -262,7 +268,7 @@ class _CollectionBoxState extends State { items: [ if (sl.get().bsmPlugin != null) DropdownOption( - label: 'BETTER SABERS (PLUGIN)', + label: l10n.betterSabersPlugin, icon: Assets.icons.laserSword.svg( width: 15, height: 15, @@ -293,7 +299,7 @@ class _CollectionBoxState extends State { } NotificationService.showNotification( - message: 'Reloading mods', + message: l10n.reloadingMods, severity: InfoBarSeverity.info, ); @@ -324,13 +330,13 @@ class _CollectionBoxState extends State { .saveCollection(); NotificationService.showNotification( - message: 'Generated BetterSabers', + message: l10n.generatedBetterSabers, severity: InfoBarSeverity.success, ); }, ), DropdownOption( - label: 'EXPORT COLLECTION TAR', + label: l10n.exportCollectionTar, icon: const Icon( mt.Icons.account_balance_wallet, ), @@ -345,7 +351,7 @@ class _CollectionBoxState extends State { }, ), DropdownOption( - label: 'EDIT COLLECTION', + label: l10n.editCollection, icon: const Icon(mt.Icons.edit), onClick: () async { context @@ -354,8 +360,8 @@ class _CollectionBoxState extends State { }, ), DropdownOption( - label: 'EXPORT COLLECTION', - icon: Icon(mt.Icons.upload), + label: l10n.exportCollection, + icon: const Icon(mt.Icons.upload), onClick: () async { await showKyberDialog( context: context, @@ -367,8 +373,8 @@ class _CollectionBoxState extends State { }, ), DropdownOption( - label: 'CREATE A COPY', - icon: Icon(FluentIcons.copy), + label: l10n.createACopy, + icon: const Icon(FluentIcons.copy), onClick: () { final currentCollection = context .read() @@ -377,8 +383,7 @@ class _CollectionBoxState extends State { final newCollection = currentCollection .copyWith( localId: const Uuid().v4(), - title: - '${currentCollection.title} (Copy)', + title: l10n.collectionCopyName(currentCollection.title), ); context.read() @@ -387,7 +392,7 @@ class _CollectionBoxState extends State { }, ), DropdownOption( - label: 'DELETE COLLECTION', + label: l10n.deleteCollection, icon: Icon( FluentIcons.delete, color: Colors.red, @@ -409,7 +414,7 @@ class _CollectionBoxState extends State { }, ), ], - placeholder: 'OPTIONS', + placeholder: l10n.options, ), ), ], @@ -517,8 +522,7 @@ class _CollectionBoxState extends State { color: Colors.black .withOpacity(.5), child: KyberTooltip( - message: - "Using multiple large or complex mods isn't recommended, as they often conflict with each other.", + message: l10n.largeModsWarning, child: Icon( FluentIcons.warning, color: Colors.yellow, @@ -572,9 +576,8 @@ class _CollectionBoxState extends State { Text( mod?.details.name ?? collectionMod.name, - style: const TextStyle( - fontFamily: FontFamily - .battlefrontUI, + style: TextStyle( + fontFamily: currentFont, fontSize: 17, height: 1, ), @@ -584,9 +587,8 @@ class _CollectionBoxState extends State { Text( mod?.details.version ?? collectionMod.version, - style: const TextStyle( - fontFamily: FontFamily - .battlefrontUI, + style: TextStyle( + fontFamily: currentFont, fontSize: 14, color: kButtonBorder, height: 1, @@ -622,4 +624,4 @@ class _CollectionBoxState extends State { ), ); } -} +} \ No newline at end of file diff --git a/Launcher/lib/features/mods/widgets/collection_list/collection_entry.dart b/Launcher/lib/features/mods/widgets/collection_list/collection_entry.dart index a697c3e0..9eda14fc 100644 --- a/Launcher/lib/features/mods/widgets/collection_list/collection_entry.dart +++ b/Launcher/lib/features/mods/widgets/collection_list/collection_entry.dart @@ -5,6 +5,7 @@ import 'package:kyber_launcher/features/mod_collections/providers/mod_collection import 'package:kyber_launcher/features/mods/widgets/collection_list/collection_icon.dart'; import 'package:kyber_launcher/gen/assets.gen.dart'; import 'package:kyber_launcher/gen/fonts.gen.dart'; +import 'package:kyber_launcher/gen/l10n/app_localizations.dart'; import 'package:kyber_launcher/shared/ui/utils/button_builder.dart'; class CollectionEntry extends StatefulWidget { @@ -37,6 +38,9 @@ class _CollectionEntryState extends State { @override Widget build(BuildContext context) { + final isEn = Localizations.localeOf(context).languageCode == 'en'; + final currentFont = isEn ? FontFamily.battlefrontUI : 'BattlefrontGlobal'; + return GestureDetector( onTap: widget.onTap, child: MouseRegion( @@ -93,8 +97,8 @@ class _CollectionEntryState extends State { children: [ Text( widget.modCollection.title, - style: const TextStyle( - fontFamily: FontFamily.battlefrontUI, + style: TextStyle( + fontFamily: currentFont, fontSize: 18, color: Colors.white, fontWeight: FontWeight.bold, @@ -122,6 +126,10 @@ class CreateCollectionEntry extends StatelessWidget { @override Widget build(BuildContext context) { + final l10n = AppLocalizations.of(context)!; + final isEn = Localizations.localeOf(context).languageCode == 'en'; + final currentFont = isEn ? FontFamily.battlefrontUI : 'BattlefrontGlobal'; + return ButtonBuilder( onClick: onTap, builder: (context, hovered) => AnimatedContainer( @@ -142,16 +150,16 @@ class CreateCollectionEntry extends StatelessWidget { Assets.icons.kblCollection.svg( height: 70, ), - const Padding( - padding: EdgeInsets.all(16), + Padding( + padding: const EdgeInsets.all(16), child: Column( mainAxisAlignment: MainAxisAlignment.end, crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - 'CREATE NEW COLLECTION', + l10n.createNewCollection, style: TextStyle( - fontFamily: FontFamily.battlefrontUI, + fontFamily: currentFont, fontSize: 18, color: Colors.white, fontWeight: FontWeight.bold, @@ -170,4 +178,4 @@ class CreateCollectionEntry extends StatelessWidget { ), ); } -} +} \ No newline at end of file diff --git a/Launcher/lib/features/navigation_bar/screens/navigation_bar.dart b/Launcher/lib/features/navigation_bar/screens/navigation_bar.dart index e77fb403..8d5044ad 100644 --- a/Launcher/lib/features/navigation_bar/screens/navigation_bar.dart +++ b/Launcher/lib/features/navigation_bar/screens/navigation_bar.dart @@ -25,6 +25,8 @@ import 'package:kyber_launcher/features/navigation_bar/widgets/keyboard_shortcut import 'package:kyber_launcher/features/navigation_bar/widgets/navigation_content.dart'; import 'package:kyber_launcher/features/server_browser/providers/ingame_view_cubit.dart'; import 'package:kyber_launcher/features/setup/screens/walk_through_setup.dart'; +import 'package:kyber_launcher/gen/fonts.gen.dart'; +import 'package:kyber_launcher/gen/l10n/app_localizations.dart'; import 'package:kyber_launcher/injection_container.dart'; import 'package:kyber_launcher/shared/ui/dialog/kyber_dialog.dart'; import 'package:protocol_handler/protocol_handler.dart'; @@ -101,6 +103,10 @@ class _NavigationBarState extends State @override Widget build(BuildContext context) { + final l10n = AppLocalizations.of(context)!; + final isEn = Localizations.localeOf(context).languageCode == 'en'; + final currentFont = isEn ? FontFamily.battlefrontUI : 'BattlefrontGlobal'; + return KeyboardShortcutsWrapper( child: BlocConsumer( listenWhen: (prev, state) => prev.initialized != state.initialized, @@ -216,6 +222,7 @@ class _NavigationBarState extends State KyberStatusState state, ) async { final viewCubit = context.read(); + final l10n = AppLocalizations.of(context)!; if (state is KyberStatusInitial && viewCubit.state.server != null) { viewCubit.unloadServer(); @@ -240,7 +247,7 @@ class _NavigationBarState extends State if (state.server == null) { NotificationService.error( - message: 'The server you were playing on could not be found.', + message: l10n.serverNotFound, ); return; } @@ -253,4 +260,4 @@ class _NavigationBarState extends State router.push('/ingame'); } } -} +} \ No newline at end of file diff --git a/Launcher/lib/features/nexusmods/dialogs/nexusmods_login.dart b/Launcher/lib/features/nexusmods/dialogs/nexusmods_login.dart index b40de2ed..47db3818 100644 --- a/Launcher/lib/features/nexusmods/dialogs/nexusmods_login.dart +++ b/Launcher/lib/features/nexusmods/dialogs/nexusmods_login.dart @@ -6,12 +6,16 @@ import 'package:kyber_launcher/core/services/app_settings.dart'; import 'package:kyber_launcher/core/services/notification_service.dart'; import 'package:kyber_launcher/core/services/windows_utils.dart'; import 'package:kyber_launcher/features/setup/widgets/nexus_login_screen.dart'; +import 'package:kyber_launcher/gen/fonts.gen.dart'; +import 'package:kyber_launcher/gen/l10n/app_localizations.dart'; import 'package:kyber_launcher/main.dart'; import 'package:kyber_launcher/shared/ui/buttons/button.dart'; import 'package:kyber_launcher/shared/ui/dialog/kyber_dialog.dart'; import 'package:logging/logging.dart'; Future showNexusLoginDialog(BuildContext context) async { + final l10n = AppLocalizations.of(context)!; + final result = await showDialog( context: context, builder: (context) => const NexusmodsLogin(), @@ -19,7 +23,7 @@ Future showNexusLoginDialog(BuildContext context) async { if (result == null || !result) { if (result == null) { - NotificationService.error(message: 'Aborting NexusMods login'); + NotificationService.error(message: l10n.abortingNexusLogin); } return false; @@ -53,7 +57,7 @@ class _NexusmodsLoginState extends State { } NotificationService.showNotification( - message: 'Please install WebView to use this feature.', + message: AppLocalizations.of(context)!.installWebViewError, severity: InfoBarSeverity.error, ); return; @@ -90,8 +94,12 @@ class _NexusmodsLoginState extends State { @override Widget build(BuildContext context) { + final l10n = AppLocalizations.of(context)!; + final isEn = Localizations.localeOf(context).languageCode == 'en'; + final currentFont = isEn ? FontFamily.battlefrontUI : 'BattlefrontGlobal'; + return KyberContentDialog( - title: Text('NexusMods Login'.toUpperCase()), + title: Text(l10n.nexusModsAuthorization.toUpperCase()), constraints: BoxConstraints( maxWidth: browserOpen ? 1000 : 600, maxHeight: browserOpen ? 857 : 400, @@ -101,7 +109,7 @@ class _NexusmodsLoginState extends State { ), actions: [ KyberButton( - text: 'Skip', + text: l10n.skip, onPressed: () { Navigator.of(context).pop(false); }, @@ -113,23 +121,28 @@ class _NexusmodsLoginState extends State { : () async { setState(() => browserOpen = true); }, - text: !browserOpen ? 'CONTINUE' : 'WAIT', + text: !browserOpen ? l10n.continueText : l10n.wait, ), ], content: Builder( builder: (context) { if (_currentStep == 3) { - return const Center( + return Center( child: Column( children: [ - Text('Windows 7 Compatibility Mode detected'), - SizedBox(height: 15), Text( - 'To login to NexusMods, please disable Windows 7 Compatibility Mode for Kyber Launcher.', + l10n.win7CompatibilityDetected, + style: TextStyle(fontFamily: currentFont), + ), + const SizedBox(height: 15), + Text( + l10n.disableWin7CompMode, + style: TextStyle(fontFamily: currentFont), ), - SizedBox(height: 15), + const SizedBox(height: 15), Text( - 'After you have logged in, you need to re-enable Windows 7 Compatibility Mode.', + l10n.reEnableWin7CompMode, + style: TextStyle(fontFamily: currentFont), ), ], ), @@ -137,12 +150,15 @@ class _NexusmodsLoginState extends State { } if (_currentStep == 0) { - return const Center( + return Center( child: Row( children: [ - ProgressRing(), - SizedBox(width: 15), - Text('Checking WebView installation...'), + const ProgressRing(), + const SizedBox(width: 15), + Text( + l10n.checkingWebView, + style: TextStyle(fontFamily: currentFont), + ), ], ), ); @@ -200,17 +216,21 @@ class _NexusmodsLoginState extends State { ), ] : [ - const Text( - 'To continue, you will need to log in with your NexusMods account in the browser that is about to open.', - style: .new( + Text( + l10n.nexusLoginIntro, + style: TextStyle( fontSize: 16, - fontWeight: .bold, + fontWeight: FontWeight.bold, + fontFamily: currentFont, ), ), const SizedBox(height: 16), - const Text( - 'The data is processed locally and will only be sent to Nexusmods.\nYou can also enable/disable this feature later in the settings menu..', - style: .new(fontSize: 14), + Text( + l10n.nexusLoginDataNotice, + style: TextStyle( + fontSize: 14, + fontFamily: currentFont, + ), ), ], ), @@ -219,4 +239,4 @@ class _NexusmodsLoginState extends State { ), ); } -} +} \ No newline at end of file diff --git a/Launcher/lib/features/nexusmods/screens/nexus_auth_screen.dart b/Launcher/lib/features/nexusmods/screens/nexus_auth_screen.dart index 4966ec69..e334983f 100644 --- a/Launcher/lib/features/nexusmods/screens/nexus_auth_screen.dart +++ b/Launcher/lib/features/nexusmods/screens/nexus_auth_screen.dart @@ -2,6 +2,7 @@ import 'package:fluent_ui/fluent_ui.dart'; import 'package:kyber_launcher/core/config/colors.dart'; import 'package:kyber_launcher/features/nexusmods/services/nexusmods_service.dart'; import 'package:kyber_launcher/gen/fonts.gen.dart'; +import 'package:kyber_launcher/gen/l10n/app_localizations.dart'; import 'package:kyber_launcher/injection_container.dart'; import 'package:kyber_launcher/shared/ui/buttons/button.dart'; import 'package:kyber_launcher/shared/ui/elements/kyber_event_container.dart'; @@ -14,6 +15,10 @@ class NexusAuthScreen extends StatelessWidget { @override Widget build(BuildContext context) { + final l10n = AppLocalizations.of(context)!; + final isEn = Localizations.localeOf(context).languageCode == 'en'; + final currentFont = isEn ? FontFamily.battlefrontUI : 'BattlefrontGlobal'; + return Column( children: [ KyberEventContainer( @@ -21,26 +26,26 @@ class NexusAuthScreen extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - 'NexusMods Authorization'.toUpperCase(), - style: const TextStyle( + l10n.nexusModsAuthorization.toUpperCase(), + style: TextStyle( fontSize: 22, fontWeight: FontWeight.bold, - fontFamily: FontFamily.battlefrontUI, + fontFamily: currentFont, ), ), - const Text( - 'You need to authorize KyberLauncher on NexusMods to use the mod browser.', + Text( + l10n.nexusAuthDescription, style: TextStyle( fontSize: 18, color: kWhiteColor, - fontFamily: FontFamily.battlefrontUI, + fontFamily: currentFont, ), ), const SizedBox(height: 20), Row( children: [ KyberButton( - text: 'Authorize Kyber', + text: l10n.authorizeKyber, onPressed: () { sl() .requestApiToken(onUrl: launchUrlString) @@ -62,4 +67,4 @@ class NexusAuthScreen extends StatelessWidget { ], ); } -} +} \ No newline at end of file diff --git a/Launcher/lib/features/reports/screens/report_view.dart b/Launcher/lib/features/reports/screens/report_view.dart index 19baf543..e30fba7b 100644 --- a/Launcher/lib/features/reports/screens/report_view.dart +++ b/Launcher/lib/features/reports/screens/report_view.dart @@ -15,6 +15,7 @@ import 'package:kyber_launcher/features/mod_browser/widgets/mod_details/mod_imag import 'package:kyber_launcher/features/reports/dialogs/report_punishment_dialog.dart'; import 'package:kyber_launcher/gen/assets.gen.dart'; import 'package:kyber_launcher/gen/fonts.gen.dart'; +import 'package:kyber_launcher/gen/l10n/app_localizations.dart'; import 'package:kyber_launcher/injection_container.dart'; import 'package:kyber_launcher/shared/ui/buttons/button.dart'; import 'package:kyber_launcher/shared/ui/buttons/custom_icon_button.dart'; @@ -87,6 +88,10 @@ class _ReportViewState extends State { @override Widget build(BuildContext context) { + final l10n = AppLocalizations.of(context)!; + final isEn = Localizations.localeOf(context).languageCode == 'en'; + final currentFont = isEn ? FontFamily.battlefrontUI : 'BattlefrontGlobal'; + if (playerName == null) { return const Center( child: ProgressRing(), @@ -95,8 +100,8 @@ class _ReportViewState extends State { final report = reports.isNotEmpty ? reports[selectedIndex ?? 0] : null; if (report == null) { - return const Center( - child: Text('No reports available'), + return Center( + child: Text(l10n.noReportsAvailable), ); } @@ -183,61 +188,56 @@ class _ReportViewState extends State { children: [ AutoSizeText( report.reportedPlayerName, - style: const TextStyle( + style: TextStyle( fontSize: 24, - fontFamily: - FontFamily.battlefrontUI, + fontFamily: currentFont, height: 1, ), maxLines: 1, - overflow: .ellipsis, + overflow: TextOverflow.ellipsis, ), const SizedBox(height: 2), RichText( text: TextSpan( style: TextStyle( color: kGrayColor, - fontFamily: - FontFamily.battlefrontUI, + fontFamily: currentFont, fontSize: 15, ), children: [ - const TextSpan( - text: 'Reported by ', + TextSpan( + text: l10n.reportedBy, ), TextSpan( text: report.reporterName, - style: const TextStyle( + style: TextStyle( color: kWhiteColor, - fontWeight: .bold, - fontFamily: FontFamily - .battlefrontUI, + fontWeight: FontWeight.bold, + fontFamily: currentFont, fontSize: 15, ), ), - const TextSpan(text: ' for '), + TextSpan(text: l10n.forLabel), TextSpan( text: report.reason.name, - style: const TextStyle( + style: TextStyle( color: kWhiteColor, - fontWeight: .bold, - fontFamily: FontFamily - .battlefrontUI, + fontWeight: FontWeight.bold, + fontFamily: currentFont, fontSize: 15, ), ), - const TextSpan(text: ' on '), + TextSpan(text: l10n.onLabel), TextSpan( text: DateFormat.yMd().add_jm().format( DateTime.fromMillisecondsSinceEpoch( report.createdAt.toInt() * 1000, ).toLocal(), ), - style: const TextStyle( + style: TextStyle( color: kWhiteColor, - fontWeight: .bold, - fontFamily: FontFamily - .battlefrontUI, + fontWeight: FontWeight.bold, + fontFamily: currentFont, fontSize: 15, ), ), @@ -388,13 +388,12 @@ class _ReportViewState extends State { Padding( padding: const EdgeInsets.all(15), child: InfoLabel( - label: 'Description', + label: l10n.descriptionLabel, child: Text( report.description, - style: const TextStyle( + style: TextStyle( color: kWhiteColor, - fontFamily: - FontFamily.battlefrontUI, + fontFamily: currentFont, fontSize: 16, ), ), @@ -404,8 +403,7 @@ class _ReportViewState extends State { Padding( padding: const .all(15), child: InfoLabel( - label: - 'Evidence (${report.evidenceLinks.length})', + label: l10n.evidenceCountLabel(report.evidenceLinks.length), child: ListView.builder( shrinkWrap: true, physics: @@ -432,8 +430,7 @@ class _ReportViewState extends State { evidence, style: TextStyle( color: kActiveColor, - fontFamily: FontFamily - .battlefrontUI, + fontFamily: currentFont, fontSize: 16, decoration: TextDecoration @@ -491,23 +488,23 @@ class _ReportViewState extends State { ), child: Row( children: [ - const Expanded( + Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - 'REPORT VIEW', + l10n.reportViewTitle, style: TextStyle( fontSize: 20, - fontFamily: FontFamily.battlefrontUI, + fontFamily: currentFont, height: 1, ), ), Text( - 'VIEW REPORTS AND EVIDENCE', + l10n.viewReportsAndEvidence, style: TextStyle( color: kWhiteColor, - fontFamily: FontFamily.battlefrontUI, + fontFamily: currentFont, fontSize: 15, ), ), @@ -549,9 +546,9 @@ class _ReportViewState extends State { children: [ Expanded( child: KyberTabBar( - tabs: const [ - Text('REPORTS'), - Text('PUNISHMENTS'), + tabs: [ + Text(l10n.reportsTab), + Text(l10n.punishmentsTab), ], onChanged: (value) => setState( () => selectedPage = value, @@ -588,14 +585,14 @@ class _ReportViewState extends State { : null; }); NotificationService.info( - message: 'Report rejected', + message: l10n.reportRejected, ); }); }, ), IntrinsicWidth( child: KyberButton( - text: 'TAKE ACTION', + text: l10n.takeAction, onPressed: () { showKyberDialog( context: context, @@ -646,8 +643,7 @@ class _ReportViewState extends State { milliseconds: 150, ), style: TextStyle( - fontFamily: - FontFamily.battlefrontUI, + fontFamily: currentFont, color: hovered || index == selectedIndex @@ -670,7 +666,7 @@ class _ReportViewState extends State { const SizedBox(width: 10), Expanded( child: AutoSizeText( - 'By ${report.reportedPlayerName}', + l10n.reportedByPlayer(report.reportedPlayerName), maxLines: 1, minFontSize: 16, ), @@ -716,7 +712,7 @@ class _ReportViewState extends State { milliseconds: 150, ), style: TextStyle( - fontFamily: FontFamily.battlefrontUI, + fontFamily: currentFont, color: kWhiteColor, ), child: Container( @@ -739,10 +735,9 @@ class _ReportViewState extends State { ).toLocal(); if (punishment.expiresAt > 0) { - expiresText = - '${expiresAt.difference(issuedAt).inDays} Days'; + expiresText = l10n.daysDuration(expiresAt.difference(issuedAt).inDays); } else { - expiresText = 'Permanent'; + expiresText = l10n.permanent; } return Column( @@ -760,7 +755,7 @@ class _ReportViewState extends State { const SizedBox(width: 10), Expanded( child: AutoSizeText( - 'Banned By ${punishment.moderator.name}', + l10n.bannedByModerator(punishment.moderator.name), maxLines: 1, minFontSize: 16, ), @@ -768,13 +763,13 @@ class _ReportViewState extends State { ], ), Text( - 'Duration: $expiresText', + l10n.durationLabel(expiresText), style: const TextStyle( fontSize: 14, ), ), Text( - 'Issued: ${DateFormat.yMd().add_jm().format(issuedAt)}', + l10n.issuedLabel(DateFormat.yMd().add_jm().format(issuedAt)), style: const TextStyle( fontSize: 14, ), @@ -808,4 +803,4 @@ class _ReportViewState extends State { ], ); } -} +} \ No newline at end of file diff --git a/Launcher/lib/features/reports/screens/reports.dart b/Launcher/lib/features/reports/screens/reports.dart index 2682ac03..8edb4c3e 100644 --- a/Launcher/lib/features/reports/screens/reports.dart +++ b/Launcher/lib/features/reports/screens/reports.dart @@ -10,6 +10,7 @@ import 'package:kyber_launcher/features/reports/models/report_list_state.dart'; import 'package:kyber_launcher/features/reports/providers/report_list_cubit.dart'; import 'package:kyber_launcher/gen/assets.gen.dart'; import 'package:kyber_launcher/gen/fonts.gen.dart'; +import 'package:kyber_launcher/gen/l10n/app_localizations.dart'; import 'package:kyber_launcher/gen/rust/api/maxima.dart'; import 'package:kyber_launcher/shared/ui/buttons/button.dart'; import 'package:kyber_launcher/shared/ui/buttons/custom_icon_button.dart'; @@ -35,6 +36,10 @@ class _ReportsState extends State { @override Widget build(BuildContext context) { + final l10n = AppLocalizations.of(context)!; + final isEn = Localizations.localeOf(context).languageCode == 'en'; + final currentFont = isEn ? FontFamily.battlefrontUI : 'BattlefrontGlobal'; + const borderRadius = BorderRadius.only( topLeft: Radius.circular(kDefaultOuterBorderRadius), topRight: Radius.circular(kDefaultOuterBorderRadius), @@ -108,7 +113,7 @@ class _ReportsState extends State { return SuperListView( children: [ KyberFilterSection( - title: 'STATUS', + title: l10n.statusFilter, selectedItems: [cubit.filter.state], items: toSelectorItems( ReportFilterStatus.values, @@ -192,41 +197,41 @@ class _ReportsState extends State { ), alignment: Alignment.center, child: KyberHeader( - title: 'REPORTS', + title: l10n.reportsTitle, headerLength: 150, sections: [ const ExpandedHeaderSection( children: [], ), - const FixedWidthHeaderSection( + FixedWidthHeaderSection( width: 120, mainAxisAlignment: MainAxisAlignment.center, children: [ Text( - 'TOTAL REPORTS', + l10n.totalReportsHeader, textAlign: TextAlign.left, ), ], ), - const FixedWidthHeaderSection( + FixedWidthHeaderSection( width: 120, mainAxisAlignment: MainAxisAlignment.center, children: [ Text( - 'LATEST REPORT', + l10n.latestReportHeader, textAlign: TextAlign.left, ), ], ), - const FixedWidthHeaderSection( + FixedWidthHeaderSection( width: 180, mainAxisAlignment: MainAxisAlignment.center, children: [ Text( - 'LATEST REASON', + l10n.latestReasonHeader, textAlign: TextAlign.left, ), ], @@ -236,7 +241,7 @@ class _ReportsState extends State { mainAxisAlignment: MainAxisAlignment.center, children: [ - Text('STATUS'.toUpperCase()), + Text(l10n.statusHeader), ], ), ], @@ -262,7 +267,7 @@ class _ReportsState extends State { ); return Center( child: Text( - 'Error loading reports: ${state.message}', + l10n.errorLoadingReports(state.message), ), ); } @@ -300,9 +305,9 @@ class _ReportsState extends State { ), child: Text( report.targetUsername, - style: const TextStyle( - fontFamily: FontFamily - .battlefrontUI, + style: TextStyle( + fontFamily: + currentFont, fontSize: 17, color: Colors.white, fontWeight: @@ -411,25 +416,25 @@ class _ReportsState extends State { child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ - const SizedBox( + SizedBox( height: 61, child: Padding( - padding: EdgeInsets.all(13), + padding: const EdgeInsets.all(13), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - 'ARMSEC - CASE FILE', + l10n.armsecCaseFile, style: TextStyle( - fontFamily: FontFamily.battlefrontUI, + fontFamily: currentFont, fontSize: 21, height: 1, ), ), Text( - 'EVIDENCE AND DATA SUMMARY', + l10n.evidenceAndDataSummary, style: TextStyle( - fontFamily: FontFamily.battlefrontUI, + fontFamily: currentFont, fontSize: 14, color: kWhiteColor, height: 0.9, @@ -474,15 +479,15 @@ class _ReportsState extends State { } if (snapshot.hasError) { - return const Center( - child: Text('Error loading user'), + return Center( + child: Text(l10n.errorLoadingUser), ); } final user = snapshot.data; if (user == null) { - return const Center( - child: Text('User not found'), + return Center( + child: Text(l10n.userNotFound), ); } @@ -501,16 +506,16 @@ class _ReportsState extends State { children: [ Text( selectedReport!.targetUsername, - style: const TextStyle( - fontFamily: FontFamily.battlefrontUI, + style: TextStyle( + fontFamily: currentFont, fontSize: 19, height: 1, ), ), Text( - 'KYBER USER', - style: const TextStyle( - fontFamily: FontFamily.battlefrontUI, + l10n.kyberUser, + style: TextStyle( + fontFamily: currentFont, fontSize: 15, color: kWhiteColor, ), @@ -524,7 +529,7 @@ class _ReportsState extends State { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ KyberButton( - text: 'OPEN', + text: l10n.open, icon: const Icon(mt.Icons.play_arrow_rounded), onPressed: () => router.push( '/staff/reports/${selectedReport!.targetUserId}', @@ -541,11 +546,11 @@ class _ReportsState extends State { // selectedIndex: -1, // ), //), - const SizedBox( + SizedBox( width: 200, child: KyberDropdownSelector( items: [], - placeholder: 'OPTIONS', + placeholder: l10n.options, ), ), ], @@ -562,4 +567,4 @@ class _ReportsState extends State { ], ); } -} +} \ No newline at end of file diff --git a/Launcher/lib/features/server_browser/screens/ingame_view.dart b/Launcher/lib/features/server_browser/screens/ingame_view.dart index 235e8eac..f99559ba 100644 --- a/Launcher/lib/features/server_browser/screens/ingame_view.dart +++ b/Launcher/lib/features/server_browser/screens/ingame_view.dart @@ -7,6 +7,7 @@ import 'package:kyber_launcher/features/mod_browser/screens/mod_details.dart'; import 'package:kyber_launcher/features/reports/dialogs/report_player_dialog.dart'; import 'package:kyber_launcher/features/server_browser/providers/ingame_view_cubit.dart'; import 'package:kyber_launcher/gen/fonts.gen.dart'; +import 'package:kyber_launcher/gen/l10n/app_localizations.dart'; import 'package:kyber_launcher/shared/ui/layout/bordered_content.dart'; import 'package:kyber_launcher/shared/ui/ui.dart'; import 'package:super_sliver_list/super_sliver_list.dart'; @@ -21,6 +22,10 @@ class IngameView extends StatefulWidget { class _IngameViewState extends State { @override Widget build(BuildContext context) { + final l10n = AppLocalizations.of(context)!; + final isEn = Localizations.localeOf(context).languageCode == 'en'; + final currentFont = isEn ? FontFamily.battlefrontUI : 'BattlefrontGlobal'; + return BlocBuilder( builder: (context, state) { return BorderedContent( @@ -31,16 +36,16 @@ class _IngameViewState extends State { crossAxisAlignment: .start, children: [ Text( - state.server?.name ?? 'Unknown Server', - style: const TextStyle( - fontFamily: FontFamily.battlefrontUI, + state.server?.name ?? l10n.unknownServer, + style: TextStyle( + fontFamily: currentFont, fontSize: 20, height: 1, ), ), if (state.server != null && state.server!.official) Text( - 'Hosted by ${state.server?.creator ?? 'Unknown Server'}', + l10n.hostedBy(state.server?.creator ?? l10n.unknownServer), style: const TextStyle( fontSize: 12, color: kWhiteColor, @@ -50,7 +55,7 @@ class _IngameViewState extends State { ], ), KyberTooltip( - message: 'Back to Server Browser'.toUpperCase(), + message: l10n.backToServerBrowser.toUpperCase(), child: KyberIconButton( onPressed: router.pop, iconData: mt.Icons.close, @@ -64,13 +69,13 @@ class _IngameViewState extends State { KyberHeader( sections: [ ExpandedHeaderSection( - children: [Text('Event Log'.toUpperCase())], + children: [Text(l10n.eventLog.toUpperCase())], ), ExpandedHeaderSection( - children: [Text('Light Side'.toUpperCase())], + children: [Text(l10n.lightSide.toUpperCase())], ), ExpandedHeaderSection( - children: [Text('Dark Side'.toUpperCase())], + children: [Text(l10n.darkSide.toUpperCase())], ), ], ), @@ -199,6 +204,10 @@ class _TeamContainer extends StatefulWidget { class _TeamContainerState extends State<_TeamContainer> { @override Widget build(BuildContext context) { + final l10n = AppLocalizations.of(context)!; + final isEn = Localizations.localeOf(context).languageCode == 'en'; + final currentFont = isEn ? FontFamily.battlefrontUI : 'BattlefrontGlobal'; + return BlocBuilder( builder: (context, state) { final players = state.players @@ -233,8 +242,8 @@ class _TeamContainerState extends State<_TeamContainer> { children: [ Text( player.name, - style: const TextStyle( - fontFamily: FontFamily.battlefrontUI, + style: TextStyle( + fontFamily: currentFont, fontSize: 18, ), ), @@ -247,7 +256,7 @@ class _TeamContainerState extends State<_TeamContainer> { child: Row( children: [ KyberTooltip( - message: 'Report Player'.toUpperCase(), + message: l10n.reportPlayer.toUpperCase(), child: CustomIconButton( onPressed: () => showKyberDialog( context: context, @@ -281,4 +290,4 @@ class _TeamContainerState extends State<_TeamContainer> { }, ); } -} +} \ No newline at end of file diff --git a/Launcher/lib/features/server_browser/screens/server_browser.dart b/Launcher/lib/features/server_browser/screens/server_browser.dart index b94fe7aa..ed0a56ba 100644 --- a/Launcher/lib/features/server_browser/screens/server_browser.dart +++ b/Launcher/lib/features/server_browser/screens/server_browser.dart @@ -16,6 +16,7 @@ import 'package:kyber_launcher/features/server_browser/widgets/server_info_box/s import 'package:kyber_launcher/features/server_browser/widgets/server_list/server_list.dart'; import 'package:kyber_launcher/gen/assets.gen.dart'; import 'package:kyber_launcher/gen/fonts.gen.dart'; +import 'package:kyber_launcher/gen/l10n/app_localizations.dart'; import 'package:kyber_launcher/shared/ui/elements/filter_dropdown.dart'; import 'package:kyber_launcher/shared/ui/layout/bordered_content.dart'; import 'package:kyber_launcher/shared/ui/ui.dart'; @@ -110,6 +111,8 @@ class _HeaderBar extends StatelessWidget { @override Widget build(BuildContext context) { + final l10n = AppLocalizations.of(context)!; + return Align( child: SizedBox( child: Row( @@ -153,8 +156,6 @@ class _HeaderBar extends StatelessWidget { width: 120, child: BlocBuilder( builder: (context, state) { - final pageText = '${state.page ?? 0}/${state.pages ?? 0}'; - return KyberTabBar( selectedIndex: -1, onChanged: (value) { @@ -166,7 +167,7 @@ class _HeaderBar extends StatelessWidget { }, tabs: [ const Icon(mt.Icons.arrow_back_ios_new_rounded), - Text(pageText), + Text(l10n.paginationInfo(state.page ?? 0, state.pages ?? 0)), const Icon(mt.Icons.arrow_forward_ios_rounded), ], ); @@ -185,6 +186,8 @@ class _FilterDropdown extends StatelessWidget { @override Widget build(BuildContext context) { + final l10n = AppLocalizations.of(context)!; + return KyberSearchFilterDropdown( onSearchChanged: (value) { final filter = context.read().filter; @@ -199,7 +202,7 @@ class _FilterDropdown extends StatelessWidget { return SuperListView( children: [ KyberFilterSection( - title: 'REGION', + title: l10n.region, selectedItems: [cubit.filter.region], items: toSelectorItems( ServerRegion.values, @@ -212,7 +215,7 @@ class _FilterDropdown extends StatelessWidget { }, ), KyberFilterSection( - title: 'SERVER TYPE', + title: l10n.serverType, selectedItems: [cubit.filter.type], items: toSelectorItems( ServerType.values, @@ -225,7 +228,7 @@ class _FilterDropdown extends StatelessWidget { }, ), KyberFilterSection( - title: 'GAME TYPE', + title: l10n.gameType, selectedItems: [cubit.filter.gameType], items: toSelectorItems( GameType.values, @@ -238,7 +241,7 @@ class _FilterDropdown extends StatelessWidget { }, ), KyberFilterSection( - title: 'GAME MODE', + title: l10n.gameMode, selectedItems: cubit.filter.modes, includeAll: true, items: toSelectorItems( @@ -265,6 +268,10 @@ class _StatusWidget extends StatelessWidget { @override Widget build(BuildContext context) { + final l10n = AppLocalizations.of(context)!; + final isEn = Localizations.localeOf(context).languageCode == 'en'; + final currentFont = isEn ? FontFamily.battlefrontUI : 'BattlefrontGlobal'; + return BlocBuilder( builder: (context, apiState) { if (apiState.status != KyberStatusEnum.warning) { @@ -290,9 +297,9 @@ class _StatusWidget extends StatelessWidget { mainAxisAlignment: .center, children: [ Text( - 'WARNING', - style: .new( - fontFamily: FontFamily.battlefrontUI, + l10n.warning, + style: TextStyle( + fontFamily: currentFont, fontSize: 21, color: kDefaultActiveColor, shadows: [ @@ -319,9 +326,9 @@ class _StatusWidget extends StatelessWidget { children: [ Flexible( child: Text( - apiState.message ?? 'Warning message not available.', - style: const TextStyle( - fontFamily: FontFamily.battlefrontUI, + apiState.message ?? l10n.warningMessageFallback, + style: TextStyle( + fontFamily: currentFont, fontSize: 15, height: 1, ), @@ -337,4 +344,4 @@ class _StatusWidget extends StatelessWidget { }, ); } -} +} \ No newline at end of file diff --git a/Launcher/lib/features/server_browser/widgets/event_list.dart b/Launcher/lib/features/server_browser/widgets/event_list.dart index c5d73e93..d5d6ca62 100644 --- a/Launcher/lib/features/server_browser/widgets/event_list.dart +++ b/Launcher/lib/features/server_browser/widgets/event_list.dart @@ -6,6 +6,7 @@ import 'package:kyber/kyber.dart'; import 'package:kyber_launcher/core/config/colors.dart'; import 'package:kyber_launcher/features/events/providers/event_cubic.dart'; import 'package:kyber_launcher/gen/fonts.gen.dart'; +import 'package:kyber_launcher/gen/l10n/app_localizations.dart'; import 'package:kyber_launcher/shared/ui/ui.dart'; import 'package:super_sliver_list/super_sliver_list.dart'; import 'package:url_launcher/url_launcher_string.dart'; @@ -15,6 +16,10 @@ class HomeEventList extends StatelessWidget { @override Widget build(BuildContext context) { + final l10n = AppLocalizations.of(context)!; + final isEn = Localizations.localeOf(context).languageCode == 'en'; + final currentFont = isEn ? FontFamily.battlefrontUI : 'BattlefrontGlobal'; + return Expanded( child: KyberCard( padding: EdgeInsets.zero, @@ -28,8 +33,8 @@ class HomeEventList extends StatelessWidget { return Center( child: Text( state.error, - style: const TextStyle( - fontFamily: FontFamily.battlefrontUI, + style: TextStyle( + fontFamily: currentFont, fontSize: 18, ), ), @@ -41,26 +46,26 @@ class HomeEventList extends StatelessWidget { return Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ - const SizedBox( + SizedBox( height: 60, child: Padding( - padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12), + padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 12), child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.center, children: [ Text( - 'EVENTS & ANNOUNCEMENTS', + l10n.eventsAndAnnouncements, style: TextStyle( - fontFamily: FontFamily.battlefrontUI, + fontFamily: currentFont, fontSize: 21, height: 1, ), ), Text( - 'VIEW UPCOMING EVENTS & RECEIVE ANNOUNCEMENTS', + l10n.viewUpcomingEventsDesc, style: TextStyle( - fontFamily: FontFamily.battlefrontUI, + fontFamily: currentFont, fontSize: 14, color: kWhiteColor, height: 1, @@ -134,6 +139,9 @@ class _EventContainer extends StatelessWidget { @override Widget build(BuildContext context) { + final isEn = Localizations.localeOf(context).languageCode == 'en'; + final currentFont = isEn ? FontFamily.battlefrontUI : 'BattlefrontGlobal'; + return ButtonBuilder( onClick: () { if (post.link.isNotEmpty) { @@ -208,8 +216,8 @@ class _EventContainer extends StatelessWidget { children: [ Text( post.header, - style: const TextStyle( - fontFamily: FontFamily.battlefrontUI, + style: TextStyle( + fontFamily: currentFont, fontSize: 18, height: 1, ), @@ -226,7 +234,7 @@ class _EventContainer extends StatelessWidget { child: Text( post.body, style: TextStyle( - fontFamily: FontFamily.battlefrontUI, + fontFamily: currentFont, color: kActiveColor, fontSize: 12, height: 1, @@ -251,8 +259,8 @@ class _EventContainer extends StatelessWidget { children: [ Text( post.header, - style: const TextStyle( - fontFamily: FontFamily.battlefrontUI, + style: TextStyle( + fontFamily: currentFont, fontSize: 28, ), ), @@ -271,7 +279,7 @@ class _EventContainer extends StatelessWidget { child: Text( post.body, style: TextStyle( - fontFamily: FontFamily.battlefrontUI, + fontFamily: currentFont, color: kActiveColor, fontSize: 12, height: 1, @@ -292,4 +300,4 @@ class _EventContainer extends StatelessWidget { }, ); } -} +} \ No newline at end of file diff --git a/Launcher/lib/features/server_browser/widgets/server_list/server_list_header.dart b/Launcher/lib/features/server_browser/widgets/server_list/server_list_header.dart index f6654d53..a607a643 100644 --- a/Launcher/lib/features/server_browser/widgets/server_list/server_list_header.dart +++ b/Launcher/lib/features/server_browser/widgets/server_list/server_list_header.dart @@ -1,7 +1,10 @@ import 'package:fluent_ui/fluent_ui.dart'; import 'package:flutter/material.dart' as mt; import 'package:kyber_launcher/core/config/colors.dart'; -import 'package:kyber_launcher/shared/ui/ui.dart'; +import 'package:kyber_launcher/gen/fonts.gen.dart'; +import 'package:kyber_launcher/gen/l10n/app_localizations.dart'; +import 'package:kyber_launcher/shared/ui/elements/header/kyber_header.dart'; +import 'package:kyber_launcher/shared/ui/utils/button_builder.dart'; class ServerListHeader extends StatelessWidget { const ServerListHeader({super.key, this.withoutQuickJoin = false}); @@ -10,6 +13,10 @@ class ServerListHeader extends StatelessWidget { @override Widget build(BuildContext context) { + final l10n = AppLocalizations.of(context)!; + final isEn = Localizations.localeOf(context).languageCode == 'en'; + final currentFont = isEn ? FontFamily.battlefrontUI : 'BattlefrontGlobal'; + return Container( decoration: const BoxDecoration( border: Border.symmetric( @@ -21,27 +28,29 @@ class ServerListHeader extends StatelessWidget { ), alignment: Alignment.center, child: KyberHeader( - title: 'SERVER BROWSER', + title: l10n.serverBrowser.toUpperCase(), headerLength: 150, sections: [ const ExpandedHeaderSection(children: []), - const FixedWidthHeaderSection( + FixedWidthHeaderSection( width: 99, mainAxisAlignment: MainAxisAlignment.center, children: [ Text( - 'PLAYERS', + l10n.playersHeader, textAlign: TextAlign.left, + style: TextStyle(fontFamily: currentFont), ), ], ), - const FixedWidthHeaderSection( + FixedWidthHeaderSection( width: 120, mainAxisAlignment: MainAxisAlignment.center, children: [ Text( - 'SERVER TYPE', + l10n.serverType, textAlign: TextAlign.left, + style: TextStyle(fontFamily: currentFont), ), ], ), @@ -49,7 +58,12 @@ class ServerListHeader extends StatelessWidget { FixedWidthHeaderSection( width: 67, mainAxisAlignment: MainAxisAlignment.center, - children: [Text('PLAY'.toUpperCase())], + children: [ + Text( + l10n.playButton, + style: TextStyle(fontFamily: currentFont), + ), + ], ), ], ), @@ -75,4 +89,4 @@ class DashedLineVerticalPainter extends CustomPainter { @override bool shouldRepaint(CustomPainter oldDelegate) => true; -} +} \ No newline at end of file diff --git a/Launcher/lib/features/server_host/screens/server_host.dart b/Launcher/lib/features/server_host/screens/server_host.dart index 58a98c72..aebede28 100644 --- a/Launcher/lib/features/server_host/screens/server_host.dart +++ b/Launcher/lib/features/server_host/screens/server_host.dart @@ -15,6 +15,8 @@ import 'package:kyber_launcher/features/server_moderation/screens/moderation_ser import 'package:kyber_launcher/features/server_moderation/screens/server_moderation.dart'; import 'package:kyber_launcher/features/tutorial/models/tutorials/server_host_tutorial.dart'; import 'package:kyber_launcher/gen/assets.gen.dart'; +import 'package:kyber_launcher/gen/fonts.gen.dart'; +import 'package:kyber_launcher/gen/l10n/app_localizations.dart'; import 'package:kyber_launcher/shared/ui/buttons/button.dart'; import 'package:kyber_launcher/shared/ui/elements/kyber_input.dart'; import 'package:kyber_launcher/shared/ui/elements/kyber_tab_bar.dart'; @@ -46,6 +48,10 @@ class _ServerHostState extends State { @override Widget build(BuildContext context) { + final l10n = AppLocalizations.of(context)!; + final isEn = Localizations.localeOf(context).languageCode == 'en'; + final currentFont = isEn ? FontFamily.battlefrontUI : 'BattlefrontGlobal'; + return BlocListener( listenWhen: (previous, current) => previous is! KyberStatusHosting && current is KyberStatusHosting || @@ -79,7 +85,7 @@ class _ServerHostState extends State { if (!createServer && !state.selected) ...[ KyberButton( icon: const Icon(mt.Icons.add), - text: 'NEW', + text: l10n.newButton, onPressed: () { setState(() => createServer = true); }, @@ -118,9 +124,9 @@ class _ServerHostState extends State { SizedBox( width: 250, child: KyberTabBar( - tabs: const [ - Text('MODERATE'), - Text('MANAGE'), + tabs: [ + Text(l10n.moderate), + Text(l10n.manage), ], onChanged: (selectedIndex) { context.read().clear(); @@ -137,9 +143,9 @@ class _ServerHostState extends State { SizedBox( width: 250, child: KyberTabBar( - tabs: const [ - Text('ROTATION'), - Text('MODS'), + tabs: [ + Text(l10n.rotation), + Text(l10n.mods), ], onChanged: (selectedIndex) { context.read().clear(); @@ -166,7 +172,7 @@ class _ServerHostState extends State { ], Expanded( child: KyberInput( - placeholder: 'Search ...', + placeholder: l10n.searchPlaceholder, controller: searchController, onChanged: context .read() @@ -255,4 +261,4 @@ class _ServerHostState extends State { ), ); } -} +} \ No newline at end of file diff --git a/Launcher/lib/features/server_host/widgets/create_server/map_rotation_page.dart b/Launcher/lib/features/server_host/widgets/create_server/map_rotation_page.dart index d1deb5d9..f64c5ea5 100644 --- a/Launcher/lib/features/server_host/widgets/create_server/map_rotation_page.dart +++ b/Launcher/lib/features/server_host/widgets/create_server/map_rotation_page.dart @@ -22,6 +22,7 @@ import 'package:kyber_launcher/features/server_host/widgets/create_server/map_ro import 'package:kyber_launcher/features/server_host/widgets/create_server/map_rotation/mode_list.dart'; import 'package:kyber_launcher/gen/assets.gen.dart'; import 'package:kyber_launcher/gen/fonts.gen.dart'; +import 'package:kyber_launcher/gen/l10n/app_localizations.dart'; import 'package:kyber_launcher/injection_container.dart'; import 'package:kyber_launcher/main.dart'; import 'package:kyber_launcher/shared/ui/buttons/custom_icon_button.dart'; @@ -49,6 +50,10 @@ class _MapRotationPageState extends State { @override Widget build(BuildContext context) { + final l10n = AppLocalizations.of(context)!; + final isEn = Localizations.localeOf(context).languageCode == 'en'; + final currentFont = isEn ? FontFamily.battlefrontUI : 'BattlefrontGlobal'; + final cubit = context.read(); final kyberState = context.watch().state; @@ -60,7 +65,7 @@ class _MapRotationPageState extends State { child: Column( children: [ KyberHeader( - title: 'Active Rotation', + title: l10n.activeRotation, headerLength: 130, headerPadding: const EdgeInsets.only(left: 15, right: 10), sections: [ @@ -86,12 +91,12 @@ class _MapRotationPageState extends State { children: [ const SizedBox(width: 10), HeaderButton( - title: 'Reset', + title: l10n.reset, onClick: cubit.clear, ), const HeaderDivider(), HeaderButton( - title: 'Export', + title: l10n.export, onClick: () async { await showKyberDialog( context: context, @@ -104,12 +109,12 @@ class _MapRotationPageState extends State { ), const HeaderDivider(), HeaderButton( - title: 'Import', + title: l10n.importLabel, onClick: () async { final filePath = await FilePicker.platform .pickFiles( allowedExtensions: ['txt', 'json'], - dialogTitle: 'Import Map Rotation', + dialogTitle: l10n.importMapRotation, type: FileType.custom, ); @@ -159,7 +164,7 @@ class _MapRotationPageState extends State { } finally { if (rotation.isEmpty) { NotificationService.error( - message: 'Invalid file format', + message: l10n.invalidFileFormat, ); return; } @@ -176,7 +181,7 @@ class _MapRotationPageState extends State { context.read().setMaps(maps); NotificationService.info( - message: 'Map rotation imported', + message: l10n.mapRotationImported, ); } }, @@ -195,10 +200,10 @@ class _MapRotationPageState extends State { return Align( alignment: Alignment.topCenter, child: Text( - 'No maps active'.toUpperCase(), - style: const TextStyle( + l10n.noMapsActive.toUpperCase(), + style: TextStyle( fontSize: 16, - fontFamily: FontFamily.battlefrontUI, + fontFamily: currentFont, color: kInactiveColor, ), ), @@ -321,6 +326,10 @@ class _ActiveMap extends StatelessWidget { @override Widget build(BuildContext context) { + final l10n = AppLocalizations.of(context)!; + final isEn = Localizations.localeOf(context).languageCode == 'en'; + final currentFont = isEn ? FontFamily.battlefrontUI : 'BattlefrontGlobal'; + return ReorderableDragStartListener( index: index, child: SizedBox( @@ -380,8 +389,7 @@ class _ActiveMap extends StatelessWidget { Padding( padding: const EdgeInsets.only(right: 10), child: KyberTooltip( - message: - 'Custom map not found, this can happen if the map is not part of the current mod collection.\nThis map will be ignored when starting the server.', + message: l10n.customMapNotFoundTooltip, child: Icon( mt.Icons.warning_rounded, size: 20, @@ -391,10 +399,10 @@ class _ActiveMap extends StatelessWidget { ), Expanded( child: Text( - map?.name ?? 'Not found', - style: const TextStyle( + map?.name ?? l10n.notFound, + style: TextStyle( fontSize: 16, - fontFamily: FontFamily.battlefrontUI, + fontFamily: currentFont, fontWeight: FontWeight.bold, height: 1, ), @@ -406,7 +414,7 @@ class _ActiveMap extends StatelessWidget { mode.name, style: TextStyle( fontSize: 16, - fontFamily: FontFamily.battlefrontUI, + fontFamily: currentFont, color: kInactiveColor.darken(30), height: 1.9, ), @@ -435,4 +443,4 @@ class _ActiveMap extends StatelessWidget { ), ); } -} +} \ No newline at end of file diff --git a/Launcher/lib/features/server_host/widgets/settings_box/settings/server_settings.dart b/Launcher/lib/features/server_host/widgets/settings_box/settings/server_settings.dart index 5492b69f..f9a66c06 100644 --- a/Launcher/lib/features/server_host/widgets/settings_box/settings/server_settings.dart +++ b/Launcher/lib/features/server_host/widgets/settings_box/settings/server_settings.dart @@ -2,6 +2,8 @@ import 'package:fluent_ui/fluent_ui.dart'; import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:form_builder_validators/form_builder_validators.dart'; import 'package:kyber_launcher/core/services/notification_service.dart'; +import 'package:kyber_launcher/gen/fonts.gen.dart'; +import 'package:kyber_launcher/gen/l10n/app_localizations.dart'; import 'package:kyber_launcher/features/server_host/widgets/settings_box/server_settings_box.dart'; import 'package:kyber_launcher/shared/ui/ui.dart'; import 'package:super_sliver_list/super_sliver_list.dart'; @@ -11,16 +13,20 @@ class ServerSettings extends StatelessWidget { @override Widget build(BuildContext context) { + final l10n = AppLocalizations.of(context)!; + final isEn = Localizations.localeOf(context).languageCode == 'en'; + final currentFont = isEn ? FontFamily.battlefrontUI : 'BattlefrontGlobal'; + return SuperListView( children: [ KyberSectionDropdown( initialExpanded: true, - title: 'SERVER', + title: l10n.serverSectionTitle, child: KyberTable( - itemStyle: const TextStyle(fontSize: 17), + itemStyle: TextStyle(fontSize: 17, fontFamily: currentFont), items: [ KyberTableItem.custom( - title: 'MAX PLAYERS', + title: l10n.maxPlayersLabel, builder: (hovered) { return FormBuilderField( name: 'maxPlayers', @@ -47,7 +53,7 @@ class ServerSettings extends StatelessWidget { }, ), KyberTableItem.custom( - title: 'Max Spectators', + title: l10n.maxSpectatorsLabel, builder: (hovered) { return FormBuilderField( name: 'maxSpectators', @@ -73,7 +79,7 @@ class ServerSettings extends StatelessWidget { }, ), KyberTableItem.switchButton( - title: 'Proximity Chat', + title: l10n.proximityChat, value: true, onChange: (value) async { NotificationService.notImplemented(); @@ -84,17 +90,17 @@ class ServerSettings extends StatelessWidget { ), KyberSectionDropdown( initialExpanded: true, - title: 'Privacy', + title: l10n.privacySectionTitle, child: KyberTable( - itemStyle: const TextStyle(fontSize: 17), + itemStyle: TextStyle(fontSize: 17, fontFamily: currentFont), items: [ KyberTableItem.custom( - title: 'Password', + title: l10n.passwordLabel, builder: (hovered) { return KyberFormInputField( name: 'password', isSensitive: true, - placeholder: 'EXAMPLE', + placeholder: l10n.passwordPlaceholder, validator: FormBuilderValidators.compose( [ FormBuilderValidators.maxLength( @@ -112,4 +118,4 @@ class ServerSettings extends StatelessWidget { ], ); } -} +} \ No newline at end of file diff --git a/Launcher/lib/features/server_moderation/screens/moderation_server_list.dart b/Launcher/lib/features/server_moderation/screens/moderation_server_list.dart index 62b0c42c..cca948e5 100644 --- a/Launcher/lib/features/server_moderation/screens/moderation_server_list.dart +++ b/Launcher/lib/features/server_moderation/screens/moderation_server_list.dart @@ -10,6 +10,8 @@ import 'package:kyber_launcher/features/kyber/models/modes.dart'; import 'package:kyber_launcher/features/server_browser/widgets/server_list/entry.dart'; import 'package:kyber_launcher/features/server_moderation/providers/moderation_cubit.dart'; import 'package:kyber_launcher/features/server_moderation/providers/moderation_servers_cubit.dart'; +import 'package:kyber_launcher/gen/fonts.gen.dart'; +import 'package:kyber_launcher/gen/l10n/app_localizations.dart'; import 'package:kyber_launcher/shared/ui/ui.dart'; import 'package:super_sliver_list/super_sliver_list.dart'; import 'package:tinycolor2/tinycolor2.dart'; @@ -32,6 +34,10 @@ class _ModerationServerListState extends State { @override Widget build(BuildContext context) { + final l10n = AppLocalizations.of(context)!; + final isEn = Localizations.localeOf(context).languageCode == 'en'; + final currentFont = isEn ? FontFamily.battlefrontUI : 'BattlefrontGlobal'; + return Column( children: [ Expanded( @@ -61,7 +67,7 @@ class _ModerationServerListState extends State { if (servers.isEmpty) { return Center( child: StrokeText( - 'No servers found'.toUpperCase(), + l10n.noServersFound.toUpperCase(), color: kWhiteColor, fontWeight: FontWeight.w500, strokeColor: kWhiteBackgroundColor.darken(20), @@ -137,8 +143,10 @@ class _Header extends StatelessWidget { @override Widget build(BuildContext context) { - return const DecoratedBox( - decoration: BoxDecoration( + final l10n = AppLocalizations.of(context)!; + + return DecoratedBox( + decoration: const BoxDecoration( border: Border.symmetric( vertical: BorderSide( color: decoColor, @@ -147,9 +155,9 @@ class _Header extends StatelessWidget { ), ), child: KyberHeader( - title: 'Server Browser', + title: l10n.serverBrowser, headerLength: 150, - sections: [ + sections: const [ ExpandedHeaderSection( children: [], ), @@ -157,4 +165,4 @@ class _Header extends StatelessWidget { ), ); } -} +} \ No newline at end of file diff --git a/Launcher/lib/features/server_moderation/screens/server_moderation.dart b/Launcher/lib/features/server_moderation/screens/server_moderation.dart index db956329..ff3acffa 100644 --- a/Launcher/lib/features/server_moderation/screens/server_moderation.dart +++ b/Launcher/lib/features/server_moderation/screens/server_moderation.dart @@ -17,6 +17,7 @@ import 'package:kyber_launcher/features/server_moderation/dialogs/moderation_inp import 'package:kyber_launcher/features/server_moderation/providers/moderation_cubit.dart'; import 'package:kyber_launcher/gen/assets.gen.dart'; import 'package:kyber_launcher/gen/fonts.gen.dart'; +import 'package:kyber_launcher/gen/l10n/app_localizations.dart'; import 'package:kyber_launcher/shared/ui/ui.dart'; import 'package:logging/logging.dart'; import 'package:super_sliver_list/super_sliver_list.dart'; @@ -34,6 +35,10 @@ class ServerModeration extends StatefulWidget { class _ServerModerationState extends State { @override Widget build(BuildContext context) { + final l10n = AppLocalizations.of(context)!; + final isEn = Localizations.localeOf(context).languageCode == 'en'; + final currentFont = isEn ? FontFamily.battlefrontUI : 'BattlefrontGlobal'; + return BlocBuilder( builder: (context, state) { if (state.id == null) { @@ -50,14 +55,14 @@ class _ServerModerationState extends State { width: 131, children: [ const SizedBox(width: 10), - Text('OPTIONS'.toUpperCase()), + Text(l10n.optionsHeader.toUpperCase()), ], ), ExpandedHeaderSection( - children: [Text('MODERATORS'.toUpperCase())], + children: [Text(l10n.moderatorsHeader.toUpperCase())], ), ExpandedHeaderSection( - children: [Text('BANNED PLAYERS'.toUpperCase())], + children: [Text(l10n.bannedPlayersHeader.toUpperCase())], ), ], ), @@ -76,7 +81,7 @@ class _ServerModerationState extends State { spacing: 15, children: [ KyberButton( - text: 'PLAY', + text: l10n.playButton, onPressed: () async { if (state.server == null) return; @@ -105,7 +110,7 @@ class _ServerModerationState extends State { }, ), KyberButton( - text: 'SPECTATE', + text: l10n.spectateButton, onPressed: () { KyberServerHelper.joinServer( state.server!, @@ -122,7 +127,7 @@ class _ServerModerationState extends State { child: Column( children: [ KyberButton( - text: 'COPY LINK', + text: l10n.copyLinkButton, onPressed: () { final uri = Uri( scheme: 'https', @@ -137,7 +142,7 @@ class _ServerModerationState extends State { .new(text: uri.toString()), ); NotificationService.info( - message: 'Copied to clipboard!', + message: l10n.copiedToClipboard, ); }, ), @@ -145,26 +150,26 @@ class _ServerModerationState extends State { ), ), const CardSection(), - const Padding( - padding: EdgeInsets.all(10), + Padding( + padding: const EdgeInsets.all(10), child: Column( spacing: 15, children: [ NormalButton( - label: Text('EXPORT BANS'), + label: Text(l10n.exportBans), onPressed: NotificationService.notImplemented, ), NormalButton( - label: Text('IMPORT BANS'), + label: Text(l10n.importBans), onPressed: NotificationService.notImplemented, ), NormalButton( - label: Text('KICK ALL'), + label: Text(l10n.kickAll), onPressed: NotificationService.notImplemented, ), NormalButton( label: Text( - 'BAN ALL', + l10n.banAll, textAlign: TextAlign.center, ), onPressed: NotificationService.notImplemented, @@ -192,13 +197,13 @@ class _ServerModerationState extends State { KyberHeader( sections: [ ExpandedHeaderSection( - children: [Text('Event Log'.toUpperCase())], + children: [Text(l10n.eventLogHeader.toUpperCase())], ), ExpandedHeaderSection( - children: [Text('Light Side'.toUpperCase())], + children: [Text(l10n.lightSideHeader.toUpperCase())], ), ExpandedHeaderSection( - children: [Text('Dark Side'.toUpperCase())], + children: [Text(l10n.darkSideHeader.toUpperCase())], ), ], ), @@ -232,11 +237,15 @@ class _UserManager extends StatefulWidget { class _UserManagerState extends State<_UserManager> { @override Widget build(BuildContext context) { + final l10n = AppLocalizations.of(context)!; + final isEn = Localizations.localeOf(context).languageCode == 'en'; + final currentFont = isEn ? FontFamily.battlefrontUI : 'BattlefrontGlobal'; + return KyberEventContainer( child: Text( - 'User Manager'.toUpperCase(), - style: const TextStyle( - fontFamily: FontFamily.battlefrontUI, + l10n.userManager.toUpperCase(), + style: TextStyle( + fontFamily: currentFont, fontWeight: FontWeight.bold, fontSize: 18, ), @@ -258,6 +267,10 @@ class _ConsoleState extends State<_Console> { @override Widget build(BuildContext context) { + final l10n = AppLocalizations.of(context)!; + final isEn = Localizations.localeOf(context).languageCode == 'en'; + final currentFont = isEn ? FontFamily.battlefrontUI : 'BattlefrontGlobal'; + return Column( children: [ Expanded( @@ -290,29 +303,29 @@ class _ConsoleState extends State<_Console> { padding: const EdgeInsets.all(15), child: mt.TextFormField( controller: controller, - style: const TextStyle( + style: TextStyle( color: kWhiteColor, - fontFamily: FontFamily.battlefrontUI, + fontFamily: currentFont, height: 1.5, ), maxLines: 4, minLines: 1, - decoration: const mt.InputDecoration( - hintText: '/COMMAND OR SEND MESSAGE', + decoration: mt.InputDecoration( + hintText: l10n.consoleHint, hintStyle: TextStyle( fontSize: 14, height: 1, color: kWhiteColor, - fontFamily: FontFamily.battlefrontUI, + fontFamily: currentFont, ), isDense: true, - enabledBorder: mt.OutlineInputBorder( + enabledBorder: const mt.OutlineInputBorder( borderSide: BorderSide(color: kGrayColor, width: 2), borderRadius: BorderRadius.all( Radius.circular(kDefaultInnerBorderRadius), ), ), - focusedBorder: mt.OutlineInputBorder( + focusedBorder: const mt.OutlineInputBorder( borderSide: BorderSide(color: kWhiteColor, width: 2), borderRadius: BorderRadius.all( Radius.circular(kDefaultInnerBorderRadius), @@ -483,6 +496,10 @@ class _Punishment extends StatelessWidget { @override Widget build(BuildContext context) { + final l10n = AppLocalizations.of(context)!; + final isEn = Localizations.localeOf(context).languageCode == 'en'; + final currentFont = isEn ? FontFamily.battlefrontUI : 'BattlefrontGlobal'; + return Column( children: [ ButtonBuilder( @@ -511,8 +528,8 @@ class _Punishment extends StatelessWidget { children: [ Text( '${punishment.user.name} (${punishment.user.id})', - style: const TextStyle( - fontFamily: FontFamily.battlefrontUI, + style: TextStyle( + fontFamily: currentFont, fontSize: 18, ), ), @@ -551,6 +568,13 @@ class _Punishment extends StatelessWidget { final until = DateTime.fromMillisecondsSinceEpoch( punishment.expiresAt.toInt(), ); + final untilText = punishment.expiresAt == 0 + ? l10n.permanent + : DateFormat.yMd().format(until); + final durationSuffix = punishment.expiresAt != 0 + ? ' (${formatDuration(until.difference(DateTime.now()), l10n)})' + : ''; + return Padding( padding: const EdgeInsets.symmetric( horizontal: 12, @@ -560,14 +584,14 @@ class _Punishment extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - 'UNTIL: ${punishment.expiresAt == 0 ? 'PERMANENT' : DateFormat.yMd().format(until)} ${punishment.expiresAt != 0 ? '(${formatDuration(until.difference(DateTime.now()))})' : ''}', + l10n.untilLabel('$untilText$durationSuffix'), style: const TextStyle( fontFamily: FontFamily.iBMPlexMono, fontSize: 12, ), ), Text( - 'REASON: ${punishment.reason}', + l10n.reasonLabel(punishment.reason), style: const TextStyle( fontFamily: FontFamily.iBMPlexMono, fontSize: 12, @@ -580,18 +604,18 @@ class _Punishment extends StatelessWidget { spacing: 10, children: [ KyberButton( - text: 'UNBAN', + text: l10n.unban, onPressed: () { context.read().unbanPlayer( punishment.user.id, ); NotificationService.info( - message: 'Player unbanned', + message: l10n.playerUnbanned, ); }, ), - const KyberButton( - text: 'MODIFY', + KyberButton( + text: l10n.modify, onPressed: NotificationService.notImplemented, ), ], @@ -612,9 +636,9 @@ class _Punishment extends StatelessWidget { ); } - String formatDuration(Duration duration) { + String formatDuration(Duration duration, AppLocalizations l10n) { if (duration.isNegative) { - return 'Time has elapsed'; + return l10n.timeElapsed; } final days = duration.inDays; @@ -625,28 +649,32 @@ class _Punishment extends StatelessWidget { final parts = []; if (days > 0) { - parts.add("$days day${days != 1 ? 's' : ''}"); + parts.add(l10n.daysRemaining(days)); } if (hours > 0 && days < 1) { - parts.add("$hours hour${hours != 1 ? 's' : ''}"); + parts.add(l10n.hoursRemaining(hours)); } if (minutes > 0 && days < 1 && hours < 1) { - parts.add("$minutes minute${minutes != 1 ? 's' : ''}"); + parts.add(l10n.minutesRemaining(minutes)); } if (seconds > 0 && days < 1 && hours < 1 && minutes < 1) { - parts.add("$seconds second${seconds != 1 ? 's' : ''}"); + parts.add(l10n.secondsRemaining(seconds)); } - return '${parts.join(", ")} remaining'; + return l10n.remaining(parts.join(", ")); } } class _ModeratorContainerState extends State<_ModeratorContainer> { @override Widget build(BuildContext context) { + final l10n = AppLocalizations.of(context)!; + final isEn = Localizations.localeOf(context).languageCode == 'en'; + final currentFont = isEn ? FontFamily.battlefrontUI : 'BattlefrontGlobal'; + return BlocBuilder( builder: (context, state) { final moderators = state.moderators; @@ -679,8 +707,8 @@ class _ModeratorContainerState extends State<_ModeratorContainer> { children: [ Text( player.name, - style: const TextStyle( - fontFamily: FontFamily.battlefrontUI, + style: TextStyle( + fontFamily: currentFont, fontSize: 18, ), ), @@ -692,12 +720,12 @@ class _ModeratorContainerState extends State<_ModeratorContainer> { curve: Curves.easeOut, child: Row( children: [ - _ModeratorWidget(state, hovered, player), + _ModeratorWidget(state, hovered, player, l10n), ], ), ), if (state.isModerator(player.id)) ...[ - _ModeratorWidget(state, hovered, player), + _ModeratorWidget(state, hovered, player, l10n), ], ], ), @@ -721,8 +749,9 @@ class _ModeratorContainerState extends State<_ModeratorContainer> { ModerationServerState state, bool hovered, KyberPlayer player, + AppLocalizations l10n, ) => KyberTooltip( - message: 'Promote User'.toUpperCase(), + message: l10n.promoteUserTooltip.toUpperCase(), child: CustomSvgButton( hoverColor: Colors.black, onPressed: () async { @@ -736,7 +765,7 @@ class _ModeratorContainerState extends State<_ModeratorContainer> { ); } else { NotificationService.showNotification( - message: 'An error occurred', + message: l10n.errorOccurred, severity: InfoBarSeverity.error, ); Logger.root.severe('Error promoting player', error, stackTrace); @@ -755,7 +784,7 @@ class _ModeratorContainerState extends State<_ModeratorContainer> { ); } else { NotificationService.showNotification( - message: 'An error occurred', + message: l10n.errorOccurred, severity: InfoBarSeverity.error, ); Logger.root.severe( @@ -793,6 +822,10 @@ class _TeamContainer extends StatefulWidget { class _TeamContainerState extends State<_TeamContainer> { @override Widget build(BuildContext context) { + final l10n = AppLocalizations.of(context)!; + final isEn = Localizations.localeOf(context).languageCode == 'en'; + final currentFont = isEn ? FontFamily.battlefrontUI : 'BattlefrontGlobal'; + return BlocBuilder( builder: (context, state) { final players = state.players @@ -827,8 +860,8 @@ class _TeamContainerState extends State<_TeamContainer> { children: [ Text( player.name, - style: const TextStyle( - fontFamily: FontFamily.battlefrontUI, + style: TextStyle( + fontFamily: currentFont, fontSize: 18, ), ), @@ -841,7 +874,7 @@ class _TeamContainerState extends State<_TeamContainer> { child: Row( children: [ KyberTooltip( - message: 'Swap Player'.toUpperCase(), + message: l10n.swapPlayerTooltip.toUpperCase(), child: CustomSvgButton( onPressed: () { context @@ -856,7 +889,7 @@ class _TeamContainerState extends State<_TeamContainer> { ), const SizedBox(width: 5), KyberTooltip( - message: 'Kick player'.toUpperCase(), + message: l10n.kickPlayerTooltip.toUpperCase(), child: CustomSvgButton( onPressed: () async { final reason = @@ -884,7 +917,7 @@ class _TeamContainerState extends State<_TeamContainer> { ); } else { NotificationService.showNotification( - message: 'An error occurred', + message: l10n.errorOccurred, severity: InfoBarSeverity.error, ); @@ -904,10 +937,10 @@ class _TeamContainerState extends State<_TeamContainer> { ), const SizedBox(width: 5), KyberTooltip( - message: 'Ban player'.toUpperCase(), + message: l10n.banPlayerTooltip.toUpperCase(), child: CustomSvgButton( onPressed: () async { - final result = await showKyberDialog( + await showKyberDialog( context: context, builder: (_) => BlocProvider.value( value: context @@ -934,13 +967,13 @@ class _TeamContainerState extends State<_TeamContainer> { .servicePlayer ?.id == state.server?.creatorId) - _ModeratorWidget(state, hovered, player), + _ModeratorWidget(state, hovered, player, l10n), ], ), ), if (state.moderators.contains(player) || state.server?.creatorId == player.id) ...[ - _ModeratorWidget(state, hovered, player), + _ModeratorWidget(state, hovered, player, l10n), ], ], ), @@ -964,8 +997,9 @@ class _TeamContainerState extends State<_TeamContainer> { ModerationServerState state, bool hovered, ServerPlayer player, + AppLocalizations l10n, ) => KyberTooltip( - message: 'Promote User'.toUpperCase(), + message: l10n.promoteUserTooltip.toUpperCase(), child: CustomSvgButton( hoverColor: Colors.black, onPressed: () async { @@ -979,7 +1013,7 @@ class _TeamContainerState extends State<_TeamContainer> { ); } else { NotificationService.showNotification( - message: 'An error occurred', + message: l10n.errorOccurred, severity: InfoBarSeverity.error, ); Logger.root.severe('Error promoting player', error, stackTrace); @@ -998,7 +1032,7 @@ class _TeamContainerState extends State<_TeamContainer> { ); } else { NotificationService.showNotification( - message: 'An error occurred', + message: l10n.errorOccurred, severity: InfoBarSeverity.error, ); Logger.root.severe( @@ -1023,4 +1057,4 @@ class _TeamContainerState extends State<_TeamContainer> { size: 17, ), ); -} +} \ No newline at end of file diff --git a/Launcher/lib/features/settings/screens/background_selector.dart b/Launcher/lib/features/settings/screens/background_selector.dart index f8648be9..59ffef63 100644 --- a/Launcher/lib/features/settings/screens/background_selector.dart +++ b/Launcher/lib/features/settings/screens/background_selector.dart @@ -8,6 +8,7 @@ import 'package:kyber_launcher/core/services/app_settings.dart'; import 'package:kyber_launcher/core/services/notification_service.dart'; import 'package:kyber_launcher/gen/assets.gen.dart'; import 'package:kyber_launcher/gen/fonts.gen.dart'; +import 'package:kyber_launcher/gen/l10n/app_localizations.dart'; import 'package:kyber_launcher/shared/ui/ui.dart'; import 'package:logging/logging.dart'; import 'package:path/path.dart'; @@ -29,6 +30,10 @@ class _BackgroundSelectorState extends State { @override Widget build(BuildContext context) { + final l10n = AppLocalizations.of(context)!; + final isEn = Localizations.localeOf(context).languageCode == 'en'; + final currentFont = isEn ? FontFamily.battlefrontUI : 'BattlefrontGlobal'; + return Column( mainAxisAlignment: MainAxisAlignment.center, children: [ @@ -57,9 +62,9 @@ class _BackgroundSelectorState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - 'SELECT A BACKGROUND', + l10n.selectBackground, style: TextStyle( - fontFamily: FontFamily.battlefrontUI, + fontFamily: currentFont, fontSize: 28, height: 1.2, color: kActiveColor, @@ -67,10 +72,9 @@ class _BackgroundSelectorState extends State { ), Text( // either 8 already done backgrounds or user can select a custom background - 'Choose from 8 backgrounds or select a custom background' - .toUpperCase(), - style: const TextStyle( - fontFamily: FontFamily.battlefrontUI, + l10n.chooseBackgroundDesc.toUpperCase(), + style: TextStyle( + fontFamily: currentFont, fontSize: 19, height: 1, ), @@ -100,30 +104,30 @@ class _BackgroundSelectorState extends State { mainAxisAlignment: MainAxisAlignment.end, children: [ KyberButton( - text: 'CUSTOM BACKGROUND', + text: l10n.customBackground, onPressed: () async { await showKyberDialog( context: context, builder: (context) => KyberContentDialog( - title: const Text( - 'CHANGE BACKGROUND', + title: Text( + l10n.changeBackground, ), constraints: const BoxConstraints( maxWidth: 600, maxHeight: 400, ), - content: const Column( + content: Column( children: [ Text( - 'Select a background image for the launcher', - style: TextStyle( + l10n.selectImageDesc, + style: const TextStyle( color: kWhiteColor, fontSize: 15, ), ), Text( - 'The image resolution should be at least 1920x1080', - style: TextStyle( + l10n.minResolutionDesc, + style: const TextStyle( color: kWhiteColor, fontSize: 15, ), @@ -132,12 +136,12 @@ class _BackgroundSelectorState extends State { ), actions: [ KyberButton( - text: 'CANCEL', + text: l10n.cancel.toUpperCase(), onPressed: () => Navigator.of(context).pop(), ), KyberButton( - text: 'Reset', + text: l10n.reset, onPressed: () async { try { await File( @@ -162,7 +166,7 @@ class _BackgroundSelectorState extends State { }, ), KyberButton( - text: 'Select Image', + text: l10n.selectImage, onPressed: () async { final result = await FilePicker .platform @@ -174,7 +178,7 @@ class _BackgroundSelectorState extends State { 'gif', ], dialogTitle: - 'Select a background image', + l10n.selectImagePickerTitle, type: FileType.custom, ); @@ -186,7 +190,7 @@ class _BackgroundSelectorState extends State { 1024 * 1024 * 10) { NotificationService.showNotification( message: - 'The file is too large. Please select a file smaller than 10MB', + l10n.fileTooLarge, severity: InfoBarSeverity.error, ); @@ -206,7 +210,7 @@ class _BackgroundSelectorState extends State { image.height < 1080) { NotificationService.showNotification( message: - 'The image resolution is too low. Please select an image with a resolution of at least 1920x1080', + l10n.resolutionTooLow, severity: InfoBarSeverity.error, ); @@ -214,7 +218,7 @@ class _BackgroundSelectorState extends State { } NotificationService.info( - message: 'Copying image...', + message: l10n.copyingImage, ); if (Preferences .customization @@ -406,4 +410,4 @@ class _BackgroundTile extends StatelessWidget { }, ); } -} +} \ No newline at end of file diff --git a/Launcher/lib/features/settings/screens/pages/accounts_and_updates.dart b/Launcher/lib/features/settings/screens/pages/accounts_and_updates.dart index 2a1d013e..d8a537e5 100644 --- a/Launcher/lib/features/settings/screens/pages/accounts_and_updates.dart +++ b/Launcher/lib/features/settings/screens/pages/accounts_and_updates.dart @@ -8,6 +8,7 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_inappwebview/flutter_inappwebview.dart'; import 'package:kyber/kyber.dart'; import 'package:kyber_launcher/core/core.dart'; +import 'package:kyber_launcher/gen/l10n/app_localizations.dart'; import 'package:kyber_launcher/features/kyber/providers/kyber_proxy_cubit.dart'; import 'package:kyber_launcher/features/maxima/providers/maxima_cubit.dart'; import 'package:kyber_launcher/features/nexusmods/dialogs/nexusmods_login.dart'; @@ -35,9 +36,11 @@ class AccountsAndUpdates extends StatelessWidget { @override Widget build(BuildContext context) { + final l10n = AppLocalizations.of(context)!; + return SuperListView( children: [ - const SettingsHeader(title: 'ACCOUNTS'), + SettingsHeader(title: l10n.accounts), HiveListener( box: box, keys: const ['nexusmods_login'], @@ -45,10 +48,10 @@ class AccountsAndUpdates extends StatelessWidget { builder: (context, state) => KyberTable( items: [ KyberTableItem.selector( - title: 'PROXY', + title: l10n.proxy, items: state.proxies.map((e) { final ping = e.ping == 99999 - ? 'UNAVAILABLE' + ? l10n.unavailable : '${e.ping}ms'; return KyberSelectorItem( title: '${e.proxy.name} ($ping)', @@ -62,7 +65,7 @@ class AccountsAndUpdates extends StatelessWidget { }, ), KyberTableItem.button( - title: 'Reset Kyber Token', + title: l10n.resetKyberToken, onClick: () async { final result = await showKyberDialog( context: context, @@ -77,10 +80,10 @@ class AccountsAndUpdates extends StatelessWidget { await context.read().requestLogin(); }, - text: 'Reset', + text: l10n.reset, ), KyberTableItem.button( - title: 'NexusMods', + title: l10n.nexusMods, onClick: () async { if (Preferences.nexusMods.isLoggedIn) { await sl.get().deleteToken(); @@ -100,11 +103,11 @@ class AccountsAndUpdates extends StatelessWidget { ); } }, - text: Preferences.nexusMods.isLoggedIn ? 'Logout' : 'Login', + text: Preferences.nexusMods.isLoggedIn ? l10n.logout : l10n.login, ), KyberTableItem.button( - title: 'Logout', - text: 'EA Logout', + title: l10n.logout, + text: l10n.eaLogout, onClick: () async { await File( '${Platform.environment['APPDATA']}\\ArmchairDevelopers\\Maxima\\data\\auth.toml', @@ -129,14 +132,14 @@ class AccountsAndUpdates extends StatelessWidget { ), ), ), - const SettingsHeader(title: 'LINKED ACCOUNTS'), + SettingsHeader(title: l10n.linkedAccounts), BlocBuilder( bloc: context.read(), builder: (context, _) => KyberTable( items: [ if (context.read().state.discordData != null) KyberTableItem.custom( - title: 'Discord', + title: l10n.discord, onClick: () async { await sl .get() @@ -176,7 +179,7 @@ class AccountsAndUpdates extends StatelessWidget { ), const SizedBox(width: 8), Text( - '${discord.globalName} | Unlink', + l10n.discordUnlink(discord.globalName), style: const TextStyle( fontSize: 18, fontWeight: FontWeight.w500, @@ -191,8 +194,8 @@ class AccountsAndUpdates extends StatelessWidget { ) else KyberTableItem.button( - title: 'Discord', - text: 'Connect', + title: l10n.discord, + text: l10n.connect, onClick: () async { final service = sl.get(); final host = service.httpHostname; @@ -207,8 +210,8 @@ class AccountsAndUpdates extends StatelessWidget { ), if (!context.read().state.isPatron) KyberTableItem.button( - title: 'Connect Patreon', - text: 'Connect', + title: l10n.connectPatreon, + text: l10n.connect, onClick: () async { await showKyberDialog( context: context, @@ -222,16 +225,16 @@ class AccountsAndUpdates extends StatelessWidget { ], ), ), - const SettingsHeader(title: 'UPDATES'), + SettingsHeader(title: l10n.updates), KyberTable( items: [ KyberTableItem.switchButton( - title: 'Automatically Update', + title: l10n.automaticallyUpdate, value: true, ), KyberTableItem.button( - title: 'Release Channel', - text: 'Select', + title: l10n.releaseChannel, + text: l10n.select, onClick: () async { await showKyberDialog( context: context, @@ -246,8 +249,8 @@ class AccountsAndUpdates extends StatelessWidget { }, ), KyberTableItem.button( - title: 'Force update', - text: 'UPDATE NOW', + title: l10n.forceUpdate, + text: l10n.updateNow, onClick: () => showKyberDialog( context: context, builder: (_) => const UpdateDialog(), @@ -255,7 +258,7 @@ class AccountsAndUpdates extends StatelessWidget { ), ], ), - const SettingsHeader(title: 'OTHER'), + SettingsHeader(title: l10n.other), HiveListener( box: box, keys: const [ @@ -269,14 +272,14 @@ class AccountsAndUpdates extends StatelessWidget { builder: (_) => KyberTable( items: [ KyberTableItem.switchButton( - title: 'Remember Window Position', + title: l10n.rememberWindowPosition, value: Preferences.customization.rememberWindowPosition, onChange: (value) async { Preferences.customization.rememberWindowPosition = value; }, ), KyberTableItem.button( - title: 'Licenses', + title: l10n.licenses, onClick: () { Navigator.of(context).push( mt.MaterialPageRoute( @@ -317,11 +320,11 @@ class AccountsAndUpdates extends StatelessWidget { ), ); }, - text: 'Show Licenses', + text: l10n.showLicenses, ), KyberTableItem.button( - title: 'Logout', - text: 'EA Logout', + title: l10n.logout, + text: l10n.eaLogout, onClick: () async { await File( '${Platform.environment['APPDATA']}\\ArmchairDevelopers\\Maxima\\data\\auth.toml', @@ -342,7 +345,7 @@ class AccountsAndUpdates extends StatelessWidget { }, ), KyberTableItem.switchButton( - title: 'Developer Mode', + title: l10n.developerMode, value: Preferences.general.developerMode, onChange: (value) { Preferences.general.developerMode = value; @@ -350,8 +353,8 @@ class AccountsAndUpdates extends StatelessWidget { ), if (Preferences.general.developerMode) ...[ KyberTableItem.button( - title: 'Environment', - text: 'Select', + title: l10n.environment, + text: l10n.select, onClick: () { showKyberDialog( context: context, @@ -365,7 +368,7 @@ class AccountsAndUpdates extends StatelessWidget { )) ...[ ...[ KyberTableItem.selector( - title: 'API Environment', + title: l10n.apiEnvironment, items: ['prod', 'stage'].map((e) { return KyberSelectorItem( title: e, @@ -396,8 +399,8 @@ class AccountsAndUpdates extends StatelessWidget { }, ), KyberTableItem.button( - title: 'Set Nexus API Token', - text: 'Set token', + title: l10n.setNexusApiToken, + text: l10n.setToken, onClick: () async { final token = await showKyberDialog( context: context, @@ -412,8 +415,8 @@ class AccountsAndUpdates extends StatelessWidget { }, ), KyberTableItem.button( - title: 'Set Background Image', - text: 'Set Image', + title: l10n.setBackgroundImage, + text: l10n.setImage, onClick: () async { final result = await showKyberDialog( context: context, @@ -433,20 +436,20 @@ class AccountsAndUpdates extends StatelessWidget { }, ), KyberTableItem.switchButton( - title: 'Dummy Server', + title: l10n.dummyServer, onChange: (value) => Preferences.admin.dummyServer = value, value: Preferences.admin.dummyServer, ), ], KyberTableItem.switchButton( - title: 'Remove Background', + title: l10n.removeBackground, value: Preferences.admin.removeBackground, onChange: (value) => Preferences.admin.removeBackground = value, ), KyberTableItem.button( - title: 'Copy Kyber Token', - text: 'Copy', + title: l10n.copyKyberToken, + text: l10n.copy, onClick: () async { final token = sl.get().token; if (token == null) { @@ -467,4 +470,4 @@ class AccountsAndUpdates extends StatelessWidget { String getAvatarUrl(String userId, {required String hash}) { return 'https://cdn.discordapp.com/avatars/$userId/$hash.png?size=512'; } -} +} \ No newline at end of file diff --git a/Launcher/lib/features/settings/screens/pages/language_and_accessibility.dart b/Launcher/lib/features/settings/screens/pages/language_and_accessibility.dart index b4a7be07..c49c20b2 100644 --- a/Launcher/lib/features/settings/screens/pages/language_and_accessibility.dart +++ b/Launcher/lib/features/settings/screens/pages/language_and_accessibility.dart @@ -1,6 +1,8 @@ import 'package:fluent_ui/fluent_ui.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:kyber_launcher/gen/l10n/app_localizations.dart'; import 'package:kyber_launcher/core/config/colors.dart'; +import 'package:kyber_launcher/core/i18n/app_locale.dart'; import 'package:kyber_launcher/core/routing/app_router.dart'; import 'package:kyber_launcher/core/services/app_settings.dart'; import 'package:kyber_launcher/features/kyber/providers/kyber_proxy_cubit.dart'; @@ -17,23 +19,135 @@ import 'package:url_launcher/url_launcher_string.dart'; class LanguageAndAccessibility extends StatelessWidget { const LanguageAndAccessibility({super.key}); + // NOTE: Helper method to show a dialog for language selection + void _showLanguageDialog(BuildContext context) async { + await showKyberDialog( + context: context, + builder: (context) => KyberContentDialog( + title: const Text('SELECT LANGUAGE'), + content: SizedBox( + // Limit height if list gets too long + height: 400, + child: SingleChildScrollView( + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _LanguageOption( + label: 'English', + localeCode: 'en', + onSelected: () => _setLocale(context, 'en'), + ), + const SizedBox(height: 5), + _LanguageOption( + label: 'Deutsch', // German + localeCode: 'de', + onSelected: () => _setLocale(context, 'de'), + ), + const SizedBox(height: 5), + _LanguageOption( + label: 'Français', // French + localeCode: 'fr', + onSelected: () => _setLocale(context, 'fr'), + ), + const SizedBox(height: 5), + _LanguageOption( + label: 'Español', // Spanish + localeCode: 'es', + onSelected: () => _setLocale(context, 'es'), + ), + const SizedBox(height: 5), + _LanguageOption( + label: 'Polski', // Polish + localeCode: 'pl', + onSelected: () => _setLocale(context, 'pl'), + ), + const SizedBox(height: 5), + _LanguageOption( + label: 'Русский', // Russian + localeCode: 'ru', + onSelected: () => _setLocale(context, 'ru'), + ), + const SizedBox(height: 5), + _LanguageOption( + label: 'Português', // Portuguese + localeCode: 'pt', + onSelected: () => _setLocale(context, 'pt'), + ), + const SizedBox(height: 5), + _LanguageOption( + label: 'Українська', // Ukrainian + localeCode: 'uk', + onSelected: () => _setLocale(context, 'uk'), + ), + const SizedBox(height: 5), + _LanguageOption( + label: 'Svenska', // Swedish + localeCode: 'sv', + onSelected: () => _setLocale(context, 'sv'), + ), + const SizedBox(height: 5), + _LanguageOption( + label: 'Nederlands', // Dutch + localeCode: 'nl', + onSelected: () => _setLocale(context, 'nl'), + ), + ], + ), + ), + ), + actions: [ + KyberButton( + text: AppLocalizations.of(context)!.cancel, + onPressed: () => Navigator.of(context).pop(), + ), + ], + ), + ); + } + + void _setLocale(BuildContext context, String code) { + AppLocale.setLocale(Locale(code)); + Navigator.of(context).pop(); + } + + // Helper method to get the display name for the button + String _getCurrentLanguageName(String code) { + switch (code) { + case 'en': return 'ENGLISH'; + case 'de': return 'DEUTSCH'; + case 'fr': return 'FRANÇAIS'; + case 'es': return 'ESPAÑOL'; + case 'pl': return 'POLSKI'; + case 'ru': return 'РУССКИЙ'; + case 'pt': return 'PORTUGUÊS'; + case 'uk': return 'УКРАЇНСЬКА'; + case 'sv': return 'SVENSKA'; + case 'nl': return 'NEDERLANDS'; + default: return 'ENGLISH'; + } + } + @override Widget build(BuildContext context) { + final l10n = AppLocalizations.of(context)!; + final currentCode = AppLocale.getLocale().languageCode; + return SuperListView( padding: const EdgeInsets.only(bottom: 15), children: [ - const Row( + Row( children: [ - SettingsHeader(title: 'ACCESSIBILITY'), + SettingsHeader(title: l10n.accessibility), ], ), const CardSection(), const SizedBox(height: 15), - const Padding( - padding: EdgeInsets.symmetric(horizontal: 21), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 21), child: Text( - 'COLORBLIND PROFILES', - style: TextStyle( + l10n.colorblindProfiles, + style: const TextStyle( color: kWhiteColor, fontFamily: FontFamily.battlefrontUI, fontSize: 15, @@ -50,19 +164,19 @@ class LanguageAndAccessibility extends StatelessWidget { spacing: 30, children: [ _ColorOption( - title: 'DEFAULT', + title: l10n.defaultColor, color: kDefaultActiveColor.withValues(), ), _ColorOption( - title: 'PROTANOMALY', + title: l10n.protanomaly, color: kProtanopia.withValues(), ), _ColorOption( - title: 'DEUTERANOMALY', + title: l10n.deuteranomaly, color: kDeuteranopia.withValues(), ), _ColorOption( - title: 'TRITANOMALY', + title: l10n.tritanomaly, color: kTritanopia.withValues(), ), ], @@ -71,23 +185,29 @@ class LanguageAndAccessibility extends StatelessWidget { ), const SizedBox(height: 15), HiveListener( - keys: const ['rememberWindowPosition'], + keys: const ['rememberWindowPosition', 'locale'], box: box, builder: (context) { return BlocBuilder( builder: (context, state) { return KyberTable( items: [ + // Language selection entry KyberTableItem.button( - title: 'RESET SETTINGS', + title: l10n.language, + onClick: () => _showLanguageDialog(context), + text: _getCurrentLanguageName(currentCode), + ), + KyberTableItem.button( + title: l10n.resetSettings, onClick: () => showKyberDialog( context: context, builder: (_) => const SettingsResetDialog(), ), - text: 'Reset', + text: l10n.reset, ), KyberTableItem.switchButton( - title: 'Remember Window Position', + title: l10n.rememberWindowPosition, value: Preferences.customization.rememberWindowPosition, onChange: (value) async { Preferences.customization.rememberWindowPosition = @@ -109,7 +229,7 @@ class LanguageAndAccessibility extends StatelessWidget { spacing: 15, children: [ Text( - 'CUSTOMIZATION', + l10n.customization, style: FluentTheme.of(context).typography.title!.copyWith( fontWeight: FontWeight.bold, color: kInactiveColor, @@ -128,7 +248,7 @@ class LanguageAndAccessibility extends StatelessWidget { ), ), child: Text( - 'PATREON EXCLUSIVE', + l10n.patreonExclusive, style: TextStyle( fontFamily: FontFamily.battlefrontUI, color: kActiveColor, @@ -151,12 +271,13 @@ class LanguageAndAccessibility extends StatelessWidget { 'disableHeadless', 'dummyServer', 'apiEnv', + 'activeColor', ], builder: (_) => KyberTable( items: [ KyberTableItem.button( - title: 'CHANGE HIGHLIGHT COLOR', - text: 'Change', + title: l10n.changeHighlightColor, + text: l10n.change, onClick: !context.read().state.canUsePerks() ? null : () async { @@ -167,7 +288,7 @@ class LanguageAndAccessibility extends StatelessWidget { maxWidth: 700, maxHeight: 600, ), - title: const Text('CHANGE COLOR'), + title: Text(l10n.changeColor), content: SingleChildScrollView( child: ColorPicker( isAlphaEnabled: false, @@ -183,11 +304,11 @@ class LanguageAndAccessibility extends StatelessWidget { ), actions: [ KyberButton( - text: 'CANCEL', + text: l10n.cancel, onPressed: () => Navigator.of(context).pop(), ), KyberButton( - text: 'Reset', + text: l10n.reset, onPressed: () { kActiveColor = const Color(0xFFfab20a); Preferences.customization.activeColor = @@ -196,7 +317,7 @@ class LanguageAndAccessibility extends StatelessWidget { }, ), KyberButton( - text: 'Save', + text: l10n.save, onPressed: () { Navigator.of(context).pop(); }, @@ -207,8 +328,8 @@ class LanguageAndAccessibility extends StatelessWidget { }, ), KyberTableItem.button( - title: 'CHANGE BACKGROUND', - text: 'Change', + title: l10n.changeBackground, + text: l10n.change, onClick: !context.read().state.canUsePerks() ? null : () { @@ -223,6 +344,61 @@ class LanguageAndAccessibility extends StatelessWidget { } } +// NOTE: Custom widget for Language Menu items +class _LanguageOption extends StatelessWidget { + const _LanguageOption({ + required this.label, + required this.localeCode, + required this.onSelected, + }); + + final String label; + final String localeCode; + final VoidCallback onSelected; + + @override + Widget build(BuildContext context) { + final currentLocale = AppLocale.getLocale().languageCode; + final isSelected = currentLocale == localeCode; + + return ButtonBuilder( + onClick: onSelected, + builder: (context, hovered) { + return Container( + width: double.infinity, + padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 10), + decoration: BoxDecoration( + color: hovered ? kActiveColor.withOpacity(0.1) : Colors.transparent, + border: Border.all( + color: isSelected ? kActiveColor : Colors.transparent, + ), + borderRadius: BorderRadius.circular(4), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + label, + style: TextStyle( + color: kWhiteColor, + fontWeight: isSelected ? FontWeight.bold : FontWeight.normal, + // NOTE: Use specific font for the language name preview + // English gets Battlefront, everyone else gets IBM Plex Mono for safety + fontFamily: localeCode == 'en' + ? FontFamily.battlefrontUI + : FontFamily.iBMPlexMono, + ), + ), + if (isSelected) + Icon(FluentIcons.check_mark, color: kActiveColor, size: 16), + ], + ), + ); + }, + ); + } +} + class _ColorOption extends StatelessWidget { const _ColorOption({required this.title, required this.color, super.key}); @@ -272,4 +448,4 @@ class _ColorOption extends StatelessWidget { }, ); } -} +} \ No newline at end of file diff --git a/Launcher/lib/features/settings/screens/pages/logs_and_activity.dart b/Launcher/lib/features/settings/screens/pages/logs_and_activity.dart index 36bee63b..aedef89e 100644 --- a/Launcher/lib/features/settings/screens/pages/logs_and_activity.dart +++ b/Launcher/lib/features/settings/screens/pages/logs_and_activity.dart @@ -7,6 +7,7 @@ import 'package:kyber_launcher/core/services/rich_presence.dart'; import 'package:kyber_launcher/core/utils/custom_logger.dart'; import 'package:kyber_launcher/features/settings/dialogs/debug_logging_warning_dialog.dart'; import 'package:kyber_launcher/gen/fonts.gen.dart'; +import 'package:kyber_launcher/gen/l10n/app_localizations.dart'; import 'package:kyber_launcher/injection_container.dart'; import 'package:kyber_launcher/main.dart'; import 'package:kyber_launcher/shared/ui/ui.dart'; @@ -20,6 +21,8 @@ class LogsAndActivity extends StatelessWidget { @override Widget build(BuildContext context) { + final l10n = AppLocalizations.of(context)!; + return HiveListener( box: box, keys: const ['discordRPC'], @@ -28,7 +31,7 @@ class LogsAndActivity extends StatelessWidget { Padding( padding: const EdgeInsets.all(8).copyWith(left: 20), child: Text( - 'ACTIVITY'.toUpperCase(), + l10n.activity.toUpperCase(), style: FluentTheme.of(context).typography.title!.copyWith( fontWeight: FontWeight.bold, color: kInactiveColor, @@ -39,7 +42,7 @@ class LogsAndActivity extends StatelessWidget { KyberTable( items: [ KyberTableItem.switchButton( - title: 'Discord Rich Presence', + title: l10n.discordRichPresence, value: Preferences.general.discordRPC, onChange: (value) async { Preferences.general.discordRPC = value; @@ -54,7 +57,7 @@ class LogsAndActivity extends StatelessWidget { Padding( padding: const EdgeInsets.all(8).copyWith(left: 20), child: Text( - 'Logging & Sentry'.toUpperCase(), + l10n.loggingAndSentry.toUpperCase(), style: FluentTheme.of(context).typography.title!.copyWith( fontWeight: FontWeight.bold, color: kInactiveColor, @@ -73,18 +76,19 @@ class LogsAndActivity extends StatelessWidget { builder: (_) => KyberTable( items: [ KyberTableItem.button( - title: 'Logs', - text: 'Export Logs', + title: l10n.logs, + text: l10n.exportLogs, onClick: () async { await CustomLogger.requestLogExport(); }, ), KyberTableItem.button( - title: - "Opt ${Preferences.general.sentryOptedOut ? "In To" : "Out Of"} Sentry", + title: Preferences.general.sentryOptedOut + ? l10n.optInToSentry + : l10n.optOutOfSentry, text: Preferences.general.sentryOptedOut - ? 'Opt In' - : 'Opt Out', + ? l10n.optIn + : l10n.optOut, onClick: () async { Preferences.general.sentryOptedOut = !Preferences.general.sentryOptedOut; @@ -97,7 +101,7 @@ class LogsAndActivity extends StatelessWidget { }, ), KyberTableItem.switchButton( - title: 'Launcher Debug Mode', + title: l10n.launcherDebugMode, value: Preferences.debug.frbDebugLogs, onChange: (value) async { if (value) { @@ -129,7 +133,7 @@ class LogsAndActivity extends StatelessWidget { }, ), KyberTableItem.switchButton( - title: 'Module Debug Mode', + title: l10n.moduleDebugMode, value: Preferences.debug.moduleDebugLogs, onChange: (value) async { if (value) { @@ -146,7 +150,7 @@ class LogsAndActivity extends StatelessWidget { }, ), KyberTableItem.switchButton( - title: 'Module RPC Debug Mode', + title: l10n.moduleRpcDebugMode, value: Preferences.debug.grpcDebugLogs, onChange: (value) async { if (value) { @@ -169,4 +173,4 @@ class LogsAndActivity extends StatelessWidget { ), ); } -} +} \ No newline at end of file diff --git a/Launcher/lib/features/settings/screens/pages/mod_support.dart b/Launcher/lib/features/settings/screens/pages/mod_support.dart index 5cb58f31..2ffa3005 100644 --- a/Launcher/lib/features/settings/screens/pages/mod_support.dart +++ b/Launcher/lib/features/settings/screens/pages/mod_support.dart @@ -3,6 +3,7 @@ import 'package:kyber_launcher/core/core.dart'; import 'package:kyber_launcher/features/frosty/dialogs/frosty_import_dialog.dart'; import 'package:kyber_launcher/features/mods/dialogs/move_directory_dialog.dart'; import 'package:kyber_launcher/features/settings/screens/settings.dart'; +import 'package:kyber_launcher/gen/l10n/app_localizations.dart'; import 'package:kyber_launcher/main.dart'; import 'package:kyber_launcher/shared/ui/ui.dart'; import 'package:super_sliver_list/super_sliver_list.dart'; @@ -12,32 +13,34 @@ class ModSupport extends StatelessWidget { @override Widget build(BuildContext context) { + final l10n = AppLocalizations.of(context)!; + return SuperListView( children: [ - const SettingsHeader(title: 'MODS'), + SettingsHeader(title: l10n.mods), HiveListener( box: box, - keys: ['enabledPreloadMods'], + keys: const ['enabledPreloadMods'], builder: (_) => KyberTable( items: [ KyberTableItem.button( - title: 'Change Mod Directory', - text: 'New Directory', + title: l10n.changeModDirectory, + text: l10n.newDirectory, onClick: () => showKyberDialog( builder: (_) => const MoveModsDirectoryDialog(), context: context, ), ), KyberTableItem.button( - title: 'Frosty Converter', - text: 'Convert your Packs', + title: l10n.frostyConverter, + text: l10n.convertYourPacks, onClick: () => showKyberDialog( builder: (_) => const FrostyImportDialog(), context: context, ), ), KyberTableItem.switchButton( - title: 'Kyber Preloaded Mods', + title: l10n.kyberPreloadedMods, value: Preferences.general.enabledPreloadMods, onChange: (bool value) { Preferences.general.enabledPreloadMods = value; @@ -49,4 +52,4 @@ class ModSupport extends StatelessWidget { ], ); } -} +} \ No newline at end of file diff --git a/Launcher/lib/features/settings/screens/pages/proximity_chat.dart b/Launcher/lib/features/settings/screens/pages/proximity_chat.dart index f7bca759..15733310 100644 --- a/Launcher/lib/features/settings/screens/pages/proximity_chat.dart +++ b/Launcher/lib/features/settings/screens/pages/proximity_chat.dart @@ -5,6 +5,7 @@ import 'package:kyber_launcher/core/services/voip_service.dart'; import 'package:kyber_launcher/features/maxima/models/maxima_game_instance.dart'; import 'package:kyber_launcher/features/settings/screens/settings.dart'; import 'package:kyber_launcher/features/settings/widgets/voip_key_picker.dart'; +import 'package:kyber_launcher/gen/l10n/app_localizations.dart'; import 'package:kyber_launcher/injection_container.dart'; import 'package:kyber_launcher/main.dart'; import 'package:kyber_launcher/shared/ui/ui.dart'; @@ -15,9 +16,11 @@ class ProximityChat extends StatelessWidget { @override Widget build(BuildContext context) { + final l10n = AppLocalizations.of(context)!; + return SuperListView( children: [ - const SettingsHeader(title: 'INGAME'), + SettingsHeader(title: l10n.ingame), HiveListener( box: box, keys: const ['ingameHotkeyEnabled'], @@ -25,24 +28,24 @@ class ProximityChat extends StatelessWidget { return KyberTable( items: [ KyberTableItem.switchButton( - title: 'Ingame Hotkey (Moderation Menu)', + title: l10n.ingameHotkey, value: Preferences.general.ingameHotkeyEnabled, onChange: (value) async { Preferences.general.ingameHotkeyEnabled = value; if (sl.isRegistered()) { NotificationService.showNotification( - message: 'To apply changes, restart the game', + message: l10n.restartGameWarning, ); } }, - enabledText: 'Enabled', - disabledText: 'Disabled', + enabledText: l10n.enabled, + disabledText: l10n.disabled, ), ], ); }, ), - const SettingsHeader(title: 'Proximity Chat'), + SettingsHeader(title: l10n.proximityChat), ListenableBuilder( listenable: sl.get(), builder: (_, __) { @@ -51,20 +54,20 @@ class ProximityChat extends StatelessWidget { final child = KyberTable( items: [ KyberTableItem.switchButton( - title: 'Proximity Chat', + title: l10n.proximityChat, onChange: (value) => service.setVoiceChat(enabled: value), value: service.isEnabled, ), KyberTableItem.switchButton( - title: 'Input Mode', + title: l10n.inputMode, onChange: (value) => service.setPushToTalk(enabled: value), value: service.isPushToTalkEnabled, - disabledText: 'Open Mic', - enabledText: 'Push to Talk', + disabledText: l10n.openMic, + enabledText: l10n.pushToTalk, ), if (service.isPushToTalkEnabled) KyberTableItem.custom( - title: 'Push to Talk Key', + title: l10n.pushToTalkKey, builder: (context) { return CharKeyPicker( value: VoipKeyResponse( @@ -76,7 +79,7 @@ class ProximityChat extends StatelessWidget { }, ), KyberTableItem.slider( - title: 'Input Volume', + title: l10n.inputVolume, value: Preferences.general.defaultInputVolume, onChanged: (value) async { Preferences.general.defaultInputVolume = value; @@ -86,7 +89,7 @@ class ProximityChat extends StatelessWidget { max: 100, ), KyberTableItem.slider( - title: 'Output Volume', + title: l10n.outputVolume, value: Preferences.general.defaultOutputVolume, onChanged: (value) async { Preferences.general.defaultOutputVolume = value; @@ -96,11 +99,11 @@ class ProximityChat extends StatelessWidget { max: 100, ), KyberTableItem.selector( - title: 'Input Device', + title: l10n.inputDevice, items: service.inputDevices.isEmpty ? [ - const KyberSelectorItem( - title: 'No devices found', + KyberSelectorItem( + title: l10n.noDevicesFound, value: '', ), ] @@ -122,11 +125,11 @@ class ProximityChat extends StatelessWidget { }, ), KyberTableItem.selector( - title: 'Output Device', + title: l10n.outputDevice, items: service.outputDevices.isEmpty ? [ - const KyberSelectorItem( - title: 'No devices found', + KyberSelectorItem( + title: l10n.noDevicesFound, value: '', ), ] @@ -154,4 +157,4 @@ class ProximityChat extends StatelessWidget { ], ); } -} +} \ No newline at end of file diff --git a/Launcher/lib/features/settings/screens/settings_list.dart b/Launcher/lib/features/settings/screens/settings_list.dart index 9c7da390..578e4bc2 100644 --- a/Launcher/lib/features/settings/screens/settings_list.dart +++ b/Launcher/lib/features/settings/screens/settings_list.dart @@ -9,6 +9,7 @@ import 'package:kyber_launcher/features/settings/screens/pages/mod_support.dart' import 'package:kyber_launcher/features/settings/screens/pages/proximity_chat.dart'; import 'package:kyber_launcher/gen/assets.gen.dart'; import 'package:kyber_launcher/gen/fonts.gen.dart'; +import 'package:kyber_launcher/gen/l10n/app_localizations.dart'; import 'package:kyber_launcher/shared/ui/ui.dart'; import 'package:package_info_plus/package_info_plus.dart'; @@ -34,41 +35,45 @@ class _SettingsListState extends State { super.initState(); } - final items = >[ - { - 'title': 'LANGUAGE & ACCESSIBILITY', - 'description': 'CHANGE LANGUAGE, PROXY & ACCESSIBILITY SETTINGS', - 'child': const LanguageAndAccessibility(), - }, - { - 'title': 'MOD CONFIGURATION', - 'description': 'CONFIGURE MODS SETTINGS, IMPORT FROM FROSTY & MORE', - 'child': const ModSupport(), - }, - { - 'title': 'CREDITS LIST', - 'description': 'VIEW DEVELOPERS, CONTRIBUTORS & PATREON SUPPORTERS', - 'child': const Credits(), - }, - { - 'title': 'INGAME SETTINGS', - 'description': 'Configure Proximity Chat settings'.toUpperCase(), - 'child': const ProximityChat(), - }, - { - 'title': 'LOGS & ACTIVITY', - 'description': 'VIEW ACTIVITY & DEBUG LOGGING SETTINGS', - 'child': const LogsAndActivity(), - }, - { - 'title': 'ACCOUNTS & UPDATES', - 'description': 'LOGOUT, UPDATE SETTINGS & MORE', - 'child': const AccountsAndUpdates(), - }, - ]; - @override Widget build(BuildContext context) { + final l10n = AppLocalizations.of(context)!; + final isEn = Localizations.localeOf(context).languageCode == 'en'; + final currentFont = isEn ? FontFamily.battlefrontUI : 'BattlefrontGlobal'; + + final items = >[ + { + 'title': l10n.langAccTitle, + 'description': l10n.langAccDesc, + 'child': const LanguageAndAccessibility(), + }, + { + 'title': l10n.modConfigTitle, + 'description': l10n.modConfigDesc, + 'child': const ModSupport(), + }, + { + 'title': l10n.creditsTitle, + 'description': l10n.creditsDesc, + 'child': const Credits(), + }, + { + 'title': l10n.ingameSettingsTitle, + 'description': l10n.proximityChatDesc, + 'child': const ProximityChat(), + }, + { + 'title': l10n.logsActivityTitle, + 'description': l10n.logsActivityDesc, + 'child': const LogsAndActivity(), + }, + { + 'title': l10n.accountsUpdatesTitle, + 'description': l10n.accountsUpdatesDesc, + 'child': const AccountsAndUpdates(), + }, + ]; + if (selectedIndex != null) { return _SettingsSubPage( title: items.elementAt(selectedIndex!)['title'] as String, @@ -128,6 +133,7 @@ class _SettingsListState extends State { as String, index: i * horizontalLength + j, hoveredRow: hoveredRow, + font: currentFont, ), ), ), @@ -164,7 +170,7 @@ class _SettingsListState extends State { } return Text( - 'VERSION: ${snapshot.data?.version}#CL${snapshot.data?.buildNumber}', + l10n.versionText(snapshot.data!.version, snapshot.data!.buildNumber), style: const TextStyle( fontFamily: FontFamily.iBMPlexMono, fontSize: 13, @@ -196,6 +202,9 @@ class _SettingsSubPage extends StatelessWidget { @override Widget build(BuildContext context) { + final isEn = Localizations.localeOf(context).languageCode == 'en'; + final currentFont = isEn ? FontFamily.battlefrontUI : 'BattlefrontGlobal'; + return Row( children: [ ConstrainedBox( @@ -231,18 +240,18 @@ class _SettingsSubPage extends StatelessWidget { children: [ Text( title, - style: const TextStyle( - fontFamily: FontFamily.battlefrontUI, + style: TextStyle( + fontFamily: currentFont, fontSize: 20, height: 1, ), ), Text( description, - style: const TextStyle( + style: TextStyle( fontSize: 15, color: kWhiteColor, - fontFamily: FontFamily.battlefrontUI, + fontFamily: currentFont, height: 1, ), ), @@ -384,6 +393,7 @@ class _SettingsContainer extends StatelessWidget { required this.item, required this.index, required this.hoveredRow, + required this.font, }); final int horizontalIndex; @@ -392,6 +402,7 @@ class _SettingsContainer extends StatelessWidget { final int index; final int hoveredRow; final String item; + final String font; @override Widget build(BuildContext context) { @@ -467,10 +478,10 @@ class _SettingsContainer extends StatelessWidget { ), child: Text( item, - style: const TextStyle( + style: TextStyle( fontSize: 20, fontWeight: FontWeight.bold, - fontFamily: FontFamily.battlefrontUI, + fontFamily: font, ), ), ), @@ -495,4 +506,4 @@ class _SettingsContainer extends StatelessWidget { ), ); } -} +} \ No newline at end of file diff --git a/Launcher/lib/features/setup/screens/walk_through_setup.dart b/Launcher/lib/features/setup/screens/walk_through_setup.dart index 56f32ba2..ab938f9c 100644 --- a/Launcher/lib/features/setup/screens/walk_through_setup.dart +++ b/Launcher/lib/features/setup/screens/walk_through_setup.dart @@ -20,6 +20,7 @@ import 'package:kyber_launcher/features/navigation_bar/widgets/title_bar.dart' a import 'package:kyber_launcher/features/setup/widgets/setup_container.dart'; import 'package:kyber_launcher/gen/assets.gen.dart'; import 'package:kyber_launcher/gen/fonts.gen.dart'; +import 'package:kyber_launcher/gen/l10n/app_localizations.dart'; import 'package:kyber_launcher/injection_container.dart'; import 'package:kyber_launcher/shared/ui/buttons/button.dart'; import 'package:kyber_launcher/shared/ui/utils/background_blur.dart'; @@ -54,6 +55,8 @@ class _WalkThroughSetupState extends State { @override Widget build(BuildContext context) { + final l10n = AppLocalizations.of(context)!; + return NavigationView( key: const Key('navigation_view'), titleBar: const SizedBox( @@ -77,8 +80,9 @@ class _WalkThroughSetupState extends State { ): () { Preferences.debug.frbDebugLogs = !Preferences.debug.frbDebugLogs; NotificationService.info( - message: - '${Preferences.debug.frbDebugLogs ? 'Enabled' : 'Disabled'} debug logs', + message: l10n.debugLogsToggled( + Preferences.debug.frbDebugLogs ? l10n.enabled : l10n.disabled, + ), ); }, }, @@ -142,7 +146,7 @@ class _WalkThroughSetupState extends State { children: [ ProgressItem( iconPath: Assets.logos.eaPlay.path, - text: 'EA Account', + text: l10n.eaAccount, done: setupPage > 0, active: setupPage == 0, ), @@ -156,7 +160,7 @@ class _WalkThroughSetupState extends State { const SizedBox(width: 10), ProgressItem( iconPath: Assets.logos.nexusMods.path, - text: 'Nexus Mods', + text: l10n.nexusMods, done: setupPage > 1, active: setupPage == 1, ), @@ -164,7 +168,7 @@ class _WalkThroughSetupState extends State { ), const SizedBox(width: 60), KyberButton( - text: setupPage == 1 ? 'FINISH' : 'SKIP', + text: setupPage == 1 ? l10n.finish : l10n.skip, icon: const Icon(FluentIcons.game), onPressed: _finishSetup, ), @@ -217,6 +221,9 @@ class ProgressItem extends StatelessWidget { @override Widget build(BuildContext context) { + final isEn = Localizations.localeOf(context).languageCode == 'en'; + final currentFont = isEn ? FontFamily.battlefrontUI : 'BattlefrontGlobal'; + final color = active ? kWhiteColor : done @@ -253,7 +260,7 @@ class ProgressItem extends StatelessWidget { Text( text.toUpperCase(), style: TextStyle( - fontFamily: FontFamily.battlefrontUI, + fontFamily: currentFont, color: color, ), textAlign: TextAlign.center, @@ -292,4 +299,4 @@ class ProgressItem extends StatelessWidget { ), ); } -} +} \ No newline at end of file diff --git a/Launcher/lib/features/social/screens/social_home.dart b/Launcher/lib/features/social/screens/social_home.dart index a01f4c3d..c76d9f11 100644 --- a/Launcher/lib/features/social/screens/social_home.dart +++ b/Launcher/lib/features/social/screens/social_home.dart @@ -13,6 +13,7 @@ import 'package:kyber_launcher/features/reports/dialogs/report_player_dialog.dar import 'package:kyber_launcher/features/server_browser/widgets/server_list/server_list_header.dart'; import 'package:kyber_launcher/gen/assets.gen.dart'; import 'package:kyber_launcher/gen/fonts.gen.dart'; +import 'package:kyber_launcher/gen/l10n/app_localizations.dart'; import 'package:kyber_launcher/gen/rust/api/maxima.dart'; import 'package:kyber_launcher/injection_container.dart'; import 'package:kyber_launcher/shared/ui/buttons/custom_icon_button.dart'; @@ -39,6 +40,9 @@ class _SocialHomeState extends State { _searchQuery.debounceTime(const Duration(milliseconds: 100)).listen(( query, ) async { + if (!mounted) return; + final l10n = AppLocalizations.of(context)!; + if (query.isEmpty) { setState(() { searchResults = null; @@ -58,10 +62,10 @@ class _SocialHomeState extends State { }); } on GrpcError catch (e) { NotificationService.error( - message: 'Failed to search players: ${e.message}', + message: l10n.searchFailed(e.message ?? ''), ); } catch (e) { - NotificationService.error(message: 'An unexpected error occurred: $e'); + NotificationService.error(message: l10n.unexpectedError(e.toString())); } }); super.initState(); @@ -75,6 +79,10 @@ class _SocialHomeState extends State { @override Widget build(BuildContext context) { + final l10n = AppLocalizations.of(context)!; + final isEn = Localizations.localeOf(context).languageCode == 'en'; + final currentFont = isEn ? FontFamily.battlefrontUI : 'BattlefrontGlobal'; + return Row( spacing: 15, children: [ @@ -126,15 +134,15 @@ class _SocialHomeState extends State { .state .servicePlayer! .displayName, - style: const TextStyle( + style: TextStyle( fontSize: 18, - fontFamily: FontFamily.battlefrontUI, + fontFamily: currentFont, fontWeight: FontWeight.bold, ), ), Expanded( child: KyberInput( - placeholder: 'Search for a kyber user', + placeholder: l10n.searchKyberUser, onChanged: _searchQuery.add, ), ), @@ -177,7 +185,7 @@ class _SocialHomeState extends State { child: AnimatedDefaultTextStyle( duration: const Duration(milliseconds: 150), style: TextStyle( - fontFamily: FontFamily.battlefrontUI, + fontFamily: currentFont, color: hovered ? kActiveColor : kWhiteColor, ), child: Container( @@ -190,10 +198,9 @@ class _SocialHomeState extends State { Expanded( child: Text( searchResults![index].name, - style: const TextStyle( + style: TextStyle( fontSize: 15, - fontFamily: - FontFamily.battlefrontUI, + fontFamily: currentFont, ), ), ), @@ -226,10 +233,10 @@ class _SocialHomeState extends State { Padding( padding: const EdgeInsets.all(20), child: Text( - 'No results found for "${_searchQuery.value}".', - style: const TextStyle( + l10n.noResultsFound(_searchQuery.value), + style: TextStyle( fontSize: 16, - fontFamily: FontFamily.battlefrontUI, + fontFamily: currentFont, color: kInactiveColor, ), ), @@ -243,8 +250,9 @@ class _SocialHomeState extends State { _Expandable( initialExpanded: true, logo: Assets.logos.eaPlay.svg(), - title: - 'FRIENDS (${context.read().state.friends.length})', + title: l10n.friendsCount( + context.read().state.friends.length, + ), players: context .read() .state @@ -258,7 +266,7 @@ class _SocialHomeState extends State { ), ), ), - const SizedBox( + SizedBox( width: 400, child: KyberCard( padding: EdgeInsets.zero, @@ -267,7 +275,7 @@ class _SocialHomeState extends State { SizedBox( height: 65, child: Padding( - padding: EdgeInsets.symmetric(vertical: 14, horizontal: 14), + padding: const EdgeInsets.symmetric(vertical: 14, horizontal: 14), child: Align( alignment: Alignment.centerLeft, child: SizedBox( @@ -276,19 +284,19 @@ class _SocialHomeState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - 'RECENTLY VIEWED', + l10n.recentlyViewed, style: TextStyle( fontSize: 20, - fontFamily: FontFamily.battlefrontUI, + fontFamily: currentFont, fontWeight: FontWeight.bold, height: 1, ), ), Text( - 'USER SEARCH HISTORY', + l10n.userSearchHistory, style: TextStyle( fontSize: 15, - fontFamily: FontFamily.battlefrontUI, + fontFamily: currentFont, color: kInactiveColor, height: 1, ), @@ -299,7 +307,7 @@ class _SocialHomeState extends State { ), ), ), - CardSection(), + const CardSection(), ], ), ), @@ -337,6 +345,10 @@ class _ExpandableState extends State<_Expandable> { @override Widget build(BuildContext context) { + final l10n = AppLocalizations.of(context)!; + final isEn = Localizations.localeOf(context).languageCode == 'en'; + final currentFont = isEn ? FontFamily.battlefrontUI : 'BattlefrontGlobal'; + return Column( children: [ ColoredBox( @@ -383,8 +395,8 @@ class _ExpandableState extends State<_Expandable> { ), child: Text( widget.title.toUpperCase(), - style: const TextStyle( - fontFamily: FontFamily.battlefrontUI, + style: TextStyle( + fontFamily: currentFont, fontSize: 15, ), ), @@ -429,12 +441,12 @@ class _ExpandableState extends State<_Expandable> { isOnline = true; } - var text = ''; + var statusText = ''; if (isOnline) { if (presence!.status.isEmpty) { - text = 'Online'; + statusText = l10n.online; } else { - text = 'Playing ${presence.status}'; + statusText = l10n.playingStatus(presence.status); } } @@ -447,7 +459,7 @@ class _ExpandableState extends State<_Expandable> { child: AnimatedDefaultTextStyle( duration: const Duration(milliseconds: 150), style: TextStyle( - fontFamily: FontFamily.battlefrontUI, + fontFamily: currentFont, color: hovered ? kActiveColor : kWhiteColor, ), child: SizedBox( @@ -468,7 +480,7 @@ class _ExpandableState extends State<_Expandable> { player.displayName, style: TextStyle( fontSize: 15, - fontFamily: FontFamily.battlefrontUI, + fontFamily: currentFont, color: presence?.basic == BasicPresence.offline || @@ -479,7 +491,7 @@ class _ExpandableState extends State<_Expandable> { ), ), const SizedBox(width: 10), - Text(text), + Text(statusText), ], ), ), @@ -498,4 +510,4 @@ class _ExpandableState extends State<_Expandable> { ], ); } -} +} \ No newline at end of file diff --git a/Launcher/lib/features/stats/screens/personal/stats_overview.dart b/Launcher/lib/features/stats/screens/personal/stats_overview.dart index e3aadd40..4d37b7e6 100644 --- a/Launcher/lib/features/stats/screens/personal/stats_overview.dart +++ b/Launcher/lib/features/stats/screens/personal/stats_overview.dart @@ -11,6 +11,7 @@ import 'package:kyber_launcher/features/stats/models/stats_object.dart'; import 'package:kyber_launcher/features/stats/providers/stats_cubit.dart'; import 'package:kyber_launcher/gen/assets.gen.dart'; import 'package:kyber_launcher/gen/fonts.gen.dart'; +import 'package:kyber_launcher/gen/l10n/app_localizations.dart'; import 'package:kyber_launcher/shared/ui/cards/kyber_container.dart'; import 'package:kyber_launcher/shared/ui/elements/kyber_tab_bar.dart'; import 'package:kyber_launcher/shared/ui/primitives/rank_icon.dart'; @@ -22,19 +23,23 @@ class UserStats extends StatefulWidget { State createState() => _UserStatsState(); } -String formatPlaytime(Duration duration) { +String formatPlaytime(Duration duration, AppLocalizations l10n) { if (duration.inHours > 0) { - return '${NumberFormat.decimalPattern().format(duration.inHours)} HRS'; + return l10n.hoursFormat(NumberFormat.decimalPattern().format(duration.inHours)); } else if (duration.inMinutes > 0) { - return '${duration.inMinutes} MIN'; + return l10n.minutesFormat(duration.inMinutes); } else { - return '${duration.inSeconds} SEC'; + return l10n.secondsFormat(duration.inSeconds); } } class _UserStatsState extends State { @override Widget build(BuildContext context) { + final l10n = AppLocalizations.of(context)!; + final isEn = Localizations.localeOf(context).languageCode == 'en'; + final currentFont = isEn ? FontFamily.battlefrontUI : 'BattlefrontGlobal'; + return Column( children: [ Expanded( @@ -49,7 +54,7 @@ class _UserStatsState extends State { } if (state.playerStats == null) { - return const Center(child: Text('No stats found')); + return Center(child: Text(l10n.noStatsFound)); } return Padding( @@ -60,17 +65,17 @@ class _UserStatsState extends State { Expanded( flex: 5, child: KyberCard( - padding: .zero, + padding: EdgeInsets.zero, child: Column( - crossAxisAlignment: .stretch, + crossAxisAlignment: CrossAxisAlignment.stretch, children: [ SizedBox( height: 61, child: Padding( - padding: const .all(8), + padding: const EdgeInsets.all(8), child: Row( spacing: 5, - mainAxisAlignment: .spaceBetween, + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Expanded( child: Row( @@ -80,12 +85,12 @@ class _UserStatsState extends State { height: 45, decoration: BoxDecoration( border: kDefaultAllBorder, - borderRadius: .circular( + borderRadius: BorderRadius.circular( kDefaultInnerBorderRadius, ), ), child: ClipRRect( - borderRadius: .circular( + borderRadius: BorderRadius.circular( kDefaultInnerBorderRadius - 2, ), child: CachedNetworkImage( @@ -96,14 +101,14 @@ class _UserStatsState extends State { .avatar! .large .path, - fadeInDuration: .zero, + fadeInDuration: Duration.zero, ), ), ), Flexible( child: Column( - crossAxisAlignment: .start, - mainAxisAlignment: .center, + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.center, children: [ Text( context @@ -111,19 +116,17 @@ class _UserStatsState extends State { .state .servicePlayer! .displayName, - style: const TextStyle( + style: TextStyle( fontSize: 18, - fontFamily: FontFamily - .battlefrontUI, + fontFamily: currentFont, height: 1.1, ), ), AutoSizeText( - '${formatPlaytime(state.playerStats!.totalPlaytime)}', - style: const TextStyle( + formatPlaytime(state.playerStats!.totalPlaytime, l10n), + style: TextStyle( height: 1.1, - fontFamily: FontFamily - .battlefrontUI, + fontFamily: currentFont, color: kWhiteColor1, ), maxLines: 1, @@ -256,11 +259,10 @@ class _UserStatsState extends State { height: 50, child: Column( children: [ - const Text( - 'MOST PLAYED', + Text( + l10n.mostPlayed, style: TextStyle( - fontFamily: FontFamily - .battlefrontUI, + fontFamily: currentFont, color: kWhiteColor1, fontSize: 15, height: 1.1, @@ -268,9 +270,8 @@ class _UserStatsState extends State { ), Text( char.name.toUpperCase(), - style: const TextStyle( - fontFamily: FontFamily - .battlefrontUI, + style: TextStyle( + fontFamily: currentFont, fontSize: 15, ), ), @@ -293,34 +294,34 @@ class _UserStatsState extends State { child: Builder( builder: (context) { final stats = { - 'TOTAL KILLS': + l10n.totalKillsLabel: NumberFormat.decimalPattern().format( state.playerStats!.totalKills, ), - 'TOTAL DEATHS': + l10n.totalDeathsLabel: NumberFormat.decimalPattern().format( state.playerStats!.totalDeaths, ), - 'K/D RATIO': state.playerStats! + l10n.kdRatioLabel: state.playerStats! .getKd() .toStringAsFixed(2), - 'ASSISTS': NumberFormat.decimalPattern() + l10n.assistsLabel: NumberFormat.decimalPattern() .format( state.playerStats!.eliminations, ), - 'DAMAGE DONE': + l10n.damageDoneLabel: NumberFormat.decimalPattern().format( state.playerStats!.totalScore, ), - 'SUICIDES': NumberFormat.decimalPattern() + l10n.suicidesLabel: NumberFormat.decimalPattern() .format(state.playerStats!.suicides), - 'GAMES WON': NumberFormat.decimalPattern() + l10n.gamesWonLabel: NumberFormat.decimalPattern() .format(state.playerStats!.totalWins), - 'GAMES LOST': + l10n.gamesLostLabel: NumberFormat.decimalPattern().format( state.playerStats!.totalLosses, ), - 'WIN RATE': + l10n.winRateLabel: '${state.playerStats!.getWinRate().toStringAsFixed(1)}%', }; return StaggeredGrid.count( @@ -364,17 +365,17 @@ class _UserStatsState extends State { ), children: [ _StatSection( - title: 'UNITS', + title: l10n.unitsSection, data: state.playerStats!.unitStats.values .toList(), ), _StatSection( - title: 'VEHICLES', + title: l10n.vehiclesSection, data: state.playerStats!.vehicleStats.values .toList(), ), _StatSection( - title: 'STARFIGHTER', + title: l10n.starfighterSection, data: state .playerStats! .starFighterStats @@ -410,6 +411,10 @@ class _StatSection extends StatelessWidget { @override Widget build(BuildContext context) { + final l10n = AppLocalizations.of(context)!; + final isEn = Localizations.localeOf(context).languageCode == 'en'; + final currentFont = isEn ? FontFamily.battlefrontUI : 'BattlefrontGlobal'; + return Container( height: 240, margin: const EdgeInsets.symmetric(vertical: 10), @@ -431,8 +436,8 @@ class _StatSection extends StatelessWidget { ), Text( title, - style: const TextStyle( - fontFamily: FontFamily.battlefrontUI, + style: TextStyle( + fontFamily: currentFont, color: kWhiteColor, fontSize: 18, ), @@ -455,7 +460,7 @@ class _StatSection extends StatelessWidget { name: data[i].name, portrait: data[i].portrait, rank: data[i].rank, - timePlayed: formatPlaytime(data[i].timePlayed), + timePlayed: formatPlaytime(data[i].timePlayed, l10n), ), ), ], @@ -482,6 +487,9 @@ class _ClassContainer extends StatelessWidget { @override Widget build(BuildContext context) { + final isEn = Localizations.localeOf(context).languageCode == 'en'; + final currentFont = isEn ? FontFamily.battlefrontUI : 'BattlefrontGlobal'; + const containerHeight = 287; const containerWidth = 220; const aspectRatio = containerWidth / containerHeight; @@ -548,17 +556,17 @@ class _ClassContainer extends StatelessWidget { children: [ Text( name.toUpperCase(), - style: const TextStyle( + style: TextStyle( fontSize: 15, - fontFamily: FontFamily.battlefrontUI, + fontFamily: currentFont, ), ), Text( timePlayed, - style: const TextStyle( + style: TextStyle( fontSize: 15, color: kGrayColor, - fontFamily: FontFamily.battlefrontUI, + fontFamily: currentFont, ), ), ], @@ -583,22 +591,25 @@ class _Stat extends StatelessWidget { @override Widget build(BuildContext context) { + final isEn = Localizations.localeOf(context).languageCode == 'en'; + final currentFont = isEn ? FontFamily.battlefrontUI : 'BattlefrontGlobal'; + return Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( title, - style: const TextStyle( - fontFamily: FontFamily.battlefrontUI, + style: TextStyle( + fontFamily: currentFont, color: kWhiteColor1, fontSize: 17, ), ), Text( value, - style: const TextStyle( - fontFamily: FontFamily.battlefrontUI, + style: TextStyle( + fontFamily: currentFont, color: kWhiteColor, fontSize: 17, ), @@ -606,4 +617,4 @@ class _Stat extends StatelessWidget { ], ); } -} +} \ No newline at end of file diff --git a/Launcher/lib/features/stats/widgets/stats_player_search.dart b/Launcher/lib/features/stats/widgets/stats_player_search.dart index 61886154..9437ea92 100644 --- a/Launcher/lib/features/stats/widgets/stats_player_search.dart +++ b/Launcher/lib/features/stats/widgets/stats_player_search.dart @@ -7,6 +7,7 @@ import 'package:kyber_launcher/core/config/colors.dart'; import 'package:kyber_launcher/features/stats/providers/stats_cubit.dart'; import 'package:kyber_launcher/features/stats/providers/stats_search_cubit.dart'; import 'package:kyber_launcher/gen/fonts.gen.dart'; +import 'package:kyber_launcher/gen/l10n/app_localizations.dart'; import 'package:kyber_launcher/shared/ui/elements/kyber_input.dart'; import 'package:kyber_launcher/shared/ui/elements/list/kyber_list.dart'; import 'package:kyber_launcher/shared/ui/utils/background_blur.dart'; @@ -32,6 +33,8 @@ class StatsPlayerSearchState extends State { @override Widget build(BuildContext context) { + final l10n = AppLocalizations.of(context)!; + return BlocListener( listener: (context, state) { if (state is! SearchLoaded && @@ -98,7 +101,7 @@ class StatsPlayerSearchState extends State { color: kInactiveColor, size: 19, ), - placeholder: 'Search...', + placeholder: l10n.searchPlaceholder, controller: textController, onChanged: (value) => context.read().search(value), @@ -122,6 +125,10 @@ class MenuWidget extends StatelessWidget { @override Widget build(BuildContext context) { + final l10n = AppLocalizations.of(context)!; + final isEn = Localizations.localeOf(context).languageCode == 'en'; + final currentFont = isEn ? FontFamily.battlefrontUI : 'BattlefrontGlobal'; + return Padding( padding: const EdgeInsets.only(top: 3), child: BackgroundBlur( @@ -157,8 +164,8 @@ class MenuWidget extends StatelessWidget { return Center( child: Text( state.error, - style: const TextStyle( - fontFamily: FontFamily.battlefrontUI, + style: TextStyle( + fontFamily: currentFont, fontSize: 18, color: kWhiteColor, ), @@ -173,9 +180,9 @@ class MenuWidget extends StatelessWidget { if (state.result.isEmpty) { return Center( child: Text( - 'No players found'.toUpperCase(), - style: const TextStyle( - fontFamily: FontFamily.battlefrontUI, + l10n.noPlayersFound.toUpperCase(), + style: TextStyle( + fontFamily: currentFont, fontSize: 18, color: kWhiteColor, ), @@ -211,8 +218,8 @@ class MenuWidget extends StatelessWidget { const SizedBox(width: 6), Text( mod.username, - style: const TextStyle( - fontFamily: FontFamily.battlefrontUI, + style: TextStyle( + fontFamily: currentFont, fontSize: 18, height: 1, ), @@ -239,4 +246,4 @@ class MenuWidget extends StatelessWidget { ), ); } -} +} \ No newline at end of file diff --git a/Launcher/lib/l10n/app_de.arb b/Launcher/lib/l10n/app_de.arb new file mode 100644 index 00000000..1536fce6 --- /dev/null +++ b/Launcher/lib/l10n/app_de.arb @@ -0,0 +1,643 @@ +{ + "language": "Language", + "accessibility": "ACCESSIBILITY", + "colorblindProfiles": "COLORBLIND PROFILES", + "defaultColor": "DEFAULT", + "protanomaly": "PROTANOMALY", + "deuteranomaly": "DEUTERANOMALY", + "tritanomaly": "TRITANOMALY", + "resetSettings": "RESET SETTINGS", + "reset": "Reset", + "rememberWindowPosition": "Remember Window Position", + "customization": "CUSTOMIZATION", + "patreonExclusive": "PATREON EXCLUSIVE", + "changeHighlightColor": "CHANGE HIGHLIGHT COLOR", + "change": "Change", + "changeBackground": "CHANGE BACKGROUND", + "save": "Save", + "cancel": "Cancel", + "changeColor": "CHANGE COLOR", + "accounts": "ACCOUNTS", + "proxy": "PROXY", + "unavailable": "UNAVAILABLE", + "resetKyberToken": "Reset Kyber Token", + "nexusMods": "NexusMods", + "logout": "Logout", + "login": "Login", + "eaLogout": "EA Logout", + "linkedAccounts": "LINKED ACCOUNTS", + "discord": "Discord", + "connect": "Connect", + "discordUnlink": "{name} | Unlink", + "connectPatreon": "Connect Patreon", + "updates": "UPDATES", + "automaticallyUpdate": "Automatically Update", + "releaseChannel": "Release Channel", + "select": "Select", + "forceUpdate": "Force update", + "updateNow": "UPDATE NOW", + "other": "OTHER", + "licenses": "Licenses", + "showLicenses": "Show Licenses", + "developerMode": "Developer Mode", + "environment": "Environment", + "apiEnvironment": "API Environment", + "setNexusApiToken": "Set Nexus API Token", + "setToken": "Set token", + "setBackgroundImage": "Set Background Image", + "setImage": "Set Image", + "dummyServer": "Dummy Server", + "removeBackground": "Remove Background", + "copyKyberToken": "Copy Kyber Token", + "copy": "Copy", + "activity": "ACTIVITY", + "discordRichPresence": "Discord Rich Presence", + "loggingAndSentry": "Logging & Sentry", + "logs": "Logs", + "exportLogs": "Export Logs", + "optInToSentry": "Opt In To Sentry", + "optOutOfSentry": "Opt Out Of Sentry", + "optIn": "Opt In", + "optOut": "Opt Out", + "launcherDebugMode": "Launcher Debug Mode", + "moduleDebugMode": "Module Debug Mode", + "moduleRpcDebugMode": "Module RPC Debug Mode", + "mods": "MODS", + "changeModDirectory": "Change Mod Directory", + "newDirectory": "New Directory", + "frostyConverter": "Frosty Converter", + "convertYourPacks": "Convert your Packs", + "kyberPreloadedMods": "Kyber Preloaded Mods", + "ingame": "INGAME", + "ingameHotkey": "Ingame Hotkey (Moderation Menu)", + "restartGameWarning": "To apply changes, restart the game", + "enabled": "Enabled", + "disabled": "Disabled", + "proximityChat": "Proximity Chat", + "inputMode": "Input Mode", + "openMic": "Open Mic", + "pushToTalk": "Push to Talk", + "pushToTalkKey": "Push to Talk Key", + "inputVolume": "Input Volume", + "outputVolume": "Output Volume", + "inputDevice": "Input Device", + "outputDevice": "Output Device", + "noDevicesFound": "No devices found", + "selectBackground": "SELECT A BACKGROUND", + "chooseBackgroundDesc": "CHOOSE FROM 8 BACKGROUNDS OR SELECT A CUSTOM BACKGROUND", + "customBackground": "CUSTOM BACKGROUND", + "selectImageDesc": "Select a background image for the launcher", + "minResolutionDesc": "The image resolution should be at least 1920x1080", + "selectImage": "Select Image", + "fileTooLarge": "The file is too large. Please select a file smaller than 10MB", + "resolutionTooLow": "The image resolution is too low. Please select an image with a resolution of at least 1920x1080", + "copyingImage": "Copying image...", + "selectImagePickerTitle": "Select a background image", + "langAccTitle": "LANGUAGE & ACCESSIBILITY", + "langAccDesc": "CHANGE LANGUAGE, PROXY & ACCESSIBILITY SETTINGS", + "modConfigTitle": "MOD CONFIGURATION", + "modConfigDesc": "CONFIGURE MODS SETTINGS, IMPORT FROM FROSTY & MORE", + "creditsTitle": "CREDITS LIST", + "creditsDesc": "VIEW DEVELOPERS, CONTRIBUTORS & PATREON SUPPORTERS", + "ingameSettingsTitle": "INGAME SETTINGS", + "proximityChatDesc": "CONFIGURE PROXIMITY CHAT SETTINGS", + "logsActivityTitle": "LOGS & ACTIVITY", + "logsActivityDesc": "VIEW ACTIVITY & DEBUG LOGGING SETTINGS", + "accountsUpdatesTitle": "ACCOUNTS & UPDATES", + "accountsUpdatesDesc": "LOGOUT, UPDATE SETTINGS & MORE", + "versionText": "VERSION: {version}#CL{build}", + "downloadManager": "DOWNLOAD MANAGER", + "pausedDownloads": "PAUSED DOWNLOADS", + "unCapSpeedPrefix": "UN-CAP DOWNLOAD SPEEDS WITH ", + "nexusModsPremium": "NEXUS MODS PREMIUM", + "downloadProgress": " / {total} ({speed}/s) {percentage}%", + "@downloadProgress": { + "placeholders": { + "total": { "type": "String" }, + "speed": { "type": "String" }, + "percentage": { "type": "int" } + } + }, + "loadMods": "LOAD MODS", + "export": "EXPORT", + "selectMods": "Select mods", + "ignoringDuplicate": "Ignoring {filename}. (Duplicate)", + "@ignoringDuplicate": { + "placeholders": { + "filename": { "type": "String" } + } + }, + "saveCollection": "Save collection", + "icon": "ICON", + "name": "Name", + "author": "Author", + "version": "Version", + "category": "Category", + "exportCollection": "EXPORT COLLECTION", + "exportingCollection": "Exporting collection...", + "selectIcon": "Select icon", + "eaLogin": "EA Login", + "loading": "Loading...", + "waitingForResponse": "Waiting for response...", + "maximaStarting": "Maxima is starting...", + "fetchingData": "Fetching data...", + "genericError": "An error occurred", + "loginFailed": "Login failed", + "userDoesNotExist": "The specified user does not exist", + "passwordIncorrect": "The specified password is incorrect", + "failedToFindAuthCode": "Failed to find auth code. Please try another browser", + "pathCopied": "The path has been copied to your clipboard", + "noAuthCodeReceived": "No authorization code was received", + "authorizationSuccessful": "Authorization successful", + "patreonAuthorizedMessage": "You have been successfully authorized as a Patreon member", + "authorizationFailed": "Authorization failed", + "maximaIntroText": "In order to use this launcher, you need to login to Maxima. This is required to launch and interact with Battlefront 2.\nYou will be redirected to the EA login page and after logging in, you will be redirected back to the launcher.", + "loginWithEa": "Login with EA", + "eaIdDisplay": "EA-ID: {eaId}", + "logout": "Log out", + "authorizePatreon": "Authorize Patreon", + "whitelistConfirmation": "Are you sure you want to add your current account to the whitelist?", + "currentAccountDisplay": "Current account: {displayName}", + "addToWhitelist": "Add to whitelist", + "gameNotOwnedMessage": "It seems like you do not own Battlefront 2. Please purchase the game or use another account.", + "maximaBackgroundServiceError": "Maxima failed to start the background service. This is usually caused by an antivirus program blocking the service. Please add an exception for the launcher and reinstall it.", + "maximaMissingFilesError": "Some files required to run Maxima are missing. This is usually caused by an antivirus program deleting the files. Please add an exception for the launcher and reinstall it.", + "antivirusExclusionInstruction": "Please exclude the following folders from your antivirus' real-time protection:", + "copyPath": "COPY PATH", + "debugLogsToggled": "{status} debug logs", + "signInNexusPrompt": "Please sign in to Nexus Mods to view mods", + "noModsFound": "No mods found", + "unknownError": "Unknown error", + "noDataFound": "No data found", + "modInformation": "MOD INFORMATION", + "viewModFilesShareEndorse": "VIEW MOD FILES, SHARE AND ENDORSE", + "files": "FILES", + "posts": "POSTS", + "endorseMustDownload": "You must download the mod before endorsing it", + "endorseWaitTime": "You must wait some time after downloading the mod before endorsing it", + "endorseLoginRequired": "You must be logged in to endorse mods", + "linkCopied": "Link copied to clipboard", + "updatedAt": "Updated: ", + "mainFiles": "MAIN FILES", + "miscellaneousFiles": "MISCELLANEOUS FILES", + "optionalFiles": "OPTIONAL FILES", + "archivedFiles": "ARCHIVED FILES", + "fileListTitle": "{title} ({count})", + "@fileListTitle": { + "placeholders": { + "title": { "type": "String" }, + "count": { "type": "int" } + } + }, + "verifiedModAuthor": "Verified Mod Author", + "viewingProfile": "VIEWING PROFILE", + "publishedMods": "PUBLISHED MODS", + "userInformation": "USER INFORMATION", + "viewUserStats": "VIEW USER STATS", + "joinedLabel": "Joined: ", + "modsLabel": "Mods: ", + "postsLabel": "Posts: ", + "uniqueDlsLabel": "Unique-DLs: ", + "viewsLabel": "Views: ", + "kudosLabel": "Kudos: ", + "contributedLabel": "Contributed: ", + "viewOnNexusMods": "VIEW ON NEXUS MODS", + "collectionMods": "Collection Mods", + "modNameVersion": "{name} ({version})", + "@modNameVersion": { + "placeholders": { + "name": { "type": "String" }, + "version": { "type": "String" } + } + }, + "downloadCollectionMods": "DOWNLOAD COLLECTION MODS", + "loadingMods": "LOADING MODS...", + "collections": "COLLECTIONS", + "manageCollectionsSub": "MANAGE MOD COLLECTIONS & CURATE COSMETIC MODS", + "browser": "Browser", + "paginationInfo": "{page}/{totalPages}", + "@paginationInfo": { + "placeholders": { + "page": { "type": "int" }, + "totalPages": { "type": "int" } + } + }, + "modScope": "MOD SCOPE", + "serverNotFound": "The server you were playing on could not be found.", + "nexusModsAuthorization": "NexusMods Authorization", + "nexusAuthDescription": "You need to authorize KyberLauncher on NexusMods to use the mod browser.", + "authorizeKyber": "Authorize Kyber", + "noReportsAvailable": "No reports available", + "reportedBy": "Reported by ", + "forLabel": " for ", + "onLabel": " on ", + "descriptionLabel": "Description", + "evidenceCountLabel": "Evidence ({count})", + "@evidenceCountLabel": { + "placeholders": { + "count": { "type": "int" } + } + }, + "reportViewTitle": "REPORT VIEW", + "viewReportsAndEvidence": "VIEW REPORTS AND EVIDENCE", + "reportsTab": "REPORTS", + "punishmentsTab": "PUNISHMENTS", + "reportRejected": "Report rejected", + "takeAction": "TAKE ACTION", + "reportedByPlayer": "By {name}", + "@reportedByPlayer": { + "placeholders": { + "name": { "type": "String" } + } + }, + "daysDuration": "{days} Days", + "@daysDuration": { + "placeholders": { + "days": { "type": "int" } + } + }, + "permanent": "Permanent", + "bannedByModerator": "Banned By {name}", + "@bannedByModerator": { + "placeholders": { + "name": { "type": "String" } + } + }, + "durationLabel": "Duration: {duration}", + "@durationLabel": { + "placeholders": { + "duration": { "type": "String" } + } + }, + "issuedLabel": "Issued: {date}", + "@issuedLabel": { + "placeholders": { + "date": { "type": "String" } + } + }, + "statusFilter": "STATUS", + "reportsTitle": "REPORTS", + "totalReportsHeader": "TOTAL REPORTS", + "latestReportHeader": "LATEST REPORT", + "latestReasonHeader": "LATEST REASON", + "statusHeader": "STATUS", + "errorLoadingReports": "Error loading reports: {message}", + "reasonWithCount": "{reasonName} ({count})", + "armsecCaseFile": "ARMSEC - CASE FILE", + "evidenceAndDataSummary": "EVIDENCE AND DATA SUMMARY", + "errorLoadingUser": "Error loading user", + "userNotFound": "User not found", + "kyberUser": "KYBER USER", + "open": "OPEN", + "options": "OPTIONS", + "@errorLoadingReports": { + "placeholders": { + "message": { "type": "String" } + } + }, + "@reasonWithCount": { + "placeholders": { + "reasonName": { "type": "String" }, + "count": { "type": "String" } + } + }, + "unknownServer": "Unknown Server", + "hostedBy": "Hosted by {creator}", + "@hostedBy": { + "placeholders": { + "creator": { "type": "String" } + } + }, + "backToServerBrowser": "Back to Server Browser", + "eventLog": "Event Log", + "lightSide": "Light Side", + "darkSide": "Dark Side", + "reportPlayer": "Report Player", + "region": "REGION", + "serverType": "SERVER TYPE", + "gameType": "GAME TYPE", + "gameMode": "GAME MODE", + "warning": "WARNING", + "warningMessageFallback": "Warning message not available.", + "newButton": "NEW", + "moderate": "MODERATE", + "manage": "MANAGE", + "rotation": "ROTATION", + "searchPlaceholder": "Search ...", + "noServersFound": "No servers found", + "serverBrowser": "Server Browser", + "optionsHeader": "OPTIONS", + "moderatorsHeader": "MODERATORS", + "bannedPlayersHeader": "BANNED PLAYERS", + "playButton": "PLAY", + "spectateButton": "SPECTATE", + "copyLinkButton": "COPY LINK", + "copiedToClipboard": "Copied to clipboard!", + "exportBans": "EXPORT BANS", + "importBans": "IMPORT BANS", + "kickAll": "KICK ALL", + "banAll": "BAN ALL", + "eventLogHeader": "Event Log", + "lightSideHeader": "Light Side", + "darkSideHeader": "Dark Side", + "userManager": "User Manager", + "consoleHint": "/COMMAND OR SEND MESSAGE", + "untilLabel": "UNTIL: {date}", + "reasonLabel": "REASON: {reason}", + "unban": "UNBAN", + "modify": "MODIFY", + "playerUnbanned": "Player unbanned", + "timeElapsed": "Time has elapsed", + "daysRemaining": "{count} day(s)", + "hoursRemaining": "{count} hour(s)", + "minutesRemaining": "{count} minute(s)", + "secondsRemaining": "{count} second(s)", + "remaining": "{time} remaining", + "promoteUserTooltip": "Promote User", + "errorOccurred": "An error occurred", + "swapPlayerTooltip": "Swap Player", + "kickPlayerTooltip": "Kick player", + "banPlayerTooltip": "Ban player", + "off": "OFF", + "on": "ON", + "home": "HOME", + "host": "HOST", + "stats": "STATS", + "settings": "SETTINGS", + "ingamePanel": "INGAME PANEL", + "extractingFileWithProgress": "EXTRACTING FILE ({extracted}/{total})", + "extractingFile": "EXTRACTING FILE", + "downloadingWithProgress": "DOWNLOADING ({percentage}% {downloaded}/{total})", + "downloadManager": "DOWNLOAD MANAGER", + "createNewCollection": "CREATE NEW COLLECTION", + "pageNotFoundTitle": "Page Not Found", + "pageNotFoundFallback": "page not found", + "goToHomePage": "Go to home page", + "newCollectionTitle": "New Collection", + "@extractingFileWithProgress": { + "placeholders": { + "extracted": { + "type": "String" + }, + "total": { + "type": "String" + } + } + }, + "@downloadingWithProgress": { + "placeholders": { + "percentage": { + "type": "String" + }, + "downloaded": { + "type": "String" + }, + "total": { + "type": "String" + } + } + }, + "@untilLabel": { + "placeholders": { + "date": { "type": "String" } + } + }, + "@reasonLabel": { + "placeholders": { + "reason": { "type": "String" } + } + }, + "@daysRemaining": { + "placeholders": { + "count": { "type": "int" } + } + }, + "@hoursRemaining": { + "placeholders": { + "count": { "type": "int" } + } + }, + "@minutesRemaining": { + "placeholders": { + "count": { "type": "int" } + } + }, + "@secondsRemaining": { + "placeholders": { + "count": { "type": "int" } + } + }, + "@remaining": { + "placeholders": { + "time": { "type": "String" } + } + }, + "eaAccount": "EA Account", + "finish": "FINISH", + "skip": "SKIP", + "searchFailed": "Failed to search players: {message}", + "unexpectedError": "An unexpected error occurred: {error}", + "searchKyberUser": "Search for a kyber user", + "noResultsFound": "No results found for \"{query}\".", + "friendsCount": "FRIENDS ({count})", + "recentlyViewed": "RECENTLY VIEWED", + "userSearchHistory": "USER SEARCH HISTORY", + "online": "Online", + "playingStatus": "Playing {status}", + "@searchFailed": { + "placeholders": { + "message": { "type": "String" } + } + }, + "@unexpectedError": { + "placeholders": { + "error": { "type": "String" } + } + }, + "@noResultsFound": { + "placeholders": { + "query": { "type": "String" } + } + }, + "@friendsCount": { + "placeholders": { + "count": { "type": "int" } + } + }, + "@playingStatus": { + "placeholders": { + "status": { "type": "String" } + } + }, + "selectCollectionIcon": "Select Collection Icon", + "collectionLabel": "COLLECTION", + "collectionDescription": "A COLLECTION IS A READY TO PLAY MOD LIST", + "modsCount": "{count} Mods", + "collectionNamePlaceholder": "Collection Name", + "saveButton": "SAVE", + "betterSabersPlugin": "BETTER SABERS (PLUGIN)", + "reloadingMods": "Reloading mods", + "generatedBetterSabers": "Generated BetterSabers", + "exportCollectionTar": "EXPORT COLLECTION TAR", + "editCollection": "EDIT COLLECTION", + "createACopy": "CREATE A COPY", + "collectionCopyName": "{title} (Copy)", + "deleteCollection": "DELETE COLLECTION", + "largeModsWarning": "Using multiple large or complex mods isn't recommended, as they often conflict with each other.", + "@modsCount": { + "placeholders": { + "count": { + "type": "int" + } + } + }, + "@collectionCopyName": { + "placeholders": { + "title": { + "type": "String" + } + } + }, + "myMods": "MY MODS", + "nameHeader": "NAME", + "categoryHeader": "CATEGORY", + "sizeHeader": "SIZE", + "typeHeader": "TYPE", + "addHeader": "ADD", + "selectedCount": "{count}", + "@selectedCount": { + "placeholders": { + "count": { + "type": "int" + } + } + }, + "all": "ALL", + "searchHint": "SEARCH...", + "noPlayersFound": "No players found", + "moveModsDirectory": "MOVE MODS DIRECTORY", + "invalidDirectoryWarning": "The current mods directory is invalid because it contains non-ASCII characters. ", + "selectNewDirectoryDesc": "Please select the new directory where you want to move the mods directory. Mods will not be moved. Only the directory will be changed.", + "nonAsciiWarning": "Non-ASCII characters in the path are not allowed (e.g. Korean, Greek, etc.).", + "adminPermissionError": "You can't select a directory that requires admin permissions. Please select another directory.", + "selectedDirNonAsciiError": "The selected directory contains non-ASCII characters. Please select a different directory.", + "browse": "Browse", + "resetToDefault": "Reset to default", + "defaultDirNonAsciiError": "The default mods directory contains non-ASCII characters. Please select a different directory.", + "modInformation": "MOD INFORMATION", + "viewInformationAboutMod": "VIEW INFORMATION ABOUT A MOD", + "modVersionAuthorDisplay": "{version} - {author}", + "@modVersionAuthorDisplay": { + "placeholders": { + "version": { + "type": "String" + }, + "author": { + "type": "String" + } + } + }, + "descriptionLabel": "DESCRIPTION", + "imagesLabel": "IMAGES", + "affectedFilesLabel": "AFFECTED FILES", + "unknownResourceName": "Unknown resource name", + "abortingNexusLogin": "Aborting NexusMods login", + "installWebViewError": "Please install WebView to use this feature.", + "continueText": "CONTINUE", + "wait": "WAIT", + "win7CompatibilityDetected": "Windows 7 Compatibility Mode detected", + "disableWin7CompMode": "To login to NexusMods, please disable Windows 7 Compatibility Mode for Kyber Launcher.", + "reEnableWin7CompMode": "After you have logged in, you need to re-enable Windows 7 Compatibility Mode.", + "checkingWebView": "Checking WebView installation...", + "nexusLoginIntro": "To continue, you will need to log in with your NexusMods account in the browser that is about to open.", + "nexusLoginDataNotice": "The data is processed locally and will only be sent to Nexusmods.\nYou can also enable/disable this feature later in the settings menu.", + "playersHeader": "PLAYERS", + "eventsAndAnnouncements": "EVENTS & ANNOUNCEMENTS", + "viewUpcomingEventsDesc": "VIEW UPCOMING EVENTS & RECEIVE ANNOUNCEMENTS", + "sortByTitle": "SORT BY", + "timeFilterTitle": "TIME FILTER", + "itemsPerPageTitle": "ITEMS PER PAGE", + "allTime": "All time", + "oneYear": "1 year", + "oneMonth": "1 month", + "twoWeeks": "2 weeks", + "oneWeek": "1 week", + "oneDay": "1 day", + "all": "ALL", + "@hoursFormat": { + "placeholders": { + "count": { + "type": "String" + } + } + }, + "@minutesFormat": { + "placeholders": { + "count": { + "type": "int" + } + } + }, + "@secondsFormat": { + "placeholders": { + "count": { + "type": "int" + } + } + }, + "hoursFormat": "{count} HRS", + "minutesFormat": "{count} MIN", + "secondsFormat": "{count} SEC", + "noStatsFound": "No stats found", + "mostPlayed": "MOST PLAYED", + "totalKillsLabel": "TOTAL KILLS", + "totalDeathsLabel": "TOTAL DEATHS", + "kdRatioLabel": "K/D RATIO", + "assistsLabel": "ASSISTS", + "damageDoneLabel": "DAMAGE DONE", + "suicidesLabel": "SUICIDES", + "gamesWonLabel": "GAMES WON", + "gamesLostLabel": "GAMES LOST", + "winRateLabel": "WIN RATE", + "unitsSection": "UNITS", + "vehiclesSection": "VEHICLES", + "starfighterSection": "STARFIGHTER", + "serverSectionTitle": "SERVER", + "maxPlayersLabel": "MAX PLAYERS", + "maxSpectatorsLabel": "Max Spectators", + "privacySectionTitle": "Privacy", + "passwordLabel": "Password", + "passwordPlaceholder": "EXAMPLE", + "modeHeroesVersusVillains": "Heroes Versus Villains", + "modeGalacticAssault": "Galactic Assault", + "modeSupremacy": "Supremacy", + "modeCoOpAttack": "CO-OP Attack", + "modeCoOpDefend": "CO-OP Defend", + "modeStrike": "Strike", + "modeExtraction": "Extraction", + "modeBlast": "Blast", + "modeEwokHunt": "Ewok Hunt", + "modeJetpackCargo": "Jetpack Cargo", + "modeStarfighterAssault": "Starfighter Assault", + "modeHeroStarfighters": "Hero Starfighters", + "modeHeroShowdown": "Hero Showdown", + "mapTatooineMosEisley": "Tatooine - Mos Eisley", + "mapTatooineJabbasPalace": "Tatooine - Jabba's Palace", + "mapRepublicVenator": "Republic Venator", + "mapSeparatistDreadnought": "Separatist Dreadnought", + "mapResurgentStarDestroyer": "Resurgent Star Destroyer", + "mapMc85StarCruiser": "MC85 Star Cruiser", + "mapNabooPalaceHangar": "Naboo - Palace Hangar", + "mapEndorResearchStation9": "Endor - Research Station 9", + "mapEndorEwokVillage": "Endor - Ewok Village", + "mapNabooTheedPalace": "Naboo - Theed Palace", + "mapCrait": "Crait", + "activeRotation": "Active Rotation", + "importLabel": "Import", + "importMapRotation": "Import Map Rotation", + "invalidFileFormat": "Invalid file format", + "mapRotationImported": "Map rotation imported", + "noMapsActive": "No maps active", + "customMapNotFoundTooltip": "Custom map not found, this can happen if the map is not part of the current mod collection.\nThis map will be ignored when starting the server.", + "notFound": "Not found" +} \ No newline at end of file diff --git a/Launcher/lib/l10n/app_en.arb b/Launcher/lib/l10n/app_en.arb new file mode 100644 index 00000000..1536fce6 --- /dev/null +++ b/Launcher/lib/l10n/app_en.arb @@ -0,0 +1,643 @@ +{ + "language": "Language", + "accessibility": "ACCESSIBILITY", + "colorblindProfiles": "COLORBLIND PROFILES", + "defaultColor": "DEFAULT", + "protanomaly": "PROTANOMALY", + "deuteranomaly": "DEUTERANOMALY", + "tritanomaly": "TRITANOMALY", + "resetSettings": "RESET SETTINGS", + "reset": "Reset", + "rememberWindowPosition": "Remember Window Position", + "customization": "CUSTOMIZATION", + "patreonExclusive": "PATREON EXCLUSIVE", + "changeHighlightColor": "CHANGE HIGHLIGHT COLOR", + "change": "Change", + "changeBackground": "CHANGE BACKGROUND", + "save": "Save", + "cancel": "Cancel", + "changeColor": "CHANGE COLOR", + "accounts": "ACCOUNTS", + "proxy": "PROXY", + "unavailable": "UNAVAILABLE", + "resetKyberToken": "Reset Kyber Token", + "nexusMods": "NexusMods", + "logout": "Logout", + "login": "Login", + "eaLogout": "EA Logout", + "linkedAccounts": "LINKED ACCOUNTS", + "discord": "Discord", + "connect": "Connect", + "discordUnlink": "{name} | Unlink", + "connectPatreon": "Connect Patreon", + "updates": "UPDATES", + "automaticallyUpdate": "Automatically Update", + "releaseChannel": "Release Channel", + "select": "Select", + "forceUpdate": "Force update", + "updateNow": "UPDATE NOW", + "other": "OTHER", + "licenses": "Licenses", + "showLicenses": "Show Licenses", + "developerMode": "Developer Mode", + "environment": "Environment", + "apiEnvironment": "API Environment", + "setNexusApiToken": "Set Nexus API Token", + "setToken": "Set token", + "setBackgroundImage": "Set Background Image", + "setImage": "Set Image", + "dummyServer": "Dummy Server", + "removeBackground": "Remove Background", + "copyKyberToken": "Copy Kyber Token", + "copy": "Copy", + "activity": "ACTIVITY", + "discordRichPresence": "Discord Rich Presence", + "loggingAndSentry": "Logging & Sentry", + "logs": "Logs", + "exportLogs": "Export Logs", + "optInToSentry": "Opt In To Sentry", + "optOutOfSentry": "Opt Out Of Sentry", + "optIn": "Opt In", + "optOut": "Opt Out", + "launcherDebugMode": "Launcher Debug Mode", + "moduleDebugMode": "Module Debug Mode", + "moduleRpcDebugMode": "Module RPC Debug Mode", + "mods": "MODS", + "changeModDirectory": "Change Mod Directory", + "newDirectory": "New Directory", + "frostyConverter": "Frosty Converter", + "convertYourPacks": "Convert your Packs", + "kyberPreloadedMods": "Kyber Preloaded Mods", + "ingame": "INGAME", + "ingameHotkey": "Ingame Hotkey (Moderation Menu)", + "restartGameWarning": "To apply changes, restart the game", + "enabled": "Enabled", + "disabled": "Disabled", + "proximityChat": "Proximity Chat", + "inputMode": "Input Mode", + "openMic": "Open Mic", + "pushToTalk": "Push to Talk", + "pushToTalkKey": "Push to Talk Key", + "inputVolume": "Input Volume", + "outputVolume": "Output Volume", + "inputDevice": "Input Device", + "outputDevice": "Output Device", + "noDevicesFound": "No devices found", + "selectBackground": "SELECT A BACKGROUND", + "chooseBackgroundDesc": "CHOOSE FROM 8 BACKGROUNDS OR SELECT A CUSTOM BACKGROUND", + "customBackground": "CUSTOM BACKGROUND", + "selectImageDesc": "Select a background image for the launcher", + "minResolutionDesc": "The image resolution should be at least 1920x1080", + "selectImage": "Select Image", + "fileTooLarge": "The file is too large. Please select a file smaller than 10MB", + "resolutionTooLow": "The image resolution is too low. Please select an image with a resolution of at least 1920x1080", + "copyingImage": "Copying image...", + "selectImagePickerTitle": "Select a background image", + "langAccTitle": "LANGUAGE & ACCESSIBILITY", + "langAccDesc": "CHANGE LANGUAGE, PROXY & ACCESSIBILITY SETTINGS", + "modConfigTitle": "MOD CONFIGURATION", + "modConfigDesc": "CONFIGURE MODS SETTINGS, IMPORT FROM FROSTY & MORE", + "creditsTitle": "CREDITS LIST", + "creditsDesc": "VIEW DEVELOPERS, CONTRIBUTORS & PATREON SUPPORTERS", + "ingameSettingsTitle": "INGAME SETTINGS", + "proximityChatDesc": "CONFIGURE PROXIMITY CHAT SETTINGS", + "logsActivityTitle": "LOGS & ACTIVITY", + "logsActivityDesc": "VIEW ACTIVITY & DEBUG LOGGING SETTINGS", + "accountsUpdatesTitle": "ACCOUNTS & UPDATES", + "accountsUpdatesDesc": "LOGOUT, UPDATE SETTINGS & MORE", + "versionText": "VERSION: {version}#CL{build}", + "downloadManager": "DOWNLOAD MANAGER", + "pausedDownloads": "PAUSED DOWNLOADS", + "unCapSpeedPrefix": "UN-CAP DOWNLOAD SPEEDS WITH ", + "nexusModsPremium": "NEXUS MODS PREMIUM", + "downloadProgress": " / {total} ({speed}/s) {percentage}%", + "@downloadProgress": { + "placeholders": { + "total": { "type": "String" }, + "speed": { "type": "String" }, + "percentage": { "type": "int" } + } + }, + "loadMods": "LOAD MODS", + "export": "EXPORT", + "selectMods": "Select mods", + "ignoringDuplicate": "Ignoring {filename}. (Duplicate)", + "@ignoringDuplicate": { + "placeholders": { + "filename": { "type": "String" } + } + }, + "saveCollection": "Save collection", + "icon": "ICON", + "name": "Name", + "author": "Author", + "version": "Version", + "category": "Category", + "exportCollection": "EXPORT COLLECTION", + "exportingCollection": "Exporting collection...", + "selectIcon": "Select icon", + "eaLogin": "EA Login", + "loading": "Loading...", + "waitingForResponse": "Waiting for response...", + "maximaStarting": "Maxima is starting...", + "fetchingData": "Fetching data...", + "genericError": "An error occurred", + "loginFailed": "Login failed", + "userDoesNotExist": "The specified user does not exist", + "passwordIncorrect": "The specified password is incorrect", + "failedToFindAuthCode": "Failed to find auth code. Please try another browser", + "pathCopied": "The path has been copied to your clipboard", + "noAuthCodeReceived": "No authorization code was received", + "authorizationSuccessful": "Authorization successful", + "patreonAuthorizedMessage": "You have been successfully authorized as a Patreon member", + "authorizationFailed": "Authorization failed", + "maximaIntroText": "In order to use this launcher, you need to login to Maxima. This is required to launch and interact with Battlefront 2.\nYou will be redirected to the EA login page and after logging in, you will be redirected back to the launcher.", + "loginWithEa": "Login with EA", + "eaIdDisplay": "EA-ID: {eaId}", + "logout": "Log out", + "authorizePatreon": "Authorize Patreon", + "whitelistConfirmation": "Are you sure you want to add your current account to the whitelist?", + "currentAccountDisplay": "Current account: {displayName}", + "addToWhitelist": "Add to whitelist", + "gameNotOwnedMessage": "It seems like you do not own Battlefront 2. Please purchase the game or use another account.", + "maximaBackgroundServiceError": "Maxima failed to start the background service. This is usually caused by an antivirus program blocking the service. Please add an exception for the launcher and reinstall it.", + "maximaMissingFilesError": "Some files required to run Maxima are missing. This is usually caused by an antivirus program deleting the files. Please add an exception for the launcher and reinstall it.", + "antivirusExclusionInstruction": "Please exclude the following folders from your antivirus' real-time protection:", + "copyPath": "COPY PATH", + "debugLogsToggled": "{status} debug logs", + "signInNexusPrompt": "Please sign in to Nexus Mods to view mods", + "noModsFound": "No mods found", + "unknownError": "Unknown error", + "noDataFound": "No data found", + "modInformation": "MOD INFORMATION", + "viewModFilesShareEndorse": "VIEW MOD FILES, SHARE AND ENDORSE", + "files": "FILES", + "posts": "POSTS", + "endorseMustDownload": "You must download the mod before endorsing it", + "endorseWaitTime": "You must wait some time after downloading the mod before endorsing it", + "endorseLoginRequired": "You must be logged in to endorse mods", + "linkCopied": "Link copied to clipboard", + "updatedAt": "Updated: ", + "mainFiles": "MAIN FILES", + "miscellaneousFiles": "MISCELLANEOUS FILES", + "optionalFiles": "OPTIONAL FILES", + "archivedFiles": "ARCHIVED FILES", + "fileListTitle": "{title} ({count})", + "@fileListTitle": { + "placeholders": { + "title": { "type": "String" }, + "count": { "type": "int" } + } + }, + "verifiedModAuthor": "Verified Mod Author", + "viewingProfile": "VIEWING PROFILE", + "publishedMods": "PUBLISHED MODS", + "userInformation": "USER INFORMATION", + "viewUserStats": "VIEW USER STATS", + "joinedLabel": "Joined: ", + "modsLabel": "Mods: ", + "postsLabel": "Posts: ", + "uniqueDlsLabel": "Unique-DLs: ", + "viewsLabel": "Views: ", + "kudosLabel": "Kudos: ", + "contributedLabel": "Contributed: ", + "viewOnNexusMods": "VIEW ON NEXUS MODS", + "collectionMods": "Collection Mods", + "modNameVersion": "{name} ({version})", + "@modNameVersion": { + "placeholders": { + "name": { "type": "String" }, + "version": { "type": "String" } + } + }, + "downloadCollectionMods": "DOWNLOAD COLLECTION MODS", + "loadingMods": "LOADING MODS...", + "collections": "COLLECTIONS", + "manageCollectionsSub": "MANAGE MOD COLLECTIONS & CURATE COSMETIC MODS", + "browser": "Browser", + "paginationInfo": "{page}/{totalPages}", + "@paginationInfo": { + "placeholders": { + "page": { "type": "int" }, + "totalPages": { "type": "int" } + } + }, + "modScope": "MOD SCOPE", + "serverNotFound": "The server you were playing on could not be found.", + "nexusModsAuthorization": "NexusMods Authorization", + "nexusAuthDescription": "You need to authorize KyberLauncher on NexusMods to use the mod browser.", + "authorizeKyber": "Authorize Kyber", + "noReportsAvailable": "No reports available", + "reportedBy": "Reported by ", + "forLabel": " for ", + "onLabel": " on ", + "descriptionLabel": "Description", + "evidenceCountLabel": "Evidence ({count})", + "@evidenceCountLabel": { + "placeholders": { + "count": { "type": "int" } + } + }, + "reportViewTitle": "REPORT VIEW", + "viewReportsAndEvidence": "VIEW REPORTS AND EVIDENCE", + "reportsTab": "REPORTS", + "punishmentsTab": "PUNISHMENTS", + "reportRejected": "Report rejected", + "takeAction": "TAKE ACTION", + "reportedByPlayer": "By {name}", + "@reportedByPlayer": { + "placeholders": { + "name": { "type": "String" } + } + }, + "daysDuration": "{days} Days", + "@daysDuration": { + "placeholders": { + "days": { "type": "int" } + } + }, + "permanent": "Permanent", + "bannedByModerator": "Banned By {name}", + "@bannedByModerator": { + "placeholders": { + "name": { "type": "String" } + } + }, + "durationLabel": "Duration: {duration}", + "@durationLabel": { + "placeholders": { + "duration": { "type": "String" } + } + }, + "issuedLabel": "Issued: {date}", + "@issuedLabel": { + "placeholders": { + "date": { "type": "String" } + } + }, + "statusFilter": "STATUS", + "reportsTitle": "REPORTS", + "totalReportsHeader": "TOTAL REPORTS", + "latestReportHeader": "LATEST REPORT", + "latestReasonHeader": "LATEST REASON", + "statusHeader": "STATUS", + "errorLoadingReports": "Error loading reports: {message}", + "reasonWithCount": "{reasonName} ({count})", + "armsecCaseFile": "ARMSEC - CASE FILE", + "evidenceAndDataSummary": "EVIDENCE AND DATA SUMMARY", + "errorLoadingUser": "Error loading user", + "userNotFound": "User not found", + "kyberUser": "KYBER USER", + "open": "OPEN", + "options": "OPTIONS", + "@errorLoadingReports": { + "placeholders": { + "message": { "type": "String" } + } + }, + "@reasonWithCount": { + "placeholders": { + "reasonName": { "type": "String" }, + "count": { "type": "String" } + } + }, + "unknownServer": "Unknown Server", + "hostedBy": "Hosted by {creator}", + "@hostedBy": { + "placeholders": { + "creator": { "type": "String" } + } + }, + "backToServerBrowser": "Back to Server Browser", + "eventLog": "Event Log", + "lightSide": "Light Side", + "darkSide": "Dark Side", + "reportPlayer": "Report Player", + "region": "REGION", + "serverType": "SERVER TYPE", + "gameType": "GAME TYPE", + "gameMode": "GAME MODE", + "warning": "WARNING", + "warningMessageFallback": "Warning message not available.", + "newButton": "NEW", + "moderate": "MODERATE", + "manage": "MANAGE", + "rotation": "ROTATION", + "searchPlaceholder": "Search ...", + "noServersFound": "No servers found", + "serverBrowser": "Server Browser", + "optionsHeader": "OPTIONS", + "moderatorsHeader": "MODERATORS", + "bannedPlayersHeader": "BANNED PLAYERS", + "playButton": "PLAY", + "spectateButton": "SPECTATE", + "copyLinkButton": "COPY LINK", + "copiedToClipboard": "Copied to clipboard!", + "exportBans": "EXPORT BANS", + "importBans": "IMPORT BANS", + "kickAll": "KICK ALL", + "banAll": "BAN ALL", + "eventLogHeader": "Event Log", + "lightSideHeader": "Light Side", + "darkSideHeader": "Dark Side", + "userManager": "User Manager", + "consoleHint": "/COMMAND OR SEND MESSAGE", + "untilLabel": "UNTIL: {date}", + "reasonLabel": "REASON: {reason}", + "unban": "UNBAN", + "modify": "MODIFY", + "playerUnbanned": "Player unbanned", + "timeElapsed": "Time has elapsed", + "daysRemaining": "{count} day(s)", + "hoursRemaining": "{count} hour(s)", + "minutesRemaining": "{count} minute(s)", + "secondsRemaining": "{count} second(s)", + "remaining": "{time} remaining", + "promoteUserTooltip": "Promote User", + "errorOccurred": "An error occurred", + "swapPlayerTooltip": "Swap Player", + "kickPlayerTooltip": "Kick player", + "banPlayerTooltip": "Ban player", + "off": "OFF", + "on": "ON", + "home": "HOME", + "host": "HOST", + "stats": "STATS", + "settings": "SETTINGS", + "ingamePanel": "INGAME PANEL", + "extractingFileWithProgress": "EXTRACTING FILE ({extracted}/{total})", + "extractingFile": "EXTRACTING FILE", + "downloadingWithProgress": "DOWNLOADING ({percentage}% {downloaded}/{total})", + "downloadManager": "DOWNLOAD MANAGER", + "createNewCollection": "CREATE NEW COLLECTION", + "pageNotFoundTitle": "Page Not Found", + "pageNotFoundFallback": "page not found", + "goToHomePage": "Go to home page", + "newCollectionTitle": "New Collection", + "@extractingFileWithProgress": { + "placeholders": { + "extracted": { + "type": "String" + }, + "total": { + "type": "String" + } + } + }, + "@downloadingWithProgress": { + "placeholders": { + "percentage": { + "type": "String" + }, + "downloaded": { + "type": "String" + }, + "total": { + "type": "String" + } + } + }, + "@untilLabel": { + "placeholders": { + "date": { "type": "String" } + } + }, + "@reasonLabel": { + "placeholders": { + "reason": { "type": "String" } + } + }, + "@daysRemaining": { + "placeholders": { + "count": { "type": "int" } + } + }, + "@hoursRemaining": { + "placeholders": { + "count": { "type": "int" } + } + }, + "@minutesRemaining": { + "placeholders": { + "count": { "type": "int" } + } + }, + "@secondsRemaining": { + "placeholders": { + "count": { "type": "int" } + } + }, + "@remaining": { + "placeholders": { + "time": { "type": "String" } + } + }, + "eaAccount": "EA Account", + "finish": "FINISH", + "skip": "SKIP", + "searchFailed": "Failed to search players: {message}", + "unexpectedError": "An unexpected error occurred: {error}", + "searchKyberUser": "Search for a kyber user", + "noResultsFound": "No results found for \"{query}\".", + "friendsCount": "FRIENDS ({count})", + "recentlyViewed": "RECENTLY VIEWED", + "userSearchHistory": "USER SEARCH HISTORY", + "online": "Online", + "playingStatus": "Playing {status}", + "@searchFailed": { + "placeholders": { + "message": { "type": "String" } + } + }, + "@unexpectedError": { + "placeholders": { + "error": { "type": "String" } + } + }, + "@noResultsFound": { + "placeholders": { + "query": { "type": "String" } + } + }, + "@friendsCount": { + "placeholders": { + "count": { "type": "int" } + } + }, + "@playingStatus": { + "placeholders": { + "status": { "type": "String" } + } + }, + "selectCollectionIcon": "Select Collection Icon", + "collectionLabel": "COLLECTION", + "collectionDescription": "A COLLECTION IS A READY TO PLAY MOD LIST", + "modsCount": "{count} Mods", + "collectionNamePlaceholder": "Collection Name", + "saveButton": "SAVE", + "betterSabersPlugin": "BETTER SABERS (PLUGIN)", + "reloadingMods": "Reloading mods", + "generatedBetterSabers": "Generated BetterSabers", + "exportCollectionTar": "EXPORT COLLECTION TAR", + "editCollection": "EDIT COLLECTION", + "createACopy": "CREATE A COPY", + "collectionCopyName": "{title} (Copy)", + "deleteCollection": "DELETE COLLECTION", + "largeModsWarning": "Using multiple large or complex mods isn't recommended, as they often conflict with each other.", + "@modsCount": { + "placeholders": { + "count": { + "type": "int" + } + } + }, + "@collectionCopyName": { + "placeholders": { + "title": { + "type": "String" + } + } + }, + "myMods": "MY MODS", + "nameHeader": "NAME", + "categoryHeader": "CATEGORY", + "sizeHeader": "SIZE", + "typeHeader": "TYPE", + "addHeader": "ADD", + "selectedCount": "{count}", + "@selectedCount": { + "placeholders": { + "count": { + "type": "int" + } + } + }, + "all": "ALL", + "searchHint": "SEARCH...", + "noPlayersFound": "No players found", + "moveModsDirectory": "MOVE MODS DIRECTORY", + "invalidDirectoryWarning": "The current mods directory is invalid because it contains non-ASCII characters. ", + "selectNewDirectoryDesc": "Please select the new directory where you want to move the mods directory. Mods will not be moved. Only the directory will be changed.", + "nonAsciiWarning": "Non-ASCII characters in the path are not allowed (e.g. Korean, Greek, etc.).", + "adminPermissionError": "You can't select a directory that requires admin permissions. Please select another directory.", + "selectedDirNonAsciiError": "The selected directory contains non-ASCII characters. Please select a different directory.", + "browse": "Browse", + "resetToDefault": "Reset to default", + "defaultDirNonAsciiError": "The default mods directory contains non-ASCII characters. Please select a different directory.", + "modInformation": "MOD INFORMATION", + "viewInformationAboutMod": "VIEW INFORMATION ABOUT A MOD", + "modVersionAuthorDisplay": "{version} - {author}", + "@modVersionAuthorDisplay": { + "placeholders": { + "version": { + "type": "String" + }, + "author": { + "type": "String" + } + } + }, + "descriptionLabel": "DESCRIPTION", + "imagesLabel": "IMAGES", + "affectedFilesLabel": "AFFECTED FILES", + "unknownResourceName": "Unknown resource name", + "abortingNexusLogin": "Aborting NexusMods login", + "installWebViewError": "Please install WebView to use this feature.", + "continueText": "CONTINUE", + "wait": "WAIT", + "win7CompatibilityDetected": "Windows 7 Compatibility Mode detected", + "disableWin7CompMode": "To login to NexusMods, please disable Windows 7 Compatibility Mode for Kyber Launcher.", + "reEnableWin7CompMode": "After you have logged in, you need to re-enable Windows 7 Compatibility Mode.", + "checkingWebView": "Checking WebView installation...", + "nexusLoginIntro": "To continue, you will need to log in with your NexusMods account in the browser that is about to open.", + "nexusLoginDataNotice": "The data is processed locally and will only be sent to Nexusmods.\nYou can also enable/disable this feature later in the settings menu.", + "playersHeader": "PLAYERS", + "eventsAndAnnouncements": "EVENTS & ANNOUNCEMENTS", + "viewUpcomingEventsDesc": "VIEW UPCOMING EVENTS & RECEIVE ANNOUNCEMENTS", + "sortByTitle": "SORT BY", + "timeFilterTitle": "TIME FILTER", + "itemsPerPageTitle": "ITEMS PER PAGE", + "allTime": "All time", + "oneYear": "1 year", + "oneMonth": "1 month", + "twoWeeks": "2 weeks", + "oneWeek": "1 week", + "oneDay": "1 day", + "all": "ALL", + "@hoursFormat": { + "placeholders": { + "count": { + "type": "String" + } + } + }, + "@minutesFormat": { + "placeholders": { + "count": { + "type": "int" + } + } + }, + "@secondsFormat": { + "placeholders": { + "count": { + "type": "int" + } + } + }, + "hoursFormat": "{count} HRS", + "minutesFormat": "{count} MIN", + "secondsFormat": "{count} SEC", + "noStatsFound": "No stats found", + "mostPlayed": "MOST PLAYED", + "totalKillsLabel": "TOTAL KILLS", + "totalDeathsLabel": "TOTAL DEATHS", + "kdRatioLabel": "K/D RATIO", + "assistsLabel": "ASSISTS", + "damageDoneLabel": "DAMAGE DONE", + "suicidesLabel": "SUICIDES", + "gamesWonLabel": "GAMES WON", + "gamesLostLabel": "GAMES LOST", + "winRateLabel": "WIN RATE", + "unitsSection": "UNITS", + "vehiclesSection": "VEHICLES", + "starfighterSection": "STARFIGHTER", + "serverSectionTitle": "SERVER", + "maxPlayersLabel": "MAX PLAYERS", + "maxSpectatorsLabel": "Max Spectators", + "privacySectionTitle": "Privacy", + "passwordLabel": "Password", + "passwordPlaceholder": "EXAMPLE", + "modeHeroesVersusVillains": "Heroes Versus Villains", + "modeGalacticAssault": "Galactic Assault", + "modeSupremacy": "Supremacy", + "modeCoOpAttack": "CO-OP Attack", + "modeCoOpDefend": "CO-OP Defend", + "modeStrike": "Strike", + "modeExtraction": "Extraction", + "modeBlast": "Blast", + "modeEwokHunt": "Ewok Hunt", + "modeJetpackCargo": "Jetpack Cargo", + "modeStarfighterAssault": "Starfighter Assault", + "modeHeroStarfighters": "Hero Starfighters", + "modeHeroShowdown": "Hero Showdown", + "mapTatooineMosEisley": "Tatooine - Mos Eisley", + "mapTatooineJabbasPalace": "Tatooine - Jabba's Palace", + "mapRepublicVenator": "Republic Venator", + "mapSeparatistDreadnought": "Separatist Dreadnought", + "mapResurgentStarDestroyer": "Resurgent Star Destroyer", + "mapMc85StarCruiser": "MC85 Star Cruiser", + "mapNabooPalaceHangar": "Naboo - Palace Hangar", + "mapEndorResearchStation9": "Endor - Research Station 9", + "mapEndorEwokVillage": "Endor - Ewok Village", + "mapNabooTheedPalace": "Naboo - Theed Palace", + "mapCrait": "Crait", + "activeRotation": "Active Rotation", + "importLabel": "Import", + "importMapRotation": "Import Map Rotation", + "invalidFileFormat": "Invalid file format", + "mapRotationImported": "Map rotation imported", + "noMapsActive": "No maps active", + "customMapNotFoundTooltip": "Custom map not found, this can happen if the map is not part of the current mod collection.\nThis map will be ignored when starting the server.", + "notFound": "Not found" +} \ No newline at end of file diff --git a/Launcher/lib/l10n/app_es.arb b/Launcher/lib/l10n/app_es.arb new file mode 100644 index 00000000..1536fce6 --- /dev/null +++ b/Launcher/lib/l10n/app_es.arb @@ -0,0 +1,643 @@ +{ + "language": "Language", + "accessibility": "ACCESSIBILITY", + "colorblindProfiles": "COLORBLIND PROFILES", + "defaultColor": "DEFAULT", + "protanomaly": "PROTANOMALY", + "deuteranomaly": "DEUTERANOMALY", + "tritanomaly": "TRITANOMALY", + "resetSettings": "RESET SETTINGS", + "reset": "Reset", + "rememberWindowPosition": "Remember Window Position", + "customization": "CUSTOMIZATION", + "patreonExclusive": "PATREON EXCLUSIVE", + "changeHighlightColor": "CHANGE HIGHLIGHT COLOR", + "change": "Change", + "changeBackground": "CHANGE BACKGROUND", + "save": "Save", + "cancel": "Cancel", + "changeColor": "CHANGE COLOR", + "accounts": "ACCOUNTS", + "proxy": "PROXY", + "unavailable": "UNAVAILABLE", + "resetKyberToken": "Reset Kyber Token", + "nexusMods": "NexusMods", + "logout": "Logout", + "login": "Login", + "eaLogout": "EA Logout", + "linkedAccounts": "LINKED ACCOUNTS", + "discord": "Discord", + "connect": "Connect", + "discordUnlink": "{name} | Unlink", + "connectPatreon": "Connect Patreon", + "updates": "UPDATES", + "automaticallyUpdate": "Automatically Update", + "releaseChannel": "Release Channel", + "select": "Select", + "forceUpdate": "Force update", + "updateNow": "UPDATE NOW", + "other": "OTHER", + "licenses": "Licenses", + "showLicenses": "Show Licenses", + "developerMode": "Developer Mode", + "environment": "Environment", + "apiEnvironment": "API Environment", + "setNexusApiToken": "Set Nexus API Token", + "setToken": "Set token", + "setBackgroundImage": "Set Background Image", + "setImage": "Set Image", + "dummyServer": "Dummy Server", + "removeBackground": "Remove Background", + "copyKyberToken": "Copy Kyber Token", + "copy": "Copy", + "activity": "ACTIVITY", + "discordRichPresence": "Discord Rich Presence", + "loggingAndSentry": "Logging & Sentry", + "logs": "Logs", + "exportLogs": "Export Logs", + "optInToSentry": "Opt In To Sentry", + "optOutOfSentry": "Opt Out Of Sentry", + "optIn": "Opt In", + "optOut": "Opt Out", + "launcherDebugMode": "Launcher Debug Mode", + "moduleDebugMode": "Module Debug Mode", + "moduleRpcDebugMode": "Module RPC Debug Mode", + "mods": "MODS", + "changeModDirectory": "Change Mod Directory", + "newDirectory": "New Directory", + "frostyConverter": "Frosty Converter", + "convertYourPacks": "Convert your Packs", + "kyberPreloadedMods": "Kyber Preloaded Mods", + "ingame": "INGAME", + "ingameHotkey": "Ingame Hotkey (Moderation Menu)", + "restartGameWarning": "To apply changes, restart the game", + "enabled": "Enabled", + "disabled": "Disabled", + "proximityChat": "Proximity Chat", + "inputMode": "Input Mode", + "openMic": "Open Mic", + "pushToTalk": "Push to Talk", + "pushToTalkKey": "Push to Talk Key", + "inputVolume": "Input Volume", + "outputVolume": "Output Volume", + "inputDevice": "Input Device", + "outputDevice": "Output Device", + "noDevicesFound": "No devices found", + "selectBackground": "SELECT A BACKGROUND", + "chooseBackgroundDesc": "CHOOSE FROM 8 BACKGROUNDS OR SELECT A CUSTOM BACKGROUND", + "customBackground": "CUSTOM BACKGROUND", + "selectImageDesc": "Select a background image for the launcher", + "minResolutionDesc": "The image resolution should be at least 1920x1080", + "selectImage": "Select Image", + "fileTooLarge": "The file is too large. Please select a file smaller than 10MB", + "resolutionTooLow": "The image resolution is too low. Please select an image with a resolution of at least 1920x1080", + "copyingImage": "Copying image...", + "selectImagePickerTitle": "Select a background image", + "langAccTitle": "LANGUAGE & ACCESSIBILITY", + "langAccDesc": "CHANGE LANGUAGE, PROXY & ACCESSIBILITY SETTINGS", + "modConfigTitle": "MOD CONFIGURATION", + "modConfigDesc": "CONFIGURE MODS SETTINGS, IMPORT FROM FROSTY & MORE", + "creditsTitle": "CREDITS LIST", + "creditsDesc": "VIEW DEVELOPERS, CONTRIBUTORS & PATREON SUPPORTERS", + "ingameSettingsTitle": "INGAME SETTINGS", + "proximityChatDesc": "CONFIGURE PROXIMITY CHAT SETTINGS", + "logsActivityTitle": "LOGS & ACTIVITY", + "logsActivityDesc": "VIEW ACTIVITY & DEBUG LOGGING SETTINGS", + "accountsUpdatesTitle": "ACCOUNTS & UPDATES", + "accountsUpdatesDesc": "LOGOUT, UPDATE SETTINGS & MORE", + "versionText": "VERSION: {version}#CL{build}", + "downloadManager": "DOWNLOAD MANAGER", + "pausedDownloads": "PAUSED DOWNLOADS", + "unCapSpeedPrefix": "UN-CAP DOWNLOAD SPEEDS WITH ", + "nexusModsPremium": "NEXUS MODS PREMIUM", + "downloadProgress": " / {total} ({speed}/s) {percentage}%", + "@downloadProgress": { + "placeholders": { + "total": { "type": "String" }, + "speed": { "type": "String" }, + "percentage": { "type": "int" } + } + }, + "loadMods": "LOAD MODS", + "export": "EXPORT", + "selectMods": "Select mods", + "ignoringDuplicate": "Ignoring {filename}. (Duplicate)", + "@ignoringDuplicate": { + "placeholders": { + "filename": { "type": "String" } + } + }, + "saveCollection": "Save collection", + "icon": "ICON", + "name": "Name", + "author": "Author", + "version": "Version", + "category": "Category", + "exportCollection": "EXPORT COLLECTION", + "exportingCollection": "Exporting collection...", + "selectIcon": "Select icon", + "eaLogin": "EA Login", + "loading": "Loading...", + "waitingForResponse": "Waiting for response...", + "maximaStarting": "Maxima is starting...", + "fetchingData": "Fetching data...", + "genericError": "An error occurred", + "loginFailed": "Login failed", + "userDoesNotExist": "The specified user does not exist", + "passwordIncorrect": "The specified password is incorrect", + "failedToFindAuthCode": "Failed to find auth code. Please try another browser", + "pathCopied": "The path has been copied to your clipboard", + "noAuthCodeReceived": "No authorization code was received", + "authorizationSuccessful": "Authorization successful", + "patreonAuthorizedMessage": "You have been successfully authorized as a Patreon member", + "authorizationFailed": "Authorization failed", + "maximaIntroText": "In order to use this launcher, you need to login to Maxima. This is required to launch and interact with Battlefront 2.\nYou will be redirected to the EA login page and after logging in, you will be redirected back to the launcher.", + "loginWithEa": "Login with EA", + "eaIdDisplay": "EA-ID: {eaId}", + "logout": "Log out", + "authorizePatreon": "Authorize Patreon", + "whitelistConfirmation": "Are you sure you want to add your current account to the whitelist?", + "currentAccountDisplay": "Current account: {displayName}", + "addToWhitelist": "Add to whitelist", + "gameNotOwnedMessage": "It seems like you do not own Battlefront 2. Please purchase the game or use another account.", + "maximaBackgroundServiceError": "Maxima failed to start the background service. This is usually caused by an antivirus program blocking the service. Please add an exception for the launcher and reinstall it.", + "maximaMissingFilesError": "Some files required to run Maxima are missing. This is usually caused by an antivirus program deleting the files. Please add an exception for the launcher and reinstall it.", + "antivirusExclusionInstruction": "Please exclude the following folders from your antivirus' real-time protection:", + "copyPath": "COPY PATH", + "debugLogsToggled": "{status} debug logs", + "signInNexusPrompt": "Please sign in to Nexus Mods to view mods", + "noModsFound": "No mods found", + "unknownError": "Unknown error", + "noDataFound": "No data found", + "modInformation": "MOD INFORMATION", + "viewModFilesShareEndorse": "VIEW MOD FILES, SHARE AND ENDORSE", + "files": "FILES", + "posts": "POSTS", + "endorseMustDownload": "You must download the mod before endorsing it", + "endorseWaitTime": "You must wait some time after downloading the mod before endorsing it", + "endorseLoginRequired": "You must be logged in to endorse mods", + "linkCopied": "Link copied to clipboard", + "updatedAt": "Updated: ", + "mainFiles": "MAIN FILES", + "miscellaneousFiles": "MISCELLANEOUS FILES", + "optionalFiles": "OPTIONAL FILES", + "archivedFiles": "ARCHIVED FILES", + "fileListTitle": "{title} ({count})", + "@fileListTitle": { + "placeholders": { + "title": { "type": "String" }, + "count": { "type": "int" } + } + }, + "verifiedModAuthor": "Verified Mod Author", + "viewingProfile": "VIEWING PROFILE", + "publishedMods": "PUBLISHED MODS", + "userInformation": "USER INFORMATION", + "viewUserStats": "VIEW USER STATS", + "joinedLabel": "Joined: ", + "modsLabel": "Mods: ", + "postsLabel": "Posts: ", + "uniqueDlsLabel": "Unique-DLs: ", + "viewsLabel": "Views: ", + "kudosLabel": "Kudos: ", + "contributedLabel": "Contributed: ", + "viewOnNexusMods": "VIEW ON NEXUS MODS", + "collectionMods": "Collection Mods", + "modNameVersion": "{name} ({version})", + "@modNameVersion": { + "placeholders": { + "name": { "type": "String" }, + "version": { "type": "String" } + } + }, + "downloadCollectionMods": "DOWNLOAD COLLECTION MODS", + "loadingMods": "LOADING MODS...", + "collections": "COLLECTIONS", + "manageCollectionsSub": "MANAGE MOD COLLECTIONS & CURATE COSMETIC MODS", + "browser": "Browser", + "paginationInfo": "{page}/{totalPages}", + "@paginationInfo": { + "placeholders": { + "page": { "type": "int" }, + "totalPages": { "type": "int" } + } + }, + "modScope": "MOD SCOPE", + "serverNotFound": "The server you were playing on could not be found.", + "nexusModsAuthorization": "NexusMods Authorization", + "nexusAuthDescription": "You need to authorize KyberLauncher on NexusMods to use the mod browser.", + "authorizeKyber": "Authorize Kyber", + "noReportsAvailable": "No reports available", + "reportedBy": "Reported by ", + "forLabel": " for ", + "onLabel": " on ", + "descriptionLabel": "Description", + "evidenceCountLabel": "Evidence ({count})", + "@evidenceCountLabel": { + "placeholders": { + "count": { "type": "int" } + } + }, + "reportViewTitle": "REPORT VIEW", + "viewReportsAndEvidence": "VIEW REPORTS AND EVIDENCE", + "reportsTab": "REPORTS", + "punishmentsTab": "PUNISHMENTS", + "reportRejected": "Report rejected", + "takeAction": "TAKE ACTION", + "reportedByPlayer": "By {name}", + "@reportedByPlayer": { + "placeholders": { + "name": { "type": "String" } + } + }, + "daysDuration": "{days} Days", + "@daysDuration": { + "placeholders": { + "days": { "type": "int" } + } + }, + "permanent": "Permanent", + "bannedByModerator": "Banned By {name}", + "@bannedByModerator": { + "placeholders": { + "name": { "type": "String" } + } + }, + "durationLabel": "Duration: {duration}", + "@durationLabel": { + "placeholders": { + "duration": { "type": "String" } + } + }, + "issuedLabel": "Issued: {date}", + "@issuedLabel": { + "placeholders": { + "date": { "type": "String" } + } + }, + "statusFilter": "STATUS", + "reportsTitle": "REPORTS", + "totalReportsHeader": "TOTAL REPORTS", + "latestReportHeader": "LATEST REPORT", + "latestReasonHeader": "LATEST REASON", + "statusHeader": "STATUS", + "errorLoadingReports": "Error loading reports: {message}", + "reasonWithCount": "{reasonName} ({count})", + "armsecCaseFile": "ARMSEC - CASE FILE", + "evidenceAndDataSummary": "EVIDENCE AND DATA SUMMARY", + "errorLoadingUser": "Error loading user", + "userNotFound": "User not found", + "kyberUser": "KYBER USER", + "open": "OPEN", + "options": "OPTIONS", + "@errorLoadingReports": { + "placeholders": { + "message": { "type": "String" } + } + }, + "@reasonWithCount": { + "placeholders": { + "reasonName": { "type": "String" }, + "count": { "type": "String" } + } + }, + "unknownServer": "Unknown Server", + "hostedBy": "Hosted by {creator}", + "@hostedBy": { + "placeholders": { + "creator": { "type": "String" } + } + }, + "backToServerBrowser": "Back to Server Browser", + "eventLog": "Event Log", + "lightSide": "Light Side", + "darkSide": "Dark Side", + "reportPlayer": "Report Player", + "region": "REGION", + "serverType": "SERVER TYPE", + "gameType": "GAME TYPE", + "gameMode": "GAME MODE", + "warning": "WARNING", + "warningMessageFallback": "Warning message not available.", + "newButton": "NEW", + "moderate": "MODERATE", + "manage": "MANAGE", + "rotation": "ROTATION", + "searchPlaceholder": "Search ...", + "noServersFound": "No servers found", + "serverBrowser": "Server Browser", + "optionsHeader": "OPTIONS", + "moderatorsHeader": "MODERATORS", + "bannedPlayersHeader": "BANNED PLAYERS", + "playButton": "PLAY", + "spectateButton": "SPECTATE", + "copyLinkButton": "COPY LINK", + "copiedToClipboard": "Copied to clipboard!", + "exportBans": "EXPORT BANS", + "importBans": "IMPORT BANS", + "kickAll": "KICK ALL", + "banAll": "BAN ALL", + "eventLogHeader": "Event Log", + "lightSideHeader": "Light Side", + "darkSideHeader": "Dark Side", + "userManager": "User Manager", + "consoleHint": "/COMMAND OR SEND MESSAGE", + "untilLabel": "UNTIL: {date}", + "reasonLabel": "REASON: {reason}", + "unban": "UNBAN", + "modify": "MODIFY", + "playerUnbanned": "Player unbanned", + "timeElapsed": "Time has elapsed", + "daysRemaining": "{count} day(s)", + "hoursRemaining": "{count} hour(s)", + "minutesRemaining": "{count} minute(s)", + "secondsRemaining": "{count} second(s)", + "remaining": "{time} remaining", + "promoteUserTooltip": "Promote User", + "errorOccurred": "An error occurred", + "swapPlayerTooltip": "Swap Player", + "kickPlayerTooltip": "Kick player", + "banPlayerTooltip": "Ban player", + "off": "OFF", + "on": "ON", + "home": "HOME", + "host": "HOST", + "stats": "STATS", + "settings": "SETTINGS", + "ingamePanel": "INGAME PANEL", + "extractingFileWithProgress": "EXTRACTING FILE ({extracted}/{total})", + "extractingFile": "EXTRACTING FILE", + "downloadingWithProgress": "DOWNLOADING ({percentage}% {downloaded}/{total})", + "downloadManager": "DOWNLOAD MANAGER", + "createNewCollection": "CREATE NEW COLLECTION", + "pageNotFoundTitle": "Page Not Found", + "pageNotFoundFallback": "page not found", + "goToHomePage": "Go to home page", + "newCollectionTitle": "New Collection", + "@extractingFileWithProgress": { + "placeholders": { + "extracted": { + "type": "String" + }, + "total": { + "type": "String" + } + } + }, + "@downloadingWithProgress": { + "placeholders": { + "percentage": { + "type": "String" + }, + "downloaded": { + "type": "String" + }, + "total": { + "type": "String" + } + } + }, + "@untilLabel": { + "placeholders": { + "date": { "type": "String" } + } + }, + "@reasonLabel": { + "placeholders": { + "reason": { "type": "String" } + } + }, + "@daysRemaining": { + "placeholders": { + "count": { "type": "int" } + } + }, + "@hoursRemaining": { + "placeholders": { + "count": { "type": "int" } + } + }, + "@minutesRemaining": { + "placeholders": { + "count": { "type": "int" } + } + }, + "@secondsRemaining": { + "placeholders": { + "count": { "type": "int" } + } + }, + "@remaining": { + "placeholders": { + "time": { "type": "String" } + } + }, + "eaAccount": "EA Account", + "finish": "FINISH", + "skip": "SKIP", + "searchFailed": "Failed to search players: {message}", + "unexpectedError": "An unexpected error occurred: {error}", + "searchKyberUser": "Search for a kyber user", + "noResultsFound": "No results found for \"{query}\".", + "friendsCount": "FRIENDS ({count})", + "recentlyViewed": "RECENTLY VIEWED", + "userSearchHistory": "USER SEARCH HISTORY", + "online": "Online", + "playingStatus": "Playing {status}", + "@searchFailed": { + "placeholders": { + "message": { "type": "String" } + } + }, + "@unexpectedError": { + "placeholders": { + "error": { "type": "String" } + } + }, + "@noResultsFound": { + "placeholders": { + "query": { "type": "String" } + } + }, + "@friendsCount": { + "placeholders": { + "count": { "type": "int" } + } + }, + "@playingStatus": { + "placeholders": { + "status": { "type": "String" } + } + }, + "selectCollectionIcon": "Select Collection Icon", + "collectionLabel": "COLLECTION", + "collectionDescription": "A COLLECTION IS A READY TO PLAY MOD LIST", + "modsCount": "{count} Mods", + "collectionNamePlaceholder": "Collection Name", + "saveButton": "SAVE", + "betterSabersPlugin": "BETTER SABERS (PLUGIN)", + "reloadingMods": "Reloading mods", + "generatedBetterSabers": "Generated BetterSabers", + "exportCollectionTar": "EXPORT COLLECTION TAR", + "editCollection": "EDIT COLLECTION", + "createACopy": "CREATE A COPY", + "collectionCopyName": "{title} (Copy)", + "deleteCollection": "DELETE COLLECTION", + "largeModsWarning": "Using multiple large or complex mods isn't recommended, as they often conflict with each other.", + "@modsCount": { + "placeholders": { + "count": { + "type": "int" + } + } + }, + "@collectionCopyName": { + "placeholders": { + "title": { + "type": "String" + } + } + }, + "myMods": "MY MODS", + "nameHeader": "NAME", + "categoryHeader": "CATEGORY", + "sizeHeader": "SIZE", + "typeHeader": "TYPE", + "addHeader": "ADD", + "selectedCount": "{count}", + "@selectedCount": { + "placeholders": { + "count": { + "type": "int" + } + } + }, + "all": "ALL", + "searchHint": "SEARCH...", + "noPlayersFound": "No players found", + "moveModsDirectory": "MOVE MODS DIRECTORY", + "invalidDirectoryWarning": "The current mods directory is invalid because it contains non-ASCII characters. ", + "selectNewDirectoryDesc": "Please select the new directory where you want to move the mods directory. Mods will not be moved. Only the directory will be changed.", + "nonAsciiWarning": "Non-ASCII characters in the path are not allowed (e.g. Korean, Greek, etc.).", + "adminPermissionError": "You can't select a directory that requires admin permissions. Please select another directory.", + "selectedDirNonAsciiError": "The selected directory contains non-ASCII characters. Please select a different directory.", + "browse": "Browse", + "resetToDefault": "Reset to default", + "defaultDirNonAsciiError": "The default mods directory contains non-ASCII characters. Please select a different directory.", + "modInformation": "MOD INFORMATION", + "viewInformationAboutMod": "VIEW INFORMATION ABOUT A MOD", + "modVersionAuthorDisplay": "{version} - {author}", + "@modVersionAuthorDisplay": { + "placeholders": { + "version": { + "type": "String" + }, + "author": { + "type": "String" + } + } + }, + "descriptionLabel": "DESCRIPTION", + "imagesLabel": "IMAGES", + "affectedFilesLabel": "AFFECTED FILES", + "unknownResourceName": "Unknown resource name", + "abortingNexusLogin": "Aborting NexusMods login", + "installWebViewError": "Please install WebView to use this feature.", + "continueText": "CONTINUE", + "wait": "WAIT", + "win7CompatibilityDetected": "Windows 7 Compatibility Mode detected", + "disableWin7CompMode": "To login to NexusMods, please disable Windows 7 Compatibility Mode for Kyber Launcher.", + "reEnableWin7CompMode": "After you have logged in, you need to re-enable Windows 7 Compatibility Mode.", + "checkingWebView": "Checking WebView installation...", + "nexusLoginIntro": "To continue, you will need to log in with your NexusMods account in the browser that is about to open.", + "nexusLoginDataNotice": "The data is processed locally and will only be sent to Nexusmods.\nYou can also enable/disable this feature later in the settings menu.", + "playersHeader": "PLAYERS", + "eventsAndAnnouncements": "EVENTS & ANNOUNCEMENTS", + "viewUpcomingEventsDesc": "VIEW UPCOMING EVENTS & RECEIVE ANNOUNCEMENTS", + "sortByTitle": "SORT BY", + "timeFilterTitle": "TIME FILTER", + "itemsPerPageTitle": "ITEMS PER PAGE", + "allTime": "All time", + "oneYear": "1 year", + "oneMonth": "1 month", + "twoWeeks": "2 weeks", + "oneWeek": "1 week", + "oneDay": "1 day", + "all": "ALL", + "@hoursFormat": { + "placeholders": { + "count": { + "type": "String" + } + } + }, + "@minutesFormat": { + "placeholders": { + "count": { + "type": "int" + } + } + }, + "@secondsFormat": { + "placeholders": { + "count": { + "type": "int" + } + } + }, + "hoursFormat": "{count} HRS", + "minutesFormat": "{count} MIN", + "secondsFormat": "{count} SEC", + "noStatsFound": "No stats found", + "mostPlayed": "MOST PLAYED", + "totalKillsLabel": "TOTAL KILLS", + "totalDeathsLabel": "TOTAL DEATHS", + "kdRatioLabel": "K/D RATIO", + "assistsLabel": "ASSISTS", + "damageDoneLabel": "DAMAGE DONE", + "suicidesLabel": "SUICIDES", + "gamesWonLabel": "GAMES WON", + "gamesLostLabel": "GAMES LOST", + "winRateLabel": "WIN RATE", + "unitsSection": "UNITS", + "vehiclesSection": "VEHICLES", + "starfighterSection": "STARFIGHTER", + "serverSectionTitle": "SERVER", + "maxPlayersLabel": "MAX PLAYERS", + "maxSpectatorsLabel": "Max Spectators", + "privacySectionTitle": "Privacy", + "passwordLabel": "Password", + "passwordPlaceholder": "EXAMPLE", + "modeHeroesVersusVillains": "Heroes Versus Villains", + "modeGalacticAssault": "Galactic Assault", + "modeSupremacy": "Supremacy", + "modeCoOpAttack": "CO-OP Attack", + "modeCoOpDefend": "CO-OP Defend", + "modeStrike": "Strike", + "modeExtraction": "Extraction", + "modeBlast": "Blast", + "modeEwokHunt": "Ewok Hunt", + "modeJetpackCargo": "Jetpack Cargo", + "modeStarfighterAssault": "Starfighter Assault", + "modeHeroStarfighters": "Hero Starfighters", + "modeHeroShowdown": "Hero Showdown", + "mapTatooineMosEisley": "Tatooine - Mos Eisley", + "mapTatooineJabbasPalace": "Tatooine - Jabba's Palace", + "mapRepublicVenator": "Republic Venator", + "mapSeparatistDreadnought": "Separatist Dreadnought", + "mapResurgentStarDestroyer": "Resurgent Star Destroyer", + "mapMc85StarCruiser": "MC85 Star Cruiser", + "mapNabooPalaceHangar": "Naboo - Palace Hangar", + "mapEndorResearchStation9": "Endor - Research Station 9", + "mapEndorEwokVillage": "Endor - Ewok Village", + "mapNabooTheedPalace": "Naboo - Theed Palace", + "mapCrait": "Crait", + "activeRotation": "Active Rotation", + "importLabel": "Import", + "importMapRotation": "Import Map Rotation", + "invalidFileFormat": "Invalid file format", + "mapRotationImported": "Map rotation imported", + "noMapsActive": "No maps active", + "customMapNotFoundTooltip": "Custom map not found, this can happen if the map is not part of the current mod collection.\nThis map will be ignored when starting the server.", + "notFound": "Not found" +} \ No newline at end of file diff --git a/Launcher/lib/l10n/app_fr.arb b/Launcher/lib/l10n/app_fr.arb new file mode 100644 index 00000000..1536fce6 --- /dev/null +++ b/Launcher/lib/l10n/app_fr.arb @@ -0,0 +1,643 @@ +{ + "language": "Language", + "accessibility": "ACCESSIBILITY", + "colorblindProfiles": "COLORBLIND PROFILES", + "defaultColor": "DEFAULT", + "protanomaly": "PROTANOMALY", + "deuteranomaly": "DEUTERANOMALY", + "tritanomaly": "TRITANOMALY", + "resetSettings": "RESET SETTINGS", + "reset": "Reset", + "rememberWindowPosition": "Remember Window Position", + "customization": "CUSTOMIZATION", + "patreonExclusive": "PATREON EXCLUSIVE", + "changeHighlightColor": "CHANGE HIGHLIGHT COLOR", + "change": "Change", + "changeBackground": "CHANGE BACKGROUND", + "save": "Save", + "cancel": "Cancel", + "changeColor": "CHANGE COLOR", + "accounts": "ACCOUNTS", + "proxy": "PROXY", + "unavailable": "UNAVAILABLE", + "resetKyberToken": "Reset Kyber Token", + "nexusMods": "NexusMods", + "logout": "Logout", + "login": "Login", + "eaLogout": "EA Logout", + "linkedAccounts": "LINKED ACCOUNTS", + "discord": "Discord", + "connect": "Connect", + "discordUnlink": "{name} | Unlink", + "connectPatreon": "Connect Patreon", + "updates": "UPDATES", + "automaticallyUpdate": "Automatically Update", + "releaseChannel": "Release Channel", + "select": "Select", + "forceUpdate": "Force update", + "updateNow": "UPDATE NOW", + "other": "OTHER", + "licenses": "Licenses", + "showLicenses": "Show Licenses", + "developerMode": "Developer Mode", + "environment": "Environment", + "apiEnvironment": "API Environment", + "setNexusApiToken": "Set Nexus API Token", + "setToken": "Set token", + "setBackgroundImage": "Set Background Image", + "setImage": "Set Image", + "dummyServer": "Dummy Server", + "removeBackground": "Remove Background", + "copyKyberToken": "Copy Kyber Token", + "copy": "Copy", + "activity": "ACTIVITY", + "discordRichPresence": "Discord Rich Presence", + "loggingAndSentry": "Logging & Sentry", + "logs": "Logs", + "exportLogs": "Export Logs", + "optInToSentry": "Opt In To Sentry", + "optOutOfSentry": "Opt Out Of Sentry", + "optIn": "Opt In", + "optOut": "Opt Out", + "launcherDebugMode": "Launcher Debug Mode", + "moduleDebugMode": "Module Debug Mode", + "moduleRpcDebugMode": "Module RPC Debug Mode", + "mods": "MODS", + "changeModDirectory": "Change Mod Directory", + "newDirectory": "New Directory", + "frostyConverter": "Frosty Converter", + "convertYourPacks": "Convert your Packs", + "kyberPreloadedMods": "Kyber Preloaded Mods", + "ingame": "INGAME", + "ingameHotkey": "Ingame Hotkey (Moderation Menu)", + "restartGameWarning": "To apply changes, restart the game", + "enabled": "Enabled", + "disabled": "Disabled", + "proximityChat": "Proximity Chat", + "inputMode": "Input Mode", + "openMic": "Open Mic", + "pushToTalk": "Push to Talk", + "pushToTalkKey": "Push to Talk Key", + "inputVolume": "Input Volume", + "outputVolume": "Output Volume", + "inputDevice": "Input Device", + "outputDevice": "Output Device", + "noDevicesFound": "No devices found", + "selectBackground": "SELECT A BACKGROUND", + "chooseBackgroundDesc": "CHOOSE FROM 8 BACKGROUNDS OR SELECT A CUSTOM BACKGROUND", + "customBackground": "CUSTOM BACKGROUND", + "selectImageDesc": "Select a background image for the launcher", + "minResolutionDesc": "The image resolution should be at least 1920x1080", + "selectImage": "Select Image", + "fileTooLarge": "The file is too large. Please select a file smaller than 10MB", + "resolutionTooLow": "The image resolution is too low. Please select an image with a resolution of at least 1920x1080", + "copyingImage": "Copying image...", + "selectImagePickerTitle": "Select a background image", + "langAccTitle": "LANGUAGE & ACCESSIBILITY", + "langAccDesc": "CHANGE LANGUAGE, PROXY & ACCESSIBILITY SETTINGS", + "modConfigTitle": "MOD CONFIGURATION", + "modConfigDesc": "CONFIGURE MODS SETTINGS, IMPORT FROM FROSTY & MORE", + "creditsTitle": "CREDITS LIST", + "creditsDesc": "VIEW DEVELOPERS, CONTRIBUTORS & PATREON SUPPORTERS", + "ingameSettingsTitle": "INGAME SETTINGS", + "proximityChatDesc": "CONFIGURE PROXIMITY CHAT SETTINGS", + "logsActivityTitle": "LOGS & ACTIVITY", + "logsActivityDesc": "VIEW ACTIVITY & DEBUG LOGGING SETTINGS", + "accountsUpdatesTitle": "ACCOUNTS & UPDATES", + "accountsUpdatesDesc": "LOGOUT, UPDATE SETTINGS & MORE", + "versionText": "VERSION: {version}#CL{build}", + "downloadManager": "DOWNLOAD MANAGER", + "pausedDownloads": "PAUSED DOWNLOADS", + "unCapSpeedPrefix": "UN-CAP DOWNLOAD SPEEDS WITH ", + "nexusModsPremium": "NEXUS MODS PREMIUM", + "downloadProgress": " / {total} ({speed}/s) {percentage}%", + "@downloadProgress": { + "placeholders": { + "total": { "type": "String" }, + "speed": { "type": "String" }, + "percentage": { "type": "int" } + } + }, + "loadMods": "LOAD MODS", + "export": "EXPORT", + "selectMods": "Select mods", + "ignoringDuplicate": "Ignoring {filename}. (Duplicate)", + "@ignoringDuplicate": { + "placeholders": { + "filename": { "type": "String" } + } + }, + "saveCollection": "Save collection", + "icon": "ICON", + "name": "Name", + "author": "Author", + "version": "Version", + "category": "Category", + "exportCollection": "EXPORT COLLECTION", + "exportingCollection": "Exporting collection...", + "selectIcon": "Select icon", + "eaLogin": "EA Login", + "loading": "Loading...", + "waitingForResponse": "Waiting for response...", + "maximaStarting": "Maxima is starting...", + "fetchingData": "Fetching data...", + "genericError": "An error occurred", + "loginFailed": "Login failed", + "userDoesNotExist": "The specified user does not exist", + "passwordIncorrect": "The specified password is incorrect", + "failedToFindAuthCode": "Failed to find auth code. Please try another browser", + "pathCopied": "The path has been copied to your clipboard", + "noAuthCodeReceived": "No authorization code was received", + "authorizationSuccessful": "Authorization successful", + "patreonAuthorizedMessage": "You have been successfully authorized as a Patreon member", + "authorizationFailed": "Authorization failed", + "maximaIntroText": "In order to use this launcher, you need to login to Maxima. This is required to launch and interact with Battlefront 2.\nYou will be redirected to the EA login page and after logging in, you will be redirected back to the launcher.", + "loginWithEa": "Login with EA", + "eaIdDisplay": "EA-ID: {eaId}", + "logout": "Log out", + "authorizePatreon": "Authorize Patreon", + "whitelistConfirmation": "Are you sure you want to add your current account to the whitelist?", + "currentAccountDisplay": "Current account: {displayName}", + "addToWhitelist": "Add to whitelist", + "gameNotOwnedMessage": "It seems like you do not own Battlefront 2. Please purchase the game or use another account.", + "maximaBackgroundServiceError": "Maxima failed to start the background service. This is usually caused by an antivirus program blocking the service. Please add an exception for the launcher and reinstall it.", + "maximaMissingFilesError": "Some files required to run Maxima are missing. This is usually caused by an antivirus program deleting the files. Please add an exception for the launcher and reinstall it.", + "antivirusExclusionInstruction": "Please exclude the following folders from your antivirus' real-time protection:", + "copyPath": "COPY PATH", + "debugLogsToggled": "{status} debug logs", + "signInNexusPrompt": "Please sign in to Nexus Mods to view mods", + "noModsFound": "No mods found", + "unknownError": "Unknown error", + "noDataFound": "No data found", + "modInformation": "MOD INFORMATION", + "viewModFilesShareEndorse": "VIEW MOD FILES, SHARE AND ENDORSE", + "files": "FILES", + "posts": "POSTS", + "endorseMustDownload": "You must download the mod before endorsing it", + "endorseWaitTime": "You must wait some time after downloading the mod before endorsing it", + "endorseLoginRequired": "You must be logged in to endorse mods", + "linkCopied": "Link copied to clipboard", + "updatedAt": "Updated: ", + "mainFiles": "MAIN FILES", + "miscellaneousFiles": "MISCELLANEOUS FILES", + "optionalFiles": "OPTIONAL FILES", + "archivedFiles": "ARCHIVED FILES", + "fileListTitle": "{title} ({count})", + "@fileListTitle": { + "placeholders": { + "title": { "type": "String" }, + "count": { "type": "int" } + } + }, + "verifiedModAuthor": "Verified Mod Author", + "viewingProfile": "VIEWING PROFILE", + "publishedMods": "PUBLISHED MODS", + "userInformation": "USER INFORMATION", + "viewUserStats": "VIEW USER STATS", + "joinedLabel": "Joined: ", + "modsLabel": "Mods: ", + "postsLabel": "Posts: ", + "uniqueDlsLabel": "Unique-DLs: ", + "viewsLabel": "Views: ", + "kudosLabel": "Kudos: ", + "contributedLabel": "Contributed: ", + "viewOnNexusMods": "VIEW ON NEXUS MODS", + "collectionMods": "Collection Mods", + "modNameVersion": "{name} ({version})", + "@modNameVersion": { + "placeholders": { + "name": { "type": "String" }, + "version": { "type": "String" } + } + }, + "downloadCollectionMods": "DOWNLOAD COLLECTION MODS", + "loadingMods": "LOADING MODS...", + "collections": "COLLECTIONS", + "manageCollectionsSub": "MANAGE MOD COLLECTIONS & CURATE COSMETIC MODS", + "browser": "Browser", + "paginationInfo": "{page}/{totalPages}", + "@paginationInfo": { + "placeholders": { + "page": { "type": "int" }, + "totalPages": { "type": "int" } + } + }, + "modScope": "MOD SCOPE", + "serverNotFound": "The server you were playing on could not be found.", + "nexusModsAuthorization": "NexusMods Authorization", + "nexusAuthDescription": "You need to authorize KyberLauncher on NexusMods to use the mod browser.", + "authorizeKyber": "Authorize Kyber", + "noReportsAvailable": "No reports available", + "reportedBy": "Reported by ", + "forLabel": " for ", + "onLabel": " on ", + "descriptionLabel": "Description", + "evidenceCountLabel": "Evidence ({count})", + "@evidenceCountLabel": { + "placeholders": { + "count": { "type": "int" } + } + }, + "reportViewTitle": "REPORT VIEW", + "viewReportsAndEvidence": "VIEW REPORTS AND EVIDENCE", + "reportsTab": "REPORTS", + "punishmentsTab": "PUNISHMENTS", + "reportRejected": "Report rejected", + "takeAction": "TAKE ACTION", + "reportedByPlayer": "By {name}", + "@reportedByPlayer": { + "placeholders": { + "name": { "type": "String" } + } + }, + "daysDuration": "{days} Days", + "@daysDuration": { + "placeholders": { + "days": { "type": "int" } + } + }, + "permanent": "Permanent", + "bannedByModerator": "Banned By {name}", + "@bannedByModerator": { + "placeholders": { + "name": { "type": "String" } + } + }, + "durationLabel": "Duration: {duration}", + "@durationLabel": { + "placeholders": { + "duration": { "type": "String" } + } + }, + "issuedLabel": "Issued: {date}", + "@issuedLabel": { + "placeholders": { + "date": { "type": "String" } + } + }, + "statusFilter": "STATUS", + "reportsTitle": "REPORTS", + "totalReportsHeader": "TOTAL REPORTS", + "latestReportHeader": "LATEST REPORT", + "latestReasonHeader": "LATEST REASON", + "statusHeader": "STATUS", + "errorLoadingReports": "Error loading reports: {message}", + "reasonWithCount": "{reasonName} ({count})", + "armsecCaseFile": "ARMSEC - CASE FILE", + "evidenceAndDataSummary": "EVIDENCE AND DATA SUMMARY", + "errorLoadingUser": "Error loading user", + "userNotFound": "User not found", + "kyberUser": "KYBER USER", + "open": "OPEN", + "options": "OPTIONS", + "@errorLoadingReports": { + "placeholders": { + "message": { "type": "String" } + } + }, + "@reasonWithCount": { + "placeholders": { + "reasonName": { "type": "String" }, + "count": { "type": "String" } + } + }, + "unknownServer": "Unknown Server", + "hostedBy": "Hosted by {creator}", + "@hostedBy": { + "placeholders": { + "creator": { "type": "String" } + } + }, + "backToServerBrowser": "Back to Server Browser", + "eventLog": "Event Log", + "lightSide": "Light Side", + "darkSide": "Dark Side", + "reportPlayer": "Report Player", + "region": "REGION", + "serverType": "SERVER TYPE", + "gameType": "GAME TYPE", + "gameMode": "GAME MODE", + "warning": "WARNING", + "warningMessageFallback": "Warning message not available.", + "newButton": "NEW", + "moderate": "MODERATE", + "manage": "MANAGE", + "rotation": "ROTATION", + "searchPlaceholder": "Search ...", + "noServersFound": "No servers found", + "serverBrowser": "Server Browser", + "optionsHeader": "OPTIONS", + "moderatorsHeader": "MODERATORS", + "bannedPlayersHeader": "BANNED PLAYERS", + "playButton": "PLAY", + "spectateButton": "SPECTATE", + "copyLinkButton": "COPY LINK", + "copiedToClipboard": "Copied to clipboard!", + "exportBans": "EXPORT BANS", + "importBans": "IMPORT BANS", + "kickAll": "KICK ALL", + "banAll": "BAN ALL", + "eventLogHeader": "Event Log", + "lightSideHeader": "Light Side", + "darkSideHeader": "Dark Side", + "userManager": "User Manager", + "consoleHint": "/COMMAND OR SEND MESSAGE", + "untilLabel": "UNTIL: {date}", + "reasonLabel": "REASON: {reason}", + "unban": "UNBAN", + "modify": "MODIFY", + "playerUnbanned": "Player unbanned", + "timeElapsed": "Time has elapsed", + "daysRemaining": "{count} day(s)", + "hoursRemaining": "{count} hour(s)", + "minutesRemaining": "{count} minute(s)", + "secondsRemaining": "{count} second(s)", + "remaining": "{time} remaining", + "promoteUserTooltip": "Promote User", + "errorOccurred": "An error occurred", + "swapPlayerTooltip": "Swap Player", + "kickPlayerTooltip": "Kick player", + "banPlayerTooltip": "Ban player", + "off": "OFF", + "on": "ON", + "home": "HOME", + "host": "HOST", + "stats": "STATS", + "settings": "SETTINGS", + "ingamePanel": "INGAME PANEL", + "extractingFileWithProgress": "EXTRACTING FILE ({extracted}/{total})", + "extractingFile": "EXTRACTING FILE", + "downloadingWithProgress": "DOWNLOADING ({percentage}% {downloaded}/{total})", + "downloadManager": "DOWNLOAD MANAGER", + "createNewCollection": "CREATE NEW COLLECTION", + "pageNotFoundTitle": "Page Not Found", + "pageNotFoundFallback": "page not found", + "goToHomePage": "Go to home page", + "newCollectionTitle": "New Collection", + "@extractingFileWithProgress": { + "placeholders": { + "extracted": { + "type": "String" + }, + "total": { + "type": "String" + } + } + }, + "@downloadingWithProgress": { + "placeholders": { + "percentage": { + "type": "String" + }, + "downloaded": { + "type": "String" + }, + "total": { + "type": "String" + } + } + }, + "@untilLabel": { + "placeholders": { + "date": { "type": "String" } + } + }, + "@reasonLabel": { + "placeholders": { + "reason": { "type": "String" } + } + }, + "@daysRemaining": { + "placeholders": { + "count": { "type": "int" } + } + }, + "@hoursRemaining": { + "placeholders": { + "count": { "type": "int" } + } + }, + "@minutesRemaining": { + "placeholders": { + "count": { "type": "int" } + } + }, + "@secondsRemaining": { + "placeholders": { + "count": { "type": "int" } + } + }, + "@remaining": { + "placeholders": { + "time": { "type": "String" } + } + }, + "eaAccount": "EA Account", + "finish": "FINISH", + "skip": "SKIP", + "searchFailed": "Failed to search players: {message}", + "unexpectedError": "An unexpected error occurred: {error}", + "searchKyberUser": "Search for a kyber user", + "noResultsFound": "No results found for \"{query}\".", + "friendsCount": "FRIENDS ({count})", + "recentlyViewed": "RECENTLY VIEWED", + "userSearchHistory": "USER SEARCH HISTORY", + "online": "Online", + "playingStatus": "Playing {status}", + "@searchFailed": { + "placeholders": { + "message": { "type": "String" } + } + }, + "@unexpectedError": { + "placeholders": { + "error": { "type": "String" } + } + }, + "@noResultsFound": { + "placeholders": { + "query": { "type": "String" } + } + }, + "@friendsCount": { + "placeholders": { + "count": { "type": "int" } + } + }, + "@playingStatus": { + "placeholders": { + "status": { "type": "String" } + } + }, + "selectCollectionIcon": "Select Collection Icon", + "collectionLabel": "COLLECTION", + "collectionDescription": "A COLLECTION IS A READY TO PLAY MOD LIST", + "modsCount": "{count} Mods", + "collectionNamePlaceholder": "Collection Name", + "saveButton": "SAVE", + "betterSabersPlugin": "BETTER SABERS (PLUGIN)", + "reloadingMods": "Reloading mods", + "generatedBetterSabers": "Generated BetterSabers", + "exportCollectionTar": "EXPORT COLLECTION TAR", + "editCollection": "EDIT COLLECTION", + "createACopy": "CREATE A COPY", + "collectionCopyName": "{title} (Copy)", + "deleteCollection": "DELETE COLLECTION", + "largeModsWarning": "Using multiple large or complex mods isn't recommended, as they often conflict with each other.", + "@modsCount": { + "placeholders": { + "count": { + "type": "int" + } + } + }, + "@collectionCopyName": { + "placeholders": { + "title": { + "type": "String" + } + } + }, + "myMods": "MY MODS", + "nameHeader": "NAME", + "categoryHeader": "CATEGORY", + "sizeHeader": "SIZE", + "typeHeader": "TYPE", + "addHeader": "ADD", + "selectedCount": "{count}", + "@selectedCount": { + "placeholders": { + "count": { + "type": "int" + } + } + }, + "all": "ALL", + "searchHint": "SEARCH...", + "noPlayersFound": "No players found", + "moveModsDirectory": "MOVE MODS DIRECTORY", + "invalidDirectoryWarning": "The current mods directory is invalid because it contains non-ASCII characters. ", + "selectNewDirectoryDesc": "Please select the new directory where you want to move the mods directory. Mods will not be moved. Only the directory will be changed.", + "nonAsciiWarning": "Non-ASCII characters in the path are not allowed (e.g. Korean, Greek, etc.).", + "adminPermissionError": "You can't select a directory that requires admin permissions. Please select another directory.", + "selectedDirNonAsciiError": "The selected directory contains non-ASCII characters. Please select a different directory.", + "browse": "Browse", + "resetToDefault": "Reset to default", + "defaultDirNonAsciiError": "The default mods directory contains non-ASCII characters. Please select a different directory.", + "modInformation": "MOD INFORMATION", + "viewInformationAboutMod": "VIEW INFORMATION ABOUT A MOD", + "modVersionAuthorDisplay": "{version} - {author}", + "@modVersionAuthorDisplay": { + "placeholders": { + "version": { + "type": "String" + }, + "author": { + "type": "String" + } + } + }, + "descriptionLabel": "DESCRIPTION", + "imagesLabel": "IMAGES", + "affectedFilesLabel": "AFFECTED FILES", + "unknownResourceName": "Unknown resource name", + "abortingNexusLogin": "Aborting NexusMods login", + "installWebViewError": "Please install WebView to use this feature.", + "continueText": "CONTINUE", + "wait": "WAIT", + "win7CompatibilityDetected": "Windows 7 Compatibility Mode detected", + "disableWin7CompMode": "To login to NexusMods, please disable Windows 7 Compatibility Mode for Kyber Launcher.", + "reEnableWin7CompMode": "After you have logged in, you need to re-enable Windows 7 Compatibility Mode.", + "checkingWebView": "Checking WebView installation...", + "nexusLoginIntro": "To continue, you will need to log in with your NexusMods account in the browser that is about to open.", + "nexusLoginDataNotice": "The data is processed locally and will only be sent to Nexusmods.\nYou can also enable/disable this feature later in the settings menu.", + "playersHeader": "PLAYERS", + "eventsAndAnnouncements": "EVENTS & ANNOUNCEMENTS", + "viewUpcomingEventsDesc": "VIEW UPCOMING EVENTS & RECEIVE ANNOUNCEMENTS", + "sortByTitle": "SORT BY", + "timeFilterTitle": "TIME FILTER", + "itemsPerPageTitle": "ITEMS PER PAGE", + "allTime": "All time", + "oneYear": "1 year", + "oneMonth": "1 month", + "twoWeeks": "2 weeks", + "oneWeek": "1 week", + "oneDay": "1 day", + "all": "ALL", + "@hoursFormat": { + "placeholders": { + "count": { + "type": "String" + } + } + }, + "@minutesFormat": { + "placeholders": { + "count": { + "type": "int" + } + } + }, + "@secondsFormat": { + "placeholders": { + "count": { + "type": "int" + } + } + }, + "hoursFormat": "{count} HRS", + "minutesFormat": "{count} MIN", + "secondsFormat": "{count} SEC", + "noStatsFound": "No stats found", + "mostPlayed": "MOST PLAYED", + "totalKillsLabel": "TOTAL KILLS", + "totalDeathsLabel": "TOTAL DEATHS", + "kdRatioLabel": "K/D RATIO", + "assistsLabel": "ASSISTS", + "damageDoneLabel": "DAMAGE DONE", + "suicidesLabel": "SUICIDES", + "gamesWonLabel": "GAMES WON", + "gamesLostLabel": "GAMES LOST", + "winRateLabel": "WIN RATE", + "unitsSection": "UNITS", + "vehiclesSection": "VEHICLES", + "starfighterSection": "STARFIGHTER", + "serverSectionTitle": "SERVER", + "maxPlayersLabel": "MAX PLAYERS", + "maxSpectatorsLabel": "Max Spectators", + "privacySectionTitle": "Privacy", + "passwordLabel": "Password", + "passwordPlaceholder": "EXAMPLE", + "modeHeroesVersusVillains": "Heroes Versus Villains", + "modeGalacticAssault": "Galactic Assault", + "modeSupremacy": "Supremacy", + "modeCoOpAttack": "CO-OP Attack", + "modeCoOpDefend": "CO-OP Defend", + "modeStrike": "Strike", + "modeExtraction": "Extraction", + "modeBlast": "Blast", + "modeEwokHunt": "Ewok Hunt", + "modeJetpackCargo": "Jetpack Cargo", + "modeStarfighterAssault": "Starfighter Assault", + "modeHeroStarfighters": "Hero Starfighters", + "modeHeroShowdown": "Hero Showdown", + "mapTatooineMosEisley": "Tatooine - Mos Eisley", + "mapTatooineJabbasPalace": "Tatooine - Jabba's Palace", + "mapRepublicVenator": "Republic Venator", + "mapSeparatistDreadnought": "Separatist Dreadnought", + "mapResurgentStarDestroyer": "Resurgent Star Destroyer", + "mapMc85StarCruiser": "MC85 Star Cruiser", + "mapNabooPalaceHangar": "Naboo - Palace Hangar", + "mapEndorResearchStation9": "Endor - Research Station 9", + "mapEndorEwokVillage": "Endor - Ewok Village", + "mapNabooTheedPalace": "Naboo - Theed Palace", + "mapCrait": "Crait", + "activeRotation": "Active Rotation", + "importLabel": "Import", + "importMapRotation": "Import Map Rotation", + "invalidFileFormat": "Invalid file format", + "mapRotationImported": "Map rotation imported", + "noMapsActive": "No maps active", + "customMapNotFoundTooltip": "Custom map not found, this can happen if the map is not part of the current mod collection.\nThis map will be ignored when starting the server.", + "notFound": "Not found" +} \ No newline at end of file diff --git a/Launcher/lib/l10n/app_nl.arb b/Launcher/lib/l10n/app_nl.arb new file mode 100644 index 00000000..1536fce6 --- /dev/null +++ b/Launcher/lib/l10n/app_nl.arb @@ -0,0 +1,643 @@ +{ + "language": "Language", + "accessibility": "ACCESSIBILITY", + "colorblindProfiles": "COLORBLIND PROFILES", + "defaultColor": "DEFAULT", + "protanomaly": "PROTANOMALY", + "deuteranomaly": "DEUTERANOMALY", + "tritanomaly": "TRITANOMALY", + "resetSettings": "RESET SETTINGS", + "reset": "Reset", + "rememberWindowPosition": "Remember Window Position", + "customization": "CUSTOMIZATION", + "patreonExclusive": "PATREON EXCLUSIVE", + "changeHighlightColor": "CHANGE HIGHLIGHT COLOR", + "change": "Change", + "changeBackground": "CHANGE BACKGROUND", + "save": "Save", + "cancel": "Cancel", + "changeColor": "CHANGE COLOR", + "accounts": "ACCOUNTS", + "proxy": "PROXY", + "unavailable": "UNAVAILABLE", + "resetKyberToken": "Reset Kyber Token", + "nexusMods": "NexusMods", + "logout": "Logout", + "login": "Login", + "eaLogout": "EA Logout", + "linkedAccounts": "LINKED ACCOUNTS", + "discord": "Discord", + "connect": "Connect", + "discordUnlink": "{name} | Unlink", + "connectPatreon": "Connect Patreon", + "updates": "UPDATES", + "automaticallyUpdate": "Automatically Update", + "releaseChannel": "Release Channel", + "select": "Select", + "forceUpdate": "Force update", + "updateNow": "UPDATE NOW", + "other": "OTHER", + "licenses": "Licenses", + "showLicenses": "Show Licenses", + "developerMode": "Developer Mode", + "environment": "Environment", + "apiEnvironment": "API Environment", + "setNexusApiToken": "Set Nexus API Token", + "setToken": "Set token", + "setBackgroundImage": "Set Background Image", + "setImage": "Set Image", + "dummyServer": "Dummy Server", + "removeBackground": "Remove Background", + "copyKyberToken": "Copy Kyber Token", + "copy": "Copy", + "activity": "ACTIVITY", + "discordRichPresence": "Discord Rich Presence", + "loggingAndSentry": "Logging & Sentry", + "logs": "Logs", + "exportLogs": "Export Logs", + "optInToSentry": "Opt In To Sentry", + "optOutOfSentry": "Opt Out Of Sentry", + "optIn": "Opt In", + "optOut": "Opt Out", + "launcherDebugMode": "Launcher Debug Mode", + "moduleDebugMode": "Module Debug Mode", + "moduleRpcDebugMode": "Module RPC Debug Mode", + "mods": "MODS", + "changeModDirectory": "Change Mod Directory", + "newDirectory": "New Directory", + "frostyConverter": "Frosty Converter", + "convertYourPacks": "Convert your Packs", + "kyberPreloadedMods": "Kyber Preloaded Mods", + "ingame": "INGAME", + "ingameHotkey": "Ingame Hotkey (Moderation Menu)", + "restartGameWarning": "To apply changes, restart the game", + "enabled": "Enabled", + "disabled": "Disabled", + "proximityChat": "Proximity Chat", + "inputMode": "Input Mode", + "openMic": "Open Mic", + "pushToTalk": "Push to Talk", + "pushToTalkKey": "Push to Talk Key", + "inputVolume": "Input Volume", + "outputVolume": "Output Volume", + "inputDevice": "Input Device", + "outputDevice": "Output Device", + "noDevicesFound": "No devices found", + "selectBackground": "SELECT A BACKGROUND", + "chooseBackgroundDesc": "CHOOSE FROM 8 BACKGROUNDS OR SELECT A CUSTOM BACKGROUND", + "customBackground": "CUSTOM BACKGROUND", + "selectImageDesc": "Select a background image for the launcher", + "minResolutionDesc": "The image resolution should be at least 1920x1080", + "selectImage": "Select Image", + "fileTooLarge": "The file is too large. Please select a file smaller than 10MB", + "resolutionTooLow": "The image resolution is too low. Please select an image with a resolution of at least 1920x1080", + "copyingImage": "Copying image...", + "selectImagePickerTitle": "Select a background image", + "langAccTitle": "LANGUAGE & ACCESSIBILITY", + "langAccDesc": "CHANGE LANGUAGE, PROXY & ACCESSIBILITY SETTINGS", + "modConfigTitle": "MOD CONFIGURATION", + "modConfigDesc": "CONFIGURE MODS SETTINGS, IMPORT FROM FROSTY & MORE", + "creditsTitle": "CREDITS LIST", + "creditsDesc": "VIEW DEVELOPERS, CONTRIBUTORS & PATREON SUPPORTERS", + "ingameSettingsTitle": "INGAME SETTINGS", + "proximityChatDesc": "CONFIGURE PROXIMITY CHAT SETTINGS", + "logsActivityTitle": "LOGS & ACTIVITY", + "logsActivityDesc": "VIEW ACTIVITY & DEBUG LOGGING SETTINGS", + "accountsUpdatesTitle": "ACCOUNTS & UPDATES", + "accountsUpdatesDesc": "LOGOUT, UPDATE SETTINGS & MORE", + "versionText": "VERSION: {version}#CL{build}", + "downloadManager": "DOWNLOAD MANAGER", + "pausedDownloads": "PAUSED DOWNLOADS", + "unCapSpeedPrefix": "UN-CAP DOWNLOAD SPEEDS WITH ", + "nexusModsPremium": "NEXUS MODS PREMIUM", + "downloadProgress": " / {total} ({speed}/s) {percentage}%", + "@downloadProgress": { + "placeholders": { + "total": { "type": "String" }, + "speed": { "type": "String" }, + "percentage": { "type": "int" } + } + }, + "loadMods": "LOAD MODS", + "export": "EXPORT", + "selectMods": "Select mods", + "ignoringDuplicate": "Ignoring {filename}. (Duplicate)", + "@ignoringDuplicate": { + "placeholders": { + "filename": { "type": "String" } + } + }, + "saveCollection": "Save collection", + "icon": "ICON", + "name": "Name", + "author": "Author", + "version": "Version", + "category": "Category", + "exportCollection": "EXPORT COLLECTION", + "exportingCollection": "Exporting collection...", + "selectIcon": "Select icon", + "eaLogin": "EA Login", + "loading": "Loading...", + "waitingForResponse": "Waiting for response...", + "maximaStarting": "Maxima is starting...", + "fetchingData": "Fetching data...", + "genericError": "An error occurred", + "loginFailed": "Login failed", + "userDoesNotExist": "The specified user does not exist", + "passwordIncorrect": "The specified password is incorrect", + "failedToFindAuthCode": "Failed to find auth code. Please try another browser", + "pathCopied": "The path has been copied to your clipboard", + "noAuthCodeReceived": "No authorization code was received", + "authorizationSuccessful": "Authorization successful", + "patreonAuthorizedMessage": "You have been successfully authorized as a Patreon member", + "authorizationFailed": "Authorization failed", + "maximaIntroText": "In order to use this launcher, you need to login to Maxima. This is required to launch and interact with Battlefront 2.\nYou will be redirected to the EA login page and after logging in, you will be redirected back to the launcher.", + "loginWithEa": "Login with EA", + "eaIdDisplay": "EA-ID: {eaId}", + "logout": "Log out", + "authorizePatreon": "Authorize Patreon", + "whitelistConfirmation": "Are you sure you want to add your current account to the whitelist?", + "currentAccountDisplay": "Current account: {displayName}", + "addToWhitelist": "Add to whitelist", + "gameNotOwnedMessage": "It seems like you do not own Battlefront 2. Please purchase the game or use another account.", + "maximaBackgroundServiceError": "Maxima failed to start the background service. This is usually caused by an antivirus program blocking the service. Please add an exception for the launcher and reinstall it.", + "maximaMissingFilesError": "Some files required to run Maxima are missing. This is usually caused by an antivirus program deleting the files. Please add an exception for the launcher and reinstall it.", + "antivirusExclusionInstruction": "Please exclude the following folders from your antivirus' real-time protection:", + "copyPath": "COPY PATH", + "debugLogsToggled": "{status} debug logs", + "signInNexusPrompt": "Please sign in to Nexus Mods to view mods", + "noModsFound": "No mods found", + "unknownError": "Unknown error", + "noDataFound": "No data found", + "modInformation": "MOD INFORMATION", + "viewModFilesShareEndorse": "VIEW MOD FILES, SHARE AND ENDORSE", + "files": "FILES", + "posts": "POSTS", + "endorseMustDownload": "You must download the mod before endorsing it", + "endorseWaitTime": "You must wait some time after downloading the mod before endorsing it", + "endorseLoginRequired": "You must be logged in to endorse mods", + "linkCopied": "Link copied to clipboard", + "updatedAt": "Updated: ", + "mainFiles": "MAIN FILES", + "miscellaneousFiles": "MISCELLANEOUS FILES", + "optionalFiles": "OPTIONAL FILES", + "archivedFiles": "ARCHIVED FILES", + "fileListTitle": "{title} ({count})", + "@fileListTitle": { + "placeholders": { + "title": { "type": "String" }, + "count": { "type": "int" } + } + }, + "verifiedModAuthor": "Verified Mod Author", + "viewingProfile": "VIEWING PROFILE", + "publishedMods": "PUBLISHED MODS", + "userInformation": "USER INFORMATION", + "viewUserStats": "VIEW USER STATS", + "joinedLabel": "Joined: ", + "modsLabel": "Mods: ", + "postsLabel": "Posts: ", + "uniqueDlsLabel": "Unique-DLs: ", + "viewsLabel": "Views: ", + "kudosLabel": "Kudos: ", + "contributedLabel": "Contributed: ", + "viewOnNexusMods": "VIEW ON NEXUS MODS", + "collectionMods": "Collection Mods", + "modNameVersion": "{name} ({version})", + "@modNameVersion": { + "placeholders": { + "name": { "type": "String" }, + "version": { "type": "String" } + } + }, + "downloadCollectionMods": "DOWNLOAD COLLECTION MODS", + "loadingMods": "LOADING MODS...", + "collections": "COLLECTIONS", + "manageCollectionsSub": "MANAGE MOD COLLECTIONS & CURATE COSMETIC MODS", + "browser": "Browser", + "paginationInfo": "{page}/{totalPages}", + "@paginationInfo": { + "placeholders": { + "page": { "type": "int" }, + "totalPages": { "type": "int" } + } + }, + "modScope": "MOD SCOPE", + "serverNotFound": "The server you were playing on could not be found.", + "nexusModsAuthorization": "NexusMods Authorization", + "nexusAuthDescription": "You need to authorize KyberLauncher on NexusMods to use the mod browser.", + "authorizeKyber": "Authorize Kyber", + "noReportsAvailable": "No reports available", + "reportedBy": "Reported by ", + "forLabel": " for ", + "onLabel": " on ", + "descriptionLabel": "Description", + "evidenceCountLabel": "Evidence ({count})", + "@evidenceCountLabel": { + "placeholders": { + "count": { "type": "int" } + } + }, + "reportViewTitle": "REPORT VIEW", + "viewReportsAndEvidence": "VIEW REPORTS AND EVIDENCE", + "reportsTab": "REPORTS", + "punishmentsTab": "PUNISHMENTS", + "reportRejected": "Report rejected", + "takeAction": "TAKE ACTION", + "reportedByPlayer": "By {name}", + "@reportedByPlayer": { + "placeholders": { + "name": { "type": "String" } + } + }, + "daysDuration": "{days} Days", + "@daysDuration": { + "placeholders": { + "days": { "type": "int" } + } + }, + "permanent": "Permanent", + "bannedByModerator": "Banned By {name}", + "@bannedByModerator": { + "placeholders": { + "name": { "type": "String" } + } + }, + "durationLabel": "Duration: {duration}", + "@durationLabel": { + "placeholders": { + "duration": { "type": "String" } + } + }, + "issuedLabel": "Issued: {date}", + "@issuedLabel": { + "placeholders": { + "date": { "type": "String" } + } + }, + "statusFilter": "STATUS", + "reportsTitle": "REPORTS", + "totalReportsHeader": "TOTAL REPORTS", + "latestReportHeader": "LATEST REPORT", + "latestReasonHeader": "LATEST REASON", + "statusHeader": "STATUS", + "errorLoadingReports": "Error loading reports: {message}", + "reasonWithCount": "{reasonName} ({count})", + "armsecCaseFile": "ARMSEC - CASE FILE", + "evidenceAndDataSummary": "EVIDENCE AND DATA SUMMARY", + "errorLoadingUser": "Error loading user", + "userNotFound": "User not found", + "kyberUser": "KYBER USER", + "open": "OPEN", + "options": "OPTIONS", + "@errorLoadingReports": { + "placeholders": { + "message": { "type": "String" } + } + }, + "@reasonWithCount": { + "placeholders": { + "reasonName": { "type": "String" }, + "count": { "type": "String" } + } + }, + "unknownServer": "Unknown Server", + "hostedBy": "Hosted by {creator}", + "@hostedBy": { + "placeholders": { + "creator": { "type": "String" } + } + }, + "backToServerBrowser": "Back to Server Browser", + "eventLog": "Event Log", + "lightSide": "Light Side", + "darkSide": "Dark Side", + "reportPlayer": "Report Player", + "region": "REGION", + "serverType": "SERVER TYPE", + "gameType": "GAME TYPE", + "gameMode": "GAME MODE", + "warning": "WARNING", + "warningMessageFallback": "Warning message not available.", + "newButton": "NEW", + "moderate": "MODERATE", + "manage": "MANAGE", + "rotation": "ROTATION", + "searchPlaceholder": "Search ...", + "noServersFound": "No servers found", + "serverBrowser": "Server Browser", + "optionsHeader": "OPTIONS", + "moderatorsHeader": "MODERATORS", + "bannedPlayersHeader": "BANNED PLAYERS", + "playButton": "PLAY", + "spectateButton": "SPECTATE", + "copyLinkButton": "COPY LINK", + "copiedToClipboard": "Copied to clipboard!", + "exportBans": "EXPORT BANS", + "importBans": "IMPORT BANS", + "kickAll": "KICK ALL", + "banAll": "BAN ALL", + "eventLogHeader": "Event Log", + "lightSideHeader": "Light Side", + "darkSideHeader": "Dark Side", + "userManager": "User Manager", + "consoleHint": "/COMMAND OR SEND MESSAGE", + "untilLabel": "UNTIL: {date}", + "reasonLabel": "REASON: {reason}", + "unban": "UNBAN", + "modify": "MODIFY", + "playerUnbanned": "Player unbanned", + "timeElapsed": "Time has elapsed", + "daysRemaining": "{count} day(s)", + "hoursRemaining": "{count} hour(s)", + "minutesRemaining": "{count} minute(s)", + "secondsRemaining": "{count} second(s)", + "remaining": "{time} remaining", + "promoteUserTooltip": "Promote User", + "errorOccurred": "An error occurred", + "swapPlayerTooltip": "Swap Player", + "kickPlayerTooltip": "Kick player", + "banPlayerTooltip": "Ban player", + "off": "OFF", + "on": "ON", + "home": "HOME", + "host": "HOST", + "stats": "STATS", + "settings": "SETTINGS", + "ingamePanel": "INGAME PANEL", + "extractingFileWithProgress": "EXTRACTING FILE ({extracted}/{total})", + "extractingFile": "EXTRACTING FILE", + "downloadingWithProgress": "DOWNLOADING ({percentage}% {downloaded}/{total})", + "downloadManager": "DOWNLOAD MANAGER", + "createNewCollection": "CREATE NEW COLLECTION", + "pageNotFoundTitle": "Page Not Found", + "pageNotFoundFallback": "page not found", + "goToHomePage": "Go to home page", + "newCollectionTitle": "New Collection", + "@extractingFileWithProgress": { + "placeholders": { + "extracted": { + "type": "String" + }, + "total": { + "type": "String" + } + } + }, + "@downloadingWithProgress": { + "placeholders": { + "percentage": { + "type": "String" + }, + "downloaded": { + "type": "String" + }, + "total": { + "type": "String" + } + } + }, + "@untilLabel": { + "placeholders": { + "date": { "type": "String" } + } + }, + "@reasonLabel": { + "placeholders": { + "reason": { "type": "String" } + } + }, + "@daysRemaining": { + "placeholders": { + "count": { "type": "int" } + } + }, + "@hoursRemaining": { + "placeholders": { + "count": { "type": "int" } + } + }, + "@minutesRemaining": { + "placeholders": { + "count": { "type": "int" } + } + }, + "@secondsRemaining": { + "placeholders": { + "count": { "type": "int" } + } + }, + "@remaining": { + "placeholders": { + "time": { "type": "String" } + } + }, + "eaAccount": "EA Account", + "finish": "FINISH", + "skip": "SKIP", + "searchFailed": "Failed to search players: {message}", + "unexpectedError": "An unexpected error occurred: {error}", + "searchKyberUser": "Search for a kyber user", + "noResultsFound": "No results found for \"{query}\".", + "friendsCount": "FRIENDS ({count})", + "recentlyViewed": "RECENTLY VIEWED", + "userSearchHistory": "USER SEARCH HISTORY", + "online": "Online", + "playingStatus": "Playing {status}", + "@searchFailed": { + "placeholders": { + "message": { "type": "String" } + } + }, + "@unexpectedError": { + "placeholders": { + "error": { "type": "String" } + } + }, + "@noResultsFound": { + "placeholders": { + "query": { "type": "String" } + } + }, + "@friendsCount": { + "placeholders": { + "count": { "type": "int" } + } + }, + "@playingStatus": { + "placeholders": { + "status": { "type": "String" } + } + }, + "selectCollectionIcon": "Select Collection Icon", + "collectionLabel": "COLLECTION", + "collectionDescription": "A COLLECTION IS A READY TO PLAY MOD LIST", + "modsCount": "{count} Mods", + "collectionNamePlaceholder": "Collection Name", + "saveButton": "SAVE", + "betterSabersPlugin": "BETTER SABERS (PLUGIN)", + "reloadingMods": "Reloading mods", + "generatedBetterSabers": "Generated BetterSabers", + "exportCollectionTar": "EXPORT COLLECTION TAR", + "editCollection": "EDIT COLLECTION", + "createACopy": "CREATE A COPY", + "collectionCopyName": "{title} (Copy)", + "deleteCollection": "DELETE COLLECTION", + "largeModsWarning": "Using multiple large or complex mods isn't recommended, as they often conflict with each other.", + "@modsCount": { + "placeholders": { + "count": { + "type": "int" + } + } + }, + "@collectionCopyName": { + "placeholders": { + "title": { + "type": "String" + } + } + }, + "myMods": "MY MODS", + "nameHeader": "NAME", + "categoryHeader": "CATEGORY", + "sizeHeader": "SIZE", + "typeHeader": "TYPE", + "addHeader": "ADD", + "selectedCount": "{count}", + "@selectedCount": { + "placeholders": { + "count": { + "type": "int" + } + } + }, + "all": "ALL", + "searchHint": "SEARCH...", + "noPlayersFound": "No players found", + "moveModsDirectory": "MOVE MODS DIRECTORY", + "invalidDirectoryWarning": "The current mods directory is invalid because it contains non-ASCII characters. ", + "selectNewDirectoryDesc": "Please select the new directory where you want to move the mods directory. Mods will not be moved. Only the directory will be changed.", + "nonAsciiWarning": "Non-ASCII characters in the path are not allowed (e.g. Korean, Greek, etc.).", + "adminPermissionError": "You can't select a directory that requires admin permissions. Please select another directory.", + "selectedDirNonAsciiError": "The selected directory contains non-ASCII characters. Please select a different directory.", + "browse": "Browse", + "resetToDefault": "Reset to default", + "defaultDirNonAsciiError": "The default mods directory contains non-ASCII characters. Please select a different directory.", + "modInformation": "MOD INFORMATION", + "viewInformationAboutMod": "VIEW INFORMATION ABOUT A MOD", + "modVersionAuthorDisplay": "{version} - {author}", + "@modVersionAuthorDisplay": { + "placeholders": { + "version": { + "type": "String" + }, + "author": { + "type": "String" + } + } + }, + "descriptionLabel": "DESCRIPTION", + "imagesLabel": "IMAGES", + "affectedFilesLabel": "AFFECTED FILES", + "unknownResourceName": "Unknown resource name", + "abortingNexusLogin": "Aborting NexusMods login", + "installWebViewError": "Please install WebView to use this feature.", + "continueText": "CONTINUE", + "wait": "WAIT", + "win7CompatibilityDetected": "Windows 7 Compatibility Mode detected", + "disableWin7CompMode": "To login to NexusMods, please disable Windows 7 Compatibility Mode for Kyber Launcher.", + "reEnableWin7CompMode": "After you have logged in, you need to re-enable Windows 7 Compatibility Mode.", + "checkingWebView": "Checking WebView installation...", + "nexusLoginIntro": "To continue, you will need to log in with your NexusMods account in the browser that is about to open.", + "nexusLoginDataNotice": "The data is processed locally and will only be sent to Nexusmods.\nYou can also enable/disable this feature later in the settings menu.", + "playersHeader": "PLAYERS", + "eventsAndAnnouncements": "EVENTS & ANNOUNCEMENTS", + "viewUpcomingEventsDesc": "VIEW UPCOMING EVENTS & RECEIVE ANNOUNCEMENTS", + "sortByTitle": "SORT BY", + "timeFilterTitle": "TIME FILTER", + "itemsPerPageTitle": "ITEMS PER PAGE", + "allTime": "All time", + "oneYear": "1 year", + "oneMonth": "1 month", + "twoWeeks": "2 weeks", + "oneWeek": "1 week", + "oneDay": "1 day", + "all": "ALL", + "@hoursFormat": { + "placeholders": { + "count": { + "type": "String" + } + } + }, + "@minutesFormat": { + "placeholders": { + "count": { + "type": "int" + } + } + }, + "@secondsFormat": { + "placeholders": { + "count": { + "type": "int" + } + } + }, + "hoursFormat": "{count} HRS", + "minutesFormat": "{count} MIN", + "secondsFormat": "{count} SEC", + "noStatsFound": "No stats found", + "mostPlayed": "MOST PLAYED", + "totalKillsLabel": "TOTAL KILLS", + "totalDeathsLabel": "TOTAL DEATHS", + "kdRatioLabel": "K/D RATIO", + "assistsLabel": "ASSISTS", + "damageDoneLabel": "DAMAGE DONE", + "suicidesLabel": "SUICIDES", + "gamesWonLabel": "GAMES WON", + "gamesLostLabel": "GAMES LOST", + "winRateLabel": "WIN RATE", + "unitsSection": "UNITS", + "vehiclesSection": "VEHICLES", + "starfighterSection": "STARFIGHTER", + "serverSectionTitle": "SERVER", + "maxPlayersLabel": "MAX PLAYERS", + "maxSpectatorsLabel": "Max Spectators", + "privacySectionTitle": "Privacy", + "passwordLabel": "Password", + "passwordPlaceholder": "EXAMPLE", + "modeHeroesVersusVillains": "Heroes Versus Villains", + "modeGalacticAssault": "Galactic Assault", + "modeSupremacy": "Supremacy", + "modeCoOpAttack": "CO-OP Attack", + "modeCoOpDefend": "CO-OP Defend", + "modeStrike": "Strike", + "modeExtraction": "Extraction", + "modeBlast": "Blast", + "modeEwokHunt": "Ewok Hunt", + "modeJetpackCargo": "Jetpack Cargo", + "modeStarfighterAssault": "Starfighter Assault", + "modeHeroStarfighters": "Hero Starfighters", + "modeHeroShowdown": "Hero Showdown", + "mapTatooineMosEisley": "Tatooine - Mos Eisley", + "mapTatooineJabbasPalace": "Tatooine - Jabba's Palace", + "mapRepublicVenator": "Republic Venator", + "mapSeparatistDreadnought": "Separatist Dreadnought", + "mapResurgentStarDestroyer": "Resurgent Star Destroyer", + "mapMc85StarCruiser": "MC85 Star Cruiser", + "mapNabooPalaceHangar": "Naboo - Palace Hangar", + "mapEndorResearchStation9": "Endor - Research Station 9", + "mapEndorEwokVillage": "Endor - Ewok Village", + "mapNabooTheedPalace": "Naboo - Theed Palace", + "mapCrait": "Crait", + "activeRotation": "Active Rotation", + "importLabel": "Import", + "importMapRotation": "Import Map Rotation", + "invalidFileFormat": "Invalid file format", + "mapRotationImported": "Map rotation imported", + "noMapsActive": "No maps active", + "customMapNotFoundTooltip": "Custom map not found, this can happen if the map is not part of the current mod collection.\nThis map will be ignored when starting the server.", + "notFound": "Not found" +} \ No newline at end of file diff --git a/Launcher/lib/l10n/app_pl.arb b/Launcher/lib/l10n/app_pl.arb new file mode 100644 index 00000000..1536fce6 --- /dev/null +++ b/Launcher/lib/l10n/app_pl.arb @@ -0,0 +1,643 @@ +{ + "language": "Language", + "accessibility": "ACCESSIBILITY", + "colorblindProfiles": "COLORBLIND PROFILES", + "defaultColor": "DEFAULT", + "protanomaly": "PROTANOMALY", + "deuteranomaly": "DEUTERANOMALY", + "tritanomaly": "TRITANOMALY", + "resetSettings": "RESET SETTINGS", + "reset": "Reset", + "rememberWindowPosition": "Remember Window Position", + "customization": "CUSTOMIZATION", + "patreonExclusive": "PATREON EXCLUSIVE", + "changeHighlightColor": "CHANGE HIGHLIGHT COLOR", + "change": "Change", + "changeBackground": "CHANGE BACKGROUND", + "save": "Save", + "cancel": "Cancel", + "changeColor": "CHANGE COLOR", + "accounts": "ACCOUNTS", + "proxy": "PROXY", + "unavailable": "UNAVAILABLE", + "resetKyberToken": "Reset Kyber Token", + "nexusMods": "NexusMods", + "logout": "Logout", + "login": "Login", + "eaLogout": "EA Logout", + "linkedAccounts": "LINKED ACCOUNTS", + "discord": "Discord", + "connect": "Connect", + "discordUnlink": "{name} | Unlink", + "connectPatreon": "Connect Patreon", + "updates": "UPDATES", + "automaticallyUpdate": "Automatically Update", + "releaseChannel": "Release Channel", + "select": "Select", + "forceUpdate": "Force update", + "updateNow": "UPDATE NOW", + "other": "OTHER", + "licenses": "Licenses", + "showLicenses": "Show Licenses", + "developerMode": "Developer Mode", + "environment": "Environment", + "apiEnvironment": "API Environment", + "setNexusApiToken": "Set Nexus API Token", + "setToken": "Set token", + "setBackgroundImage": "Set Background Image", + "setImage": "Set Image", + "dummyServer": "Dummy Server", + "removeBackground": "Remove Background", + "copyKyberToken": "Copy Kyber Token", + "copy": "Copy", + "activity": "ACTIVITY", + "discordRichPresence": "Discord Rich Presence", + "loggingAndSentry": "Logging & Sentry", + "logs": "Logs", + "exportLogs": "Export Logs", + "optInToSentry": "Opt In To Sentry", + "optOutOfSentry": "Opt Out Of Sentry", + "optIn": "Opt In", + "optOut": "Opt Out", + "launcherDebugMode": "Launcher Debug Mode", + "moduleDebugMode": "Module Debug Mode", + "moduleRpcDebugMode": "Module RPC Debug Mode", + "mods": "MODS", + "changeModDirectory": "Change Mod Directory", + "newDirectory": "New Directory", + "frostyConverter": "Frosty Converter", + "convertYourPacks": "Convert your Packs", + "kyberPreloadedMods": "Kyber Preloaded Mods", + "ingame": "INGAME", + "ingameHotkey": "Ingame Hotkey (Moderation Menu)", + "restartGameWarning": "To apply changes, restart the game", + "enabled": "Enabled", + "disabled": "Disabled", + "proximityChat": "Proximity Chat", + "inputMode": "Input Mode", + "openMic": "Open Mic", + "pushToTalk": "Push to Talk", + "pushToTalkKey": "Push to Talk Key", + "inputVolume": "Input Volume", + "outputVolume": "Output Volume", + "inputDevice": "Input Device", + "outputDevice": "Output Device", + "noDevicesFound": "No devices found", + "selectBackground": "SELECT A BACKGROUND", + "chooseBackgroundDesc": "CHOOSE FROM 8 BACKGROUNDS OR SELECT A CUSTOM BACKGROUND", + "customBackground": "CUSTOM BACKGROUND", + "selectImageDesc": "Select a background image for the launcher", + "minResolutionDesc": "The image resolution should be at least 1920x1080", + "selectImage": "Select Image", + "fileTooLarge": "The file is too large. Please select a file smaller than 10MB", + "resolutionTooLow": "The image resolution is too low. Please select an image with a resolution of at least 1920x1080", + "copyingImage": "Copying image...", + "selectImagePickerTitle": "Select a background image", + "langAccTitle": "LANGUAGE & ACCESSIBILITY", + "langAccDesc": "CHANGE LANGUAGE, PROXY & ACCESSIBILITY SETTINGS", + "modConfigTitle": "MOD CONFIGURATION", + "modConfigDesc": "CONFIGURE MODS SETTINGS, IMPORT FROM FROSTY & MORE", + "creditsTitle": "CREDITS LIST", + "creditsDesc": "VIEW DEVELOPERS, CONTRIBUTORS & PATREON SUPPORTERS", + "ingameSettingsTitle": "INGAME SETTINGS", + "proximityChatDesc": "CONFIGURE PROXIMITY CHAT SETTINGS", + "logsActivityTitle": "LOGS & ACTIVITY", + "logsActivityDesc": "VIEW ACTIVITY & DEBUG LOGGING SETTINGS", + "accountsUpdatesTitle": "ACCOUNTS & UPDATES", + "accountsUpdatesDesc": "LOGOUT, UPDATE SETTINGS & MORE", + "versionText": "VERSION: {version}#CL{build}", + "downloadManager": "DOWNLOAD MANAGER", + "pausedDownloads": "PAUSED DOWNLOADS", + "unCapSpeedPrefix": "UN-CAP DOWNLOAD SPEEDS WITH ", + "nexusModsPremium": "NEXUS MODS PREMIUM", + "downloadProgress": " / {total} ({speed}/s) {percentage}%", + "@downloadProgress": { + "placeholders": { + "total": { "type": "String" }, + "speed": { "type": "String" }, + "percentage": { "type": "int" } + } + }, + "loadMods": "LOAD MODS", + "export": "EXPORT", + "selectMods": "Select mods", + "ignoringDuplicate": "Ignoring {filename}. (Duplicate)", + "@ignoringDuplicate": { + "placeholders": { + "filename": { "type": "String" } + } + }, + "saveCollection": "Save collection", + "icon": "ICON", + "name": "Name", + "author": "Author", + "version": "Version", + "category": "Category", + "exportCollection": "EXPORT COLLECTION", + "exportingCollection": "Exporting collection...", + "selectIcon": "Select icon", + "eaLogin": "EA Login", + "loading": "Loading...", + "waitingForResponse": "Waiting for response...", + "maximaStarting": "Maxima is starting...", + "fetchingData": "Fetching data...", + "genericError": "An error occurred", + "loginFailed": "Login failed", + "userDoesNotExist": "The specified user does not exist", + "passwordIncorrect": "The specified password is incorrect", + "failedToFindAuthCode": "Failed to find auth code. Please try another browser", + "pathCopied": "The path has been copied to your clipboard", + "noAuthCodeReceived": "No authorization code was received", + "authorizationSuccessful": "Authorization successful", + "patreonAuthorizedMessage": "You have been successfully authorized as a Patreon member", + "authorizationFailed": "Authorization failed", + "maximaIntroText": "In order to use this launcher, you need to login to Maxima. This is required to launch and interact with Battlefront 2.\nYou will be redirected to the EA login page and after logging in, you will be redirected back to the launcher.", + "loginWithEa": "Login with EA", + "eaIdDisplay": "EA-ID: {eaId}", + "logout": "Log out", + "authorizePatreon": "Authorize Patreon", + "whitelistConfirmation": "Are you sure you want to add your current account to the whitelist?", + "currentAccountDisplay": "Current account: {displayName}", + "addToWhitelist": "Add to whitelist", + "gameNotOwnedMessage": "It seems like you do not own Battlefront 2. Please purchase the game or use another account.", + "maximaBackgroundServiceError": "Maxima failed to start the background service. This is usually caused by an antivirus program blocking the service. Please add an exception for the launcher and reinstall it.", + "maximaMissingFilesError": "Some files required to run Maxima are missing. This is usually caused by an antivirus program deleting the files. Please add an exception for the launcher and reinstall it.", + "antivirusExclusionInstruction": "Please exclude the following folders from your antivirus' real-time protection:", + "copyPath": "COPY PATH", + "debugLogsToggled": "{status} debug logs", + "signInNexusPrompt": "Please sign in to Nexus Mods to view mods", + "noModsFound": "No mods found", + "unknownError": "Unknown error", + "noDataFound": "No data found", + "modInformation": "MOD INFORMATION", + "viewModFilesShareEndorse": "VIEW MOD FILES, SHARE AND ENDORSE", + "files": "FILES", + "posts": "POSTS", + "endorseMustDownload": "You must download the mod before endorsing it", + "endorseWaitTime": "You must wait some time after downloading the mod before endorsing it", + "endorseLoginRequired": "You must be logged in to endorse mods", + "linkCopied": "Link copied to clipboard", + "updatedAt": "Updated: ", + "mainFiles": "MAIN FILES", + "miscellaneousFiles": "MISCELLANEOUS FILES", + "optionalFiles": "OPTIONAL FILES", + "archivedFiles": "ARCHIVED FILES", + "fileListTitle": "{title} ({count})", + "@fileListTitle": { + "placeholders": { + "title": { "type": "String" }, + "count": { "type": "int" } + } + }, + "verifiedModAuthor": "Verified Mod Author", + "viewingProfile": "VIEWING PROFILE", + "publishedMods": "PUBLISHED MODS", + "userInformation": "USER INFORMATION", + "viewUserStats": "VIEW USER STATS", + "joinedLabel": "Joined: ", + "modsLabel": "Mods: ", + "postsLabel": "Posts: ", + "uniqueDlsLabel": "Unique-DLs: ", + "viewsLabel": "Views: ", + "kudosLabel": "Kudos: ", + "contributedLabel": "Contributed: ", + "viewOnNexusMods": "VIEW ON NEXUS MODS", + "collectionMods": "Collection Mods", + "modNameVersion": "{name} ({version})", + "@modNameVersion": { + "placeholders": { + "name": { "type": "String" }, + "version": { "type": "String" } + } + }, + "downloadCollectionMods": "DOWNLOAD COLLECTION MODS", + "loadingMods": "LOADING MODS...", + "collections": "COLLECTIONS", + "manageCollectionsSub": "MANAGE MOD COLLECTIONS & CURATE COSMETIC MODS", + "browser": "Browser", + "paginationInfo": "{page}/{totalPages}", + "@paginationInfo": { + "placeholders": { + "page": { "type": "int" }, + "totalPages": { "type": "int" } + } + }, + "modScope": "MOD SCOPE", + "serverNotFound": "The server you were playing on could not be found.", + "nexusModsAuthorization": "NexusMods Authorization", + "nexusAuthDescription": "You need to authorize KyberLauncher on NexusMods to use the mod browser.", + "authorizeKyber": "Authorize Kyber", + "noReportsAvailable": "No reports available", + "reportedBy": "Reported by ", + "forLabel": " for ", + "onLabel": " on ", + "descriptionLabel": "Description", + "evidenceCountLabel": "Evidence ({count})", + "@evidenceCountLabel": { + "placeholders": { + "count": { "type": "int" } + } + }, + "reportViewTitle": "REPORT VIEW", + "viewReportsAndEvidence": "VIEW REPORTS AND EVIDENCE", + "reportsTab": "REPORTS", + "punishmentsTab": "PUNISHMENTS", + "reportRejected": "Report rejected", + "takeAction": "TAKE ACTION", + "reportedByPlayer": "By {name}", + "@reportedByPlayer": { + "placeholders": { + "name": { "type": "String" } + } + }, + "daysDuration": "{days} Days", + "@daysDuration": { + "placeholders": { + "days": { "type": "int" } + } + }, + "permanent": "Permanent", + "bannedByModerator": "Banned By {name}", + "@bannedByModerator": { + "placeholders": { + "name": { "type": "String" } + } + }, + "durationLabel": "Duration: {duration}", + "@durationLabel": { + "placeholders": { + "duration": { "type": "String" } + } + }, + "issuedLabel": "Issued: {date}", + "@issuedLabel": { + "placeholders": { + "date": { "type": "String" } + } + }, + "statusFilter": "STATUS", + "reportsTitle": "REPORTS", + "totalReportsHeader": "TOTAL REPORTS", + "latestReportHeader": "LATEST REPORT", + "latestReasonHeader": "LATEST REASON", + "statusHeader": "STATUS", + "errorLoadingReports": "Error loading reports: {message}", + "reasonWithCount": "{reasonName} ({count})", + "armsecCaseFile": "ARMSEC - CASE FILE", + "evidenceAndDataSummary": "EVIDENCE AND DATA SUMMARY", + "errorLoadingUser": "Error loading user", + "userNotFound": "User not found", + "kyberUser": "KYBER USER", + "open": "OPEN", + "options": "OPTIONS", + "@errorLoadingReports": { + "placeholders": { + "message": { "type": "String" } + } + }, + "@reasonWithCount": { + "placeholders": { + "reasonName": { "type": "String" }, + "count": { "type": "String" } + } + }, + "unknownServer": "Unknown Server", + "hostedBy": "Hosted by {creator}", + "@hostedBy": { + "placeholders": { + "creator": { "type": "String" } + } + }, + "backToServerBrowser": "Back to Server Browser", + "eventLog": "Event Log", + "lightSide": "Light Side", + "darkSide": "Dark Side", + "reportPlayer": "Report Player", + "region": "REGION", + "serverType": "SERVER TYPE", + "gameType": "GAME TYPE", + "gameMode": "GAME MODE", + "warning": "WARNING", + "warningMessageFallback": "Warning message not available.", + "newButton": "NEW", + "moderate": "MODERATE", + "manage": "MANAGE", + "rotation": "ROTATION", + "searchPlaceholder": "Search ...", + "noServersFound": "No servers found", + "serverBrowser": "Server Browser", + "optionsHeader": "OPTIONS", + "moderatorsHeader": "MODERATORS", + "bannedPlayersHeader": "BANNED PLAYERS", + "playButton": "PLAY", + "spectateButton": "SPECTATE", + "copyLinkButton": "COPY LINK", + "copiedToClipboard": "Copied to clipboard!", + "exportBans": "EXPORT BANS", + "importBans": "IMPORT BANS", + "kickAll": "KICK ALL", + "banAll": "BAN ALL", + "eventLogHeader": "Event Log", + "lightSideHeader": "Light Side", + "darkSideHeader": "Dark Side", + "userManager": "User Manager", + "consoleHint": "/COMMAND OR SEND MESSAGE", + "untilLabel": "UNTIL: {date}", + "reasonLabel": "REASON: {reason}", + "unban": "UNBAN", + "modify": "MODIFY", + "playerUnbanned": "Player unbanned", + "timeElapsed": "Time has elapsed", + "daysRemaining": "{count} day(s)", + "hoursRemaining": "{count} hour(s)", + "minutesRemaining": "{count} minute(s)", + "secondsRemaining": "{count} second(s)", + "remaining": "{time} remaining", + "promoteUserTooltip": "Promote User", + "errorOccurred": "An error occurred", + "swapPlayerTooltip": "Swap Player", + "kickPlayerTooltip": "Kick player", + "banPlayerTooltip": "Ban player", + "off": "OFF", + "on": "ON", + "home": "HOME", + "host": "HOST", + "stats": "STATS", + "settings": "SETTINGS", + "ingamePanel": "INGAME PANEL", + "extractingFileWithProgress": "EXTRACTING FILE ({extracted}/{total})", + "extractingFile": "EXTRACTING FILE", + "downloadingWithProgress": "DOWNLOADING ({percentage}% {downloaded}/{total})", + "downloadManager": "DOWNLOAD MANAGER", + "createNewCollection": "CREATE NEW COLLECTION", + "pageNotFoundTitle": "Page Not Found", + "pageNotFoundFallback": "page not found", + "goToHomePage": "Go to home page", + "newCollectionTitle": "New Collection", + "@extractingFileWithProgress": { + "placeholders": { + "extracted": { + "type": "String" + }, + "total": { + "type": "String" + } + } + }, + "@downloadingWithProgress": { + "placeholders": { + "percentage": { + "type": "String" + }, + "downloaded": { + "type": "String" + }, + "total": { + "type": "String" + } + } + }, + "@untilLabel": { + "placeholders": { + "date": { "type": "String" } + } + }, + "@reasonLabel": { + "placeholders": { + "reason": { "type": "String" } + } + }, + "@daysRemaining": { + "placeholders": { + "count": { "type": "int" } + } + }, + "@hoursRemaining": { + "placeholders": { + "count": { "type": "int" } + } + }, + "@minutesRemaining": { + "placeholders": { + "count": { "type": "int" } + } + }, + "@secondsRemaining": { + "placeholders": { + "count": { "type": "int" } + } + }, + "@remaining": { + "placeholders": { + "time": { "type": "String" } + } + }, + "eaAccount": "EA Account", + "finish": "FINISH", + "skip": "SKIP", + "searchFailed": "Failed to search players: {message}", + "unexpectedError": "An unexpected error occurred: {error}", + "searchKyberUser": "Search for a kyber user", + "noResultsFound": "No results found for \"{query}\".", + "friendsCount": "FRIENDS ({count})", + "recentlyViewed": "RECENTLY VIEWED", + "userSearchHistory": "USER SEARCH HISTORY", + "online": "Online", + "playingStatus": "Playing {status}", + "@searchFailed": { + "placeholders": { + "message": { "type": "String" } + } + }, + "@unexpectedError": { + "placeholders": { + "error": { "type": "String" } + } + }, + "@noResultsFound": { + "placeholders": { + "query": { "type": "String" } + } + }, + "@friendsCount": { + "placeholders": { + "count": { "type": "int" } + } + }, + "@playingStatus": { + "placeholders": { + "status": { "type": "String" } + } + }, + "selectCollectionIcon": "Select Collection Icon", + "collectionLabel": "COLLECTION", + "collectionDescription": "A COLLECTION IS A READY TO PLAY MOD LIST", + "modsCount": "{count} Mods", + "collectionNamePlaceholder": "Collection Name", + "saveButton": "SAVE", + "betterSabersPlugin": "BETTER SABERS (PLUGIN)", + "reloadingMods": "Reloading mods", + "generatedBetterSabers": "Generated BetterSabers", + "exportCollectionTar": "EXPORT COLLECTION TAR", + "editCollection": "EDIT COLLECTION", + "createACopy": "CREATE A COPY", + "collectionCopyName": "{title} (Copy)", + "deleteCollection": "DELETE COLLECTION", + "largeModsWarning": "Using multiple large or complex mods isn't recommended, as they often conflict with each other.", + "@modsCount": { + "placeholders": { + "count": { + "type": "int" + } + } + }, + "@collectionCopyName": { + "placeholders": { + "title": { + "type": "String" + } + } + }, + "myMods": "MY MODS", + "nameHeader": "NAME", + "categoryHeader": "CATEGORY", + "sizeHeader": "SIZE", + "typeHeader": "TYPE", + "addHeader": "ADD", + "selectedCount": "{count}", + "@selectedCount": { + "placeholders": { + "count": { + "type": "int" + } + } + }, + "all": "ALL", + "searchHint": "SEARCH...", + "noPlayersFound": "No players found", + "moveModsDirectory": "MOVE MODS DIRECTORY", + "invalidDirectoryWarning": "The current mods directory is invalid because it contains non-ASCII characters. ", + "selectNewDirectoryDesc": "Please select the new directory where you want to move the mods directory. Mods will not be moved. Only the directory will be changed.", + "nonAsciiWarning": "Non-ASCII characters in the path are not allowed (e.g. Korean, Greek, etc.).", + "adminPermissionError": "You can't select a directory that requires admin permissions. Please select another directory.", + "selectedDirNonAsciiError": "The selected directory contains non-ASCII characters. Please select a different directory.", + "browse": "Browse", + "resetToDefault": "Reset to default", + "defaultDirNonAsciiError": "The default mods directory contains non-ASCII characters. Please select a different directory.", + "modInformation": "MOD INFORMATION", + "viewInformationAboutMod": "VIEW INFORMATION ABOUT A MOD", + "modVersionAuthorDisplay": "{version} - {author}", + "@modVersionAuthorDisplay": { + "placeholders": { + "version": { + "type": "String" + }, + "author": { + "type": "String" + } + } + }, + "descriptionLabel": "DESCRIPTION", + "imagesLabel": "IMAGES", + "affectedFilesLabel": "AFFECTED FILES", + "unknownResourceName": "Unknown resource name", + "abortingNexusLogin": "Aborting NexusMods login", + "installWebViewError": "Please install WebView to use this feature.", + "continueText": "CONTINUE", + "wait": "WAIT", + "win7CompatibilityDetected": "Windows 7 Compatibility Mode detected", + "disableWin7CompMode": "To login to NexusMods, please disable Windows 7 Compatibility Mode for Kyber Launcher.", + "reEnableWin7CompMode": "After you have logged in, you need to re-enable Windows 7 Compatibility Mode.", + "checkingWebView": "Checking WebView installation...", + "nexusLoginIntro": "To continue, you will need to log in with your NexusMods account in the browser that is about to open.", + "nexusLoginDataNotice": "The data is processed locally and will only be sent to Nexusmods.\nYou can also enable/disable this feature later in the settings menu.", + "playersHeader": "PLAYERS", + "eventsAndAnnouncements": "EVENTS & ANNOUNCEMENTS", + "viewUpcomingEventsDesc": "VIEW UPCOMING EVENTS & RECEIVE ANNOUNCEMENTS", + "sortByTitle": "SORT BY", + "timeFilterTitle": "TIME FILTER", + "itemsPerPageTitle": "ITEMS PER PAGE", + "allTime": "All time", + "oneYear": "1 year", + "oneMonth": "1 month", + "twoWeeks": "2 weeks", + "oneWeek": "1 week", + "oneDay": "1 day", + "all": "ALL", + "@hoursFormat": { + "placeholders": { + "count": { + "type": "String" + } + } + }, + "@minutesFormat": { + "placeholders": { + "count": { + "type": "int" + } + } + }, + "@secondsFormat": { + "placeholders": { + "count": { + "type": "int" + } + } + }, + "hoursFormat": "{count} HRS", + "minutesFormat": "{count} MIN", + "secondsFormat": "{count} SEC", + "noStatsFound": "No stats found", + "mostPlayed": "MOST PLAYED", + "totalKillsLabel": "TOTAL KILLS", + "totalDeathsLabel": "TOTAL DEATHS", + "kdRatioLabel": "K/D RATIO", + "assistsLabel": "ASSISTS", + "damageDoneLabel": "DAMAGE DONE", + "suicidesLabel": "SUICIDES", + "gamesWonLabel": "GAMES WON", + "gamesLostLabel": "GAMES LOST", + "winRateLabel": "WIN RATE", + "unitsSection": "UNITS", + "vehiclesSection": "VEHICLES", + "starfighterSection": "STARFIGHTER", + "serverSectionTitle": "SERVER", + "maxPlayersLabel": "MAX PLAYERS", + "maxSpectatorsLabel": "Max Spectators", + "privacySectionTitle": "Privacy", + "passwordLabel": "Password", + "passwordPlaceholder": "EXAMPLE", + "modeHeroesVersusVillains": "Heroes Versus Villains", + "modeGalacticAssault": "Galactic Assault", + "modeSupremacy": "Supremacy", + "modeCoOpAttack": "CO-OP Attack", + "modeCoOpDefend": "CO-OP Defend", + "modeStrike": "Strike", + "modeExtraction": "Extraction", + "modeBlast": "Blast", + "modeEwokHunt": "Ewok Hunt", + "modeJetpackCargo": "Jetpack Cargo", + "modeStarfighterAssault": "Starfighter Assault", + "modeHeroStarfighters": "Hero Starfighters", + "modeHeroShowdown": "Hero Showdown", + "mapTatooineMosEisley": "Tatooine - Mos Eisley", + "mapTatooineJabbasPalace": "Tatooine - Jabba's Palace", + "mapRepublicVenator": "Republic Venator", + "mapSeparatistDreadnought": "Separatist Dreadnought", + "mapResurgentStarDestroyer": "Resurgent Star Destroyer", + "mapMc85StarCruiser": "MC85 Star Cruiser", + "mapNabooPalaceHangar": "Naboo - Palace Hangar", + "mapEndorResearchStation9": "Endor - Research Station 9", + "mapEndorEwokVillage": "Endor - Ewok Village", + "mapNabooTheedPalace": "Naboo - Theed Palace", + "mapCrait": "Crait", + "activeRotation": "Active Rotation", + "importLabel": "Import", + "importMapRotation": "Import Map Rotation", + "invalidFileFormat": "Invalid file format", + "mapRotationImported": "Map rotation imported", + "noMapsActive": "No maps active", + "customMapNotFoundTooltip": "Custom map not found, this can happen if the map is not part of the current mod collection.\nThis map will be ignored when starting the server.", + "notFound": "Not found" +} \ No newline at end of file diff --git a/Launcher/lib/l10n/app_pt.arb b/Launcher/lib/l10n/app_pt.arb new file mode 100644 index 00000000..1536fce6 --- /dev/null +++ b/Launcher/lib/l10n/app_pt.arb @@ -0,0 +1,643 @@ +{ + "language": "Language", + "accessibility": "ACCESSIBILITY", + "colorblindProfiles": "COLORBLIND PROFILES", + "defaultColor": "DEFAULT", + "protanomaly": "PROTANOMALY", + "deuteranomaly": "DEUTERANOMALY", + "tritanomaly": "TRITANOMALY", + "resetSettings": "RESET SETTINGS", + "reset": "Reset", + "rememberWindowPosition": "Remember Window Position", + "customization": "CUSTOMIZATION", + "patreonExclusive": "PATREON EXCLUSIVE", + "changeHighlightColor": "CHANGE HIGHLIGHT COLOR", + "change": "Change", + "changeBackground": "CHANGE BACKGROUND", + "save": "Save", + "cancel": "Cancel", + "changeColor": "CHANGE COLOR", + "accounts": "ACCOUNTS", + "proxy": "PROXY", + "unavailable": "UNAVAILABLE", + "resetKyberToken": "Reset Kyber Token", + "nexusMods": "NexusMods", + "logout": "Logout", + "login": "Login", + "eaLogout": "EA Logout", + "linkedAccounts": "LINKED ACCOUNTS", + "discord": "Discord", + "connect": "Connect", + "discordUnlink": "{name} | Unlink", + "connectPatreon": "Connect Patreon", + "updates": "UPDATES", + "automaticallyUpdate": "Automatically Update", + "releaseChannel": "Release Channel", + "select": "Select", + "forceUpdate": "Force update", + "updateNow": "UPDATE NOW", + "other": "OTHER", + "licenses": "Licenses", + "showLicenses": "Show Licenses", + "developerMode": "Developer Mode", + "environment": "Environment", + "apiEnvironment": "API Environment", + "setNexusApiToken": "Set Nexus API Token", + "setToken": "Set token", + "setBackgroundImage": "Set Background Image", + "setImage": "Set Image", + "dummyServer": "Dummy Server", + "removeBackground": "Remove Background", + "copyKyberToken": "Copy Kyber Token", + "copy": "Copy", + "activity": "ACTIVITY", + "discordRichPresence": "Discord Rich Presence", + "loggingAndSentry": "Logging & Sentry", + "logs": "Logs", + "exportLogs": "Export Logs", + "optInToSentry": "Opt In To Sentry", + "optOutOfSentry": "Opt Out Of Sentry", + "optIn": "Opt In", + "optOut": "Opt Out", + "launcherDebugMode": "Launcher Debug Mode", + "moduleDebugMode": "Module Debug Mode", + "moduleRpcDebugMode": "Module RPC Debug Mode", + "mods": "MODS", + "changeModDirectory": "Change Mod Directory", + "newDirectory": "New Directory", + "frostyConverter": "Frosty Converter", + "convertYourPacks": "Convert your Packs", + "kyberPreloadedMods": "Kyber Preloaded Mods", + "ingame": "INGAME", + "ingameHotkey": "Ingame Hotkey (Moderation Menu)", + "restartGameWarning": "To apply changes, restart the game", + "enabled": "Enabled", + "disabled": "Disabled", + "proximityChat": "Proximity Chat", + "inputMode": "Input Mode", + "openMic": "Open Mic", + "pushToTalk": "Push to Talk", + "pushToTalkKey": "Push to Talk Key", + "inputVolume": "Input Volume", + "outputVolume": "Output Volume", + "inputDevice": "Input Device", + "outputDevice": "Output Device", + "noDevicesFound": "No devices found", + "selectBackground": "SELECT A BACKGROUND", + "chooseBackgroundDesc": "CHOOSE FROM 8 BACKGROUNDS OR SELECT A CUSTOM BACKGROUND", + "customBackground": "CUSTOM BACKGROUND", + "selectImageDesc": "Select a background image for the launcher", + "minResolutionDesc": "The image resolution should be at least 1920x1080", + "selectImage": "Select Image", + "fileTooLarge": "The file is too large. Please select a file smaller than 10MB", + "resolutionTooLow": "The image resolution is too low. Please select an image with a resolution of at least 1920x1080", + "copyingImage": "Copying image...", + "selectImagePickerTitle": "Select a background image", + "langAccTitle": "LANGUAGE & ACCESSIBILITY", + "langAccDesc": "CHANGE LANGUAGE, PROXY & ACCESSIBILITY SETTINGS", + "modConfigTitle": "MOD CONFIGURATION", + "modConfigDesc": "CONFIGURE MODS SETTINGS, IMPORT FROM FROSTY & MORE", + "creditsTitle": "CREDITS LIST", + "creditsDesc": "VIEW DEVELOPERS, CONTRIBUTORS & PATREON SUPPORTERS", + "ingameSettingsTitle": "INGAME SETTINGS", + "proximityChatDesc": "CONFIGURE PROXIMITY CHAT SETTINGS", + "logsActivityTitle": "LOGS & ACTIVITY", + "logsActivityDesc": "VIEW ACTIVITY & DEBUG LOGGING SETTINGS", + "accountsUpdatesTitle": "ACCOUNTS & UPDATES", + "accountsUpdatesDesc": "LOGOUT, UPDATE SETTINGS & MORE", + "versionText": "VERSION: {version}#CL{build}", + "downloadManager": "DOWNLOAD MANAGER", + "pausedDownloads": "PAUSED DOWNLOADS", + "unCapSpeedPrefix": "UN-CAP DOWNLOAD SPEEDS WITH ", + "nexusModsPremium": "NEXUS MODS PREMIUM", + "downloadProgress": " / {total} ({speed}/s) {percentage}%", + "@downloadProgress": { + "placeholders": { + "total": { "type": "String" }, + "speed": { "type": "String" }, + "percentage": { "type": "int" } + } + }, + "loadMods": "LOAD MODS", + "export": "EXPORT", + "selectMods": "Select mods", + "ignoringDuplicate": "Ignoring {filename}. (Duplicate)", + "@ignoringDuplicate": { + "placeholders": { + "filename": { "type": "String" } + } + }, + "saveCollection": "Save collection", + "icon": "ICON", + "name": "Name", + "author": "Author", + "version": "Version", + "category": "Category", + "exportCollection": "EXPORT COLLECTION", + "exportingCollection": "Exporting collection...", + "selectIcon": "Select icon", + "eaLogin": "EA Login", + "loading": "Loading...", + "waitingForResponse": "Waiting for response...", + "maximaStarting": "Maxima is starting...", + "fetchingData": "Fetching data...", + "genericError": "An error occurred", + "loginFailed": "Login failed", + "userDoesNotExist": "The specified user does not exist", + "passwordIncorrect": "The specified password is incorrect", + "failedToFindAuthCode": "Failed to find auth code. Please try another browser", + "pathCopied": "The path has been copied to your clipboard", + "noAuthCodeReceived": "No authorization code was received", + "authorizationSuccessful": "Authorization successful", + "patreonAuthorizedMessage": "You have been successfully authorized as a Patreon member", + "authorizationFailed": "Authorization failed", + "maximaIntroText": "In order to use this launcher, you need to login to Maxima. This is required to launch and interact with Battlefront 2.\nYou will be redirected to the EA login page and after logging in, you will be redirected back to the launcher.", + "loginWithEa": "Login with EA", + "eaIdDisplay": "EA-ID: {eaId}", + "logout": "Log out", + "authorizePatreon": "Authorize Patreon", + "whitelistConfirmation": "Are you sure you want to add your current account to the whitelist?", + "currentAccountDisplay": "Current account: {displayName}", + "addToWhitelist": "Add to whitelist", + "gameNotOwnedMessage": "It seems like you do not own Battlefront 2. Please purchase the game or use another account.", + "maximaBackgroundServiceError": "Maxima failed to start the background service. This is usually caused by an antivirus program blocking the service. Please add an exception for the launcher and reinstall it.", + "maximaMissingFilesError": "Some files required to run Maxima are missing. This is usually caused by an antivirus program deleting the files. Please add an exception for the launcher and reinstall it.", + "antivirusExclusionInstruction": "Please exclude the following folders from your antivirus' real-time protection:", + "copyPath": "COPY PATH", + "debugLogsToggled": "{status} debug logs", + "signInNexusPrompt": "Please sign in to Nexus Mods to view mods", + "noModsFound": "No mods found", + "unknownError": "Unknown error", + "noDataFound": "No data found", + "modInformation": "MOD INFORMATION", + "viewModFilesShareEndorse": "VIEW MOD FILES, SHARE AND ENDORSE", + "files": "FILES", + "posts": "POSTS", + "endorseMustDownload": "You must download the mod before endorsing it", + "endorseWaitTime": "You must wait some time after downloading the mod before endorsing it", + "endorseLoginRequired": "You must be logged in to endorse mods", + "linkCopied": "Link copied to clipboard", + "updatedAt": "Updated: ", + "mainFiles": "MAIN FILES", + "miscellaneousFiles": "MISCELLANEOUS FILES", + "optionalFiles": "OPTIONAL FILES", + "archivedFiles": "ARCHIVED FILES", + "fileListTitle": "{title} ({count})", + "@fileListTitle": { + "placeholders": { + "title": { "type": "String" }, + "count": { "type": "int" } + } + }, + "verifiedModAuthor": "Verified Mod Author", + "viewingProfile": "VIEWING PROFILE", + "publishedMods": "PUBLISHED MODS", + "userInformation": "USER INFORMATION", + "viewUserStats": "VIEW USER STATS", + "joinedLabel": "Joined: ", + "modsLabel": "Mods: ", + "postsLabel": "Posts: ", + "uniqueDlsLabel": "Unique-DLs: ", + "viewsLabel": "Views: ", + "kudosLabel": "Kudos: ", + "contributedLabel": "Contributed: ", + "viewOnNexusMods": "VIEW ON NEXUS MODS", + "collectionMods": "Collection Mods", + "modNameVersion": "{name} ({version})", + "@modNameVersion": { + "placeholders": { + "name": { "type": "String" }, + "version": { "type": "String" } + } + }, + "downloadCollectionMods": "DOWNLOAD COLLECTION MODS", + "loadingMods": "LOADING MODS...", + "collections": "COLLECTIONS", + "manageCollectionsSub": "MANAGE MOD COLLECTIONS & CURATE COSMETIC MODS", + "browser": "Browser", + "paginationInfo": "{page}/{totalPages}", + "@paginationInfo": { + "placeholders": { + "page": { "type": "int" }, + "totalPages": { "type": "int" } + } + }, + "modScope": "MOD SCOPE", + "serverNotFound": "The server you were playing on could not be found.", + "nexusModsAuthorization": "NexusMods Authorization", + "nexusAuthDescription": "You need to authorize KyberLauncher on NexusMods to use the mod browser.", + "authorizeKyber": "Authorize Kyber", + "noReportsAvailable": "No reports available", + "reportedBy": "Reported by ", + "forLabel": " for ", + "onLabel": " on ", + "descriptionLabel": "Description", + "evidenceCountLabel": "Evidence ({count})", + "@evidenceCountLabel": { + "placeholders": { + "count": { "type": "int" } + } + }, + "reportViewTitle": "REPORT VIEW", + "viewReportsAndEvidence": "VIEW REPORTS AND EVIDENCE", + "reportsTab": "REPORTS", + "punishmentsTab": "PUNISHMENTS", + "reportRejected": "Report rejected", + "takeAction": "TAKE ACTION", + "reportedByPlayer": "By {name}", + "@reportedByPlayer": { + "placeholders": { + "name": { "type": "String" } + } + }, + "daysDuration": "{days} Days", + "@daysDuration": { + "placeholders": { + "days": { "type": "int" } + } + }, + "permanent": "Permanent", + "bannedByModerator": "Banned By {name}", + "@bannedByModerator": { + "placeholders": { + "name": { "type": "String" } + } + }, + "durationLabel": "Duration: {duration}", + "@durationLabel": { + "placeholders": { + "duration": { "type": "String" } + } + }, + "issuedLabel": "Issued: {date}", + "@issuedLabel": { + "placeholders": { + "date": { "type": "String" } + } + }, + "statusFilter": "STATUS", + "reportsTitle": "REPORTS", + "totalReportsHeader": "TOTAL REPORTS", + "latestReportHeader": "LATEST REPORT", + "latestReasonHeader": "LATEST REASON", + "statusHeader": "STATUS", + "errorLoadingReports": "Error loading reports: {message}", + "reasonWithCount": "{reasonName} ({count})", + "armsecCaseFile": "ARMSEC - CASE FILE", + "evidenceAndDataSummary": "EVIDENCE AND DATA SUMMARY", + "errorLoadingUser": "Error loading user", + "userNotFound": "User not found", + "kyberUser": "KYBER USER", + "open": "OPEN", + "options": "OPTIONS", + "@errorLoadingReports": { + "placeholders": { + "message": { "type": "String" } + } + }, + "@reasonWithCount": { + "placeholders": { + "reasonName": { "type": "String" }, + "count": { "type": "String" } + } + }, + "unknownServer": "Unknown Server", + "hostedBy": "Hosted by {creator}", + "@hostedBy": { + "placeholders": { + "creator": { "type": "String" } + } + }, + "backToServerBrowser": "Back to Server Browser", + "eventLog": "Event Log", + "lightSide": "Light Side", + "darkSide": "Dark Side", + "reportPlayer": "Report Player", + "region": "REGION", + "serverType": "SERVER TYPE", + "gameType": "GAME TYPE", + "gameMode": "GAME MODE", + "warning": "WARNING", + "warningMessageFallback": "Warning message not available.", + "newButton": "NEW", + "moderate": "MODERATE", + "manage": "MANAGE", + "rotation": "ROTATION", + "searchPlaceholder": "Search ...", + "noServersFound": "No servers found", + "serverBrowser": "Server Browser", + "optionsHeader": "OPTIONS", + "moderatorsHeader": "MODERATORS", + "bannedPlayersHeader": "BANNED PLAYERS", + "playButton": "PLAY", + "spectateButton": "SPECTATE", + "copyLinkButton": "COPY LINK", + "copiedToClipboard": "Copied to clipboard!", + "exportBans": "EXPORT BANS", + "importBans": "IMPORT BANS", + "kickAll": "KICK ALL", + "banAll": "BAN ALL", + "eventLogHeader": "Event Log", + "lightSideHeader": "Light Side", + "darkSideHeader": "Dark Side", + "userManager": "User Manager", + "consoleHint": "/COMMAND OR SEND MESSAGE", + "untilLabel": "UNTIL: {date}", + "reasonLabel": "REASON: {reason}", + "unban": "UNBAN", + "modify": "MODIFY", + "playerUnbanned": "Player unbanned", + "timeElapsed": "Time has elapsed", + "daysRemaining": "{count} day(s)", + "hoursRemaining": "{count} hour(s)", + "minutesRemaining": "{count} minute(s)", + "secondsRemaining": "{count} second(s)", + "remaining": "{time} remaining", + "promoteUserTooltip": "Promote User", + "errorOccurred": "An error occurred", + "swapPlayerTooltip": "Swap Player", + "kickPlayerTooltip": "Kick player", + "banPlayerTooltip": "Ban player", + "off": "OFF", + "on": "ON", + "home": "HOME", + "host": "HOST", + "stats": "STATS", + "settings": "SETTINGS", + "ingamePanel": "INGAME PANEL", + "extractingFileWithProgress": "EXTRACTING FILE ({extracted}/{total})", + "extractingFile": "EXTRACTING FILE", + "downloadingWithProgress": "DOWNLOADING ({percentage}% {downloaded}/{total})", + "downloadManager": "DOWNLOAD MANAGER", + "createNewCollection": "CREATE NEW COLLECTION", + "pageNotFoundTitle": "Page Not Found", + "pageNotFoundFallback": "page not found", + "goToHomePage": "Go to home page", + "newCollectionTitle": "New Collection", + "@extractingFileWithProgress": { + "placeholders": { + "extracted": { + "type": "String" + }, + "total": { + "type": "String" + } + } + }, + "@downloadingWithProgress": { + "placeholders": { + "percentage": { + "type": "String" + }, + "downloaded": { + "type": "String" + }, + "total": { + "type": "String" + } + } + }, + "@untilLabel": { + "placeholders": { + "date": { "type": "String" } + } + }, + "@reasonLabel": { + "placeholders": { + "reason": { "type": "String" } + } + }, + "@daysRemaining": { + "placeholders": { + "count": { "type": "int" } + } + }, + "@hoursRemaining": { + "placeholders": { + "count": { "type": "int" } + } + }, + "@minutesRemaining": { + "placeholders": { + "count": { "type": "int" } + } + }, + "@secondsRemaining": { + "placeholders": { + "count": { "type": "int" } + } + }, + "@remaining": { + "placeholders": { + "time": { "type": "String" } + } + }, + "eaAccount": "EA Account", + "finish": "FINISH", + "skip": "SKIP", + "searchFailed": "Failed to search players: {message}", + "unexpectedError": "An unexpected error occurred: {error}", + "searchKyberUser": "Search for a kyber user", + "noResultsFound": "No results found for \"{query}\".", + "friendsCount": "FRIENDS ({count})", + "recentlyViewed": "RECENTLY VIEWED", + "userSearchHistory": "USER SEARCH HISTORY", + "online": "Online", + "playingStatus": "Playing {status}", + "@searchFailed": { + "placeholders": { + "message": { "type": "String" } + } + }, + "@unexpectedError": { + "placeholders": { + "error": { "type": "String" } + } + }, + "@noResultsFound": { + "placeholders": { + "query": { "type": "String" } + } + }, + "@friendsCount": { + "placeholders": { + "count": { "type": "int" } + } + }, + "@playingStatus": { + "placeholders": { + "status": { "type": "String" } + } + }, + "selectCollectionIcon": "Select Collection Icon", + "collectionLabel": "COLLECTION", + "collectionDescription": "A COLLECTION IS A READY TO PLAY MOD LIST", + "modsCount": "{count} Mods", + "collectionNamePlaceholder": "Collection Name", + "saveButton": "SAVE", + "betterSabersPlugin": "BETTER SABERS (PLUGIN)", + "reloadingMods": "Reloading mods", + "generatedBetterSabers": "Generated BetterSabers", + "exportCollectionTar": "EXPORT COLLECTION TAR", + "editCollection": "EDIT COLLECTION", + "createACopy": "CREATE A COPY", + "collectionCopyName": "{title} (Copy)", + "deleteCollection": "DELETE COLLECTION", + "largeModsWarning": "Using multiple large or complex mods isn't recommended, as they often conflict with each other.", + "@modsCount": { + "placeholders": { + "count": { + "type": "int" + } + } + }, + "@collectionCopyName": { + "placeholders": { + "title": { + "type": "String" + } + } + }, + "myMods": "MY MODS", + "nameHeader": "NAME", + "categoryHeader": "CATEGORY", + "sizeHeader": "SIZE", + "typeHeader": "TYPE", + "addHeader": "ADD", + "selectedCount": "{count}", + "@selectedCount": { + "placeholders": { + "count": { + "type": "int" + } + } + }, + "all": "ALL", + "searchHint": "SEARCH...", + "noPlayersFound": "No players found", + "moveModsDirectory": "MOVE MODS DIRECTORY", + "invalidDirectoryWarning": "The current mods directory is invalid because it contains non-ASCII characters. ", + "selectNewDirectoryDesc": "Please select the new directory where you want to move the mods directory. Mods will not be moved. Only the directory will be changed.", + "nonAsciiWarning": "Non-ASCII characters in the path are not allowed (e.g. Korean, Greek, etc.).", + "adminPermissionError": "You can't select a directory that requires admin permissions. Please select another directory.", + "selectedDirNonAsciiError": "The selected directory contains non-ASCII characters. Please select a different directory.", + "browse": "Browse", + "resetToDefault": "Reset to default", + "defaultDirNonAsciiError": "The default mods directory contains non-ASCII characters. Please select a different directory.", + "modInformation": "MOD INFORMATION", + "viewInformationAboutMod": "VIEW INFORMATION ABOUT A MOD", + "modVersionAuthorDisplay": "{version} - {author}", + "@modVersionAuthorDisplay": { + "placeholders": { + "version": { + "type": "String" + }, + "author": { + "type": "String" + } + } + }, + "descriptionLabel": "DESCRIPTION", + "imagesLabel": "IMAGES", + "affectedFilesLabel": "AFFECTED FILES", + "unknownResourceName": "Unknown resource name", + "abortingNexusLogin": "Aborting NexusMods login", + "installWebViewError": "Please install WebView to use this feature.", + "continueText": "CONTINUE", + "wait": "WAIT", + "win7CompatibilityDetected": "Windows 7 Compatibility Mode detected", + "disableWin7CompMode": "To login to NexusMods, please disable Windows 7 Compatibility Mode for Kyber Launcher.", + "reEnableWin7CompMode": "After you have logged in, you need to re-enable Windows 7 Compatibility Mode.", + "checkingWebView": "Checking WebView installation...", + "nexusLoginIntro": "To continue, you will need to log in with your NexusMods account in the browser that is about to open.", + "nexusLoginDataNotice": "The data is processed locally and will only be sent to Nexusmods.\nYou can also enable/disable this feature later in the settings menu.", + "playersHeader": "PLAYERS", + "eventsAndAnnouncements": "EVENTS & ANNOUNCEMENTS", + "viewUpcomingEventsDesc": "VIEW UPCOMING EVENTS & RECEIVE ANNOUNCEMENTS", + "sortByTitle": "SORT BY", + "timeFilterTitle": "TIME FILTER", + "itemsPerPageTitle": "ITEMS PER PAGE", + "allTime": "All time", + "oneYear": "1 year", + "oneMonth": "1 month", + "twoWeeks": "2 weeks", + "oneWeek": "1 week", + "oneDay": "1 day", + "all": "ALL", + "@hoursFormat": { + "placeholders": { + "count": { + "type": "String" + } + } + }, + "@minutesFormat": { + "placeholders": { + "count": { + "type": "int" + } + } + }, + "@secondsFormat": { + "placeholders": { + "count": { + "type": "int" + } + } + }, + "hoursFormat": "{count} HRS", + "minutesFormat": "{count} MIN", + "secondsFormat": "{count} SEC", + "noStatsFound": "No stats found", + "mostPlayed": "MOST PLAYED", + "totalKillsLabel": "TOTAL KILLS", + "totalDeathsLabel": "TOTAL DEATHS", + "kdRatioLabel": "K/D RATIO", + "assistsLabel": "ASSISTS", + "damageDoneLabel": "DAMAGE DONE", + "suicidesLabel": "SUICIDES", + "gamesWonLabel": "GAMES WON", + "gamesLostLabel": "GAMES LOST", + "winRateLabel": "WIN RATE", + "unitsSection": "UNITS", + "vehiclesSection": "VEHICLES", + "starfighterSection": "STARFIGHTER", + "serverSectionTitle": "SERVER", + "maxPlayersLabel": "MAX PLAYERS", + "maxSpectatorsLabel": "Max Spectators", + "privacySectionTitle": "Privacy", + "passwordLabel": "Password", + "passwordPlaceholder": "EXAMPLE", + "modeHeroesVersusVillains": "Heroes Versus Villains", + "modeGalacticAssault": "Galactic Assault", + "modeSupremacy": "Supremacy", + "modeCoOpAttack": "CO-OP Attack", + "modeCoOpDefend": "CO-OP Defend", + "modeStrike": "Strike", + "modeExtraction": "Extraction", + "modeBlast": "Blast", + "modeEwokHunt": "Ewok Hunt", + "modeJetpackCargo": "Jetpack Cargo", + "modeStarfighterAssault": "Starfighter Assault", + "modeHeroStarfighters": "Hero Starfighters", + "modeHeroShowdown": "Hero Showdown", + "mapTatooineMosEisley": "Tatooine - Mos Eisley", + "mapTatooineJabbasPalace": "Tatooine - Jabba's Palace", + "mapRepublicVenator": "Republic Venator", + "mapSeparatistDreadnought": "Separatist Dreadnought", + "mapResurgentStarDestroyer": "Resurgent Star Destroyer", + "mapMc85StarCruiser": "MC85 Star Cruiser", + "mapNabooPalaceHangar": "Naboo - Palace Hangar", + "mapEndorResearchStation9": "Endor - Research Station 9", + "mapEndorEwokVillage": "Endor - Ewok Village", + "mapNabooTheedPalace": "Naboo - Theed Palace", + "mapCrait": "Crait", + "activeRotation": "Active Rotation", + "importLabel": "Import", + "importMapRotation": "Import Map Rotation", + "invalidFileFormat": "Invalid file format", + "mapRotationImported": "Map rotation imported", + "noMapsActive": "No maps active", + "customMapNotFoundTooltip": "Custom map not found, this can happen if the map is not part of the current mod collection.\nThis map will be ignored when starting the server.", + "notFound": "Not found" +} \ No newline at end of file diff --git a/Launcher/lib/l10n/app_ru.arb b/Launcher/lib/l10n/app_ru.arb new file mode 100644 index 00000000..d72d4dde --- /dev/null +++ b/Launcher/lib/l10n/app_ru.arb @@ -0,0 +1,643 @@ +{ + "language": "Язык", + "accessibility": "ДОСТУПНОСТЬ", + "colorblindProfiles": "ПРОФИЛИ ДЛЯ ДАЛЬТОНИКОВ", + "defaultColor": "ПО УМОЛЧАНИЮ", + "protanomaly": "ПРОТАНОМАЛИЯ", + "deuteranomaly": "ДЕЙТЕРАНОМАЛИЯ", + "tritanomaly": "ТРИТАНОМАЛИЯ", + "resetSettings": "СБРОСИТЬ НАСТРОЙКИ", + "reset": "Сброс", + "rememberWindowPosition": "Запоминать положение окна", + "customization": "ПЕРСОНАЛИЗАЦИЯ", + "patreonExclusive": "ЭКСКЛЮЗИВ PATREON", + "changeHighlightColor": "ИЗМЕНИТЬ ЦВЕТ ВЫДЕЛЕНИЯ", + "change": "Изменить", + "changeBackground": "ИЗМЕНИТЬ ФОН", + "save": "Сохранить", + "cancel": "Отмена", + "changeColor": "ИЗМЕНИТЬ ЦВЕТ", + "accounts": "АККАУНТЫ", + "proxy": "ПРОКСИ", + "unavailable": "НЕДОСТУПНО", + "resetKyberToken": "Сбросить токен Kyber", + "nexusMods": "NexusMods", + "logout": "Выйти", + "login": "Войти", + "eaLogout": "Выйти из EA", + "linkedAccounts": "СВЯЗАННЫЕ АККАУНТЫ", + "discord": "Discord", + "connect": "Подключить", + "discordUnlink": "{name} | Отключить", + "connectPatreon": "Подключить Patreon", + "updates": "ОБНОВЛЕНИЯ", + "automaticallyUpdate": "Обновлять автоматически", + "releaseChannel": "Канал обновлений", + "select": "Выбрать", + "forceUpdate": "Принудительное обновление", + "updateNow": "ОБНОВИТЬ СЕЙЧАС", + "other": "ПРОЧЕЕ", + "licenses": "Лицензии", + "showLicenses": "Показать лицензии", + "developerMode": "Режим разработчика", + "environment": "Окружение", + "apiEnvironment": "Окружение API", + "setNexusApiToken": "Установить API-токен Nexus", + "setToken": "Установить токен", + "setBackgroundImage": "Установить фоновое изображение", + "setImage": "Выбрать изображение", + "dummyServer": "Фиктивный сервер", + "removeBackground": "Удалить фон", + "copyKyberToken": "Копировать токен Kyber", + "copy": "Копировать", + "activity": "АКТИВНОСТЬ", + "discordRichPresence": "Discord Rich Presence", + "loggingAndSentry": "Логирование и Sentry", + "logs": "Логи", + "exportLogs": "Экспорт логов", + "optInToSentry": "Включить Sentry", + "optOutOfSentry": "Выключить Sentry", + "optIn": "Включить", + "optOut": "Выключить", + "launcherDebugMode": "Режим отладки лаунчера", + "moduleDebugMode": "Режим отладки модуля", + "moduleRpcDebugMode": "Режим отладки Module RPC", + "mods": "МОДЫ", + "changeModDirectory": "Изменить папку модов", + "newDirectory": "Новая папка", + "frostyConverter": "Конвертер Frosty", + "convertYourPacks": "Конвертировать ваши паки", + "kyberPreloadedMods": "Предзагруженные моды Kyber", + "ingame": "В ИГРЕ", + "ingameHotkey": "Клавиша меню (меню модерации)", + "restartGameWarning": "Чтобы применить изменения, перезапустите игру", + "enabled": "Включено", + "disabled": "Выключено", + "proximityChat": "Позиционный голос", + "inputMode": "Режим ввода", + "openMic": "Открытый микрофон", + "pushToTalk": "Рация (PTT)", + "pushToTalkKey": "Клавиша рации", + "inputVolume": "Громкость входа", + "outputVolume": "Громкость выхода", + "inputDevice": "Устройство ввода", + "outputDevice": "Устройство вывода", + "noDevicesFound": "Устройства не найдены", + "selectBackground": "ВЫБЕРИТЕ ФОН", + "chooseBackgroundDesc": "ВЫБЕРИТЕ ИЗ 8 ФОНОВ ИЛИ УСТАНОВИТЕ СВОЙ", + "customBackground": "СВОЙ ФОН", + "selectImageDesc": "Выберите фоновое изображение для лаунчера", + "minResolutionDesc": "Разрешение изображения должно быть не менее 1920x1080", + "selectImage": "Выбрать изображение", + "fileTooLarge": "Файл слишком большой. Пожалуйста, выберите файл меньше 10 МБ", + "resolutionTooLow": "Слишком низкое разрешение. Пожалуйста, выберите изображение минимум 1920x1080", + "copyingImage": "Копирование изображения...", + "selectImagePickerTitle": "Выберите фоновое изображение", + "langAccTitle": "ЯЗЫК И ДОСТУПНОСТЬ", + "langAccDesc": "ИЗМЕНИТЬ ЯЗЫК, ПРОКСИ И НАСТРОЙКИ ДОСТУПНОСТИ", + "modConfigTitle": "КОНФИГУРАЦИЯ МОДОВ", + "modConfigDesc": "НАСТРОЙКА МОДОВ, ИМПОРТ ИЗ FROSTY И ПРОЧЕЕ", + "creditsTitle": "СПИСОК АВТОРОВ", + "creditsDesc": "РАЗРАБОТЧИКИ, ПОМОЩНИКИ И ПОДПИСЧИКИ PATREON", + "ingameSettingsTitle": "ВНУТРИИГРОВЫЕ НАСТРОЙКИ", + "proximityChatDesc": "НАСТРОЙКА ПОЗИЦИОННОГО ГОЛОСОВОГО ЧАТА", + "logsActivityTitle": "ЛОГИ И АКТИВНОСТЬ", + "logsActivityDesc": "ПРОСМОТР АКТИВНОСТИ И НАСТРОЕК ОТЛАДКИ", + "accountsUpdatesTitle": "АККАУНТЫ И ОБНОВЛЕНИЯ", + "accountsUpdatesDesc": "ВЫХОД, НАСТРОЙКИ ОБНОВЛЕНИЙ И ПРОЧЕЕ", + "versionText": "ВЕРСИЯ: {version}#CL{build}", + "downloadManager": "МЕНЕДЖЕР ЗАГРУЗОК", + "pausedDownloads": "ПРИОСТАНОВЛЕННЫЕ ЗАГРУЗКИ", + "unCapSpeedPrefix": "СНИМИТЕ ОГРАНИЧЕНИЕ СКОРОСТИ С ", + "nexusModsPremium": "NEXUS MODS PREMIUM", + "downloadProgress": " / {total} ({speed}/с) {percentage}%", + "@downloadProgress": { + "placeholders": { + "total": { "type": "String" }, + "speed": { "type": "String" }, + "percentage": { "type": "int" } + } + }, + "loadMods": "ЗАГРУЗИТЬ МОДЫ", + "export": "ЭКСПОРТ", + "selectMods": "Выбрать моды", + "ignoringDuplicate": "Игнорирование {filename}. (Дубликат)", + "@ignoringDuplicate": { + "placeholders": { + "filename": { "type": "String" } + } + }, + "saveCollection": "Сохранить коллекцию", + "icon": "ЗНАЧОК", + "name": "Название", + "author": "Автор", + "version": "Версия", + "category": "Категория", + "exportCollection": "ЭКСПОРТ КОЛЛЕКЦИИ", + "exportingCollection": "Экспорт коллекции...", + "selectIcon": "Выбрать значок", + "eaLogin": "Вход в EA", + "loading": "Загрузка...", + "waitingForResponse": "Ожидание ответа...", + "maximaStarting": "Запуск Maxima...", + "fetchingData": "Получение данных...", + "genericError": "Произошла ошибка", + "loginFailed": "Ошибка входа", + "userDoesNotExist": "Указанный пользователь не существует", + "passwordIncorrect": "Неверный пароль", + "failedToFindAuthCode": "Не удалось найти код авторизации. Попробуйте другой браузер", + "pathCopied": "Путь скопирован в буфер обмена", + "noAuthCodeReceived": "Код авторизации не получен", + "authorizationSuccessful": "Авторизация успешна", + "patreonAuthorizedMessage": "Вы успешно авторизованы как участник Patreon", + "authorizationFailed": "Ошибка авторизации", + "maximaIntroText": "Для использования лаунчера необходимо войти в Maxima. Это требуется для запуска и взаимодействия с Battlefront 2.\nВы будете перенаправлены на страницу входа EA, а после входа — обратно в лаунчер.", + "loginWithEa": "Войти через EA", + "eaIdDisplay": "EA-ID: {eaId}", + "logout": "Выйти", + "authorizePatreon": "Авторизовать Patreon", + "whitelistConfirmation": "Вы уверены, что хотите добавить текущий аккаунт в белый список?", + "currentAccountDisplay": "Текущий аккаунт: {displayName}", + "addToWhitelist": "Добавить в белый список", + "gameNotOwnedMessage": "Похоже, у вас нет Battlefront 2. Пожалуйста, купите игру или используйте другой аккаунт.", + "maximaBackgroundServiceError": "Maxima не удалось запустить фоновую службу. Обычно это вызвано антивирусом, блокирующим службу. Пожалуйста, добавьте лаунчер в исключения и переустановите его.", + "maximaMissingFilesError": "Некоторые файлы, необходимые для работы Maxima, отсутствуют. Обычно это происходит из-за удаления файлов антивирусом. Пожалуйста, добавьте лаунчер в исключения и переустановите его.", + "antivirusExclusionInstruction": "Пожалуйста, добавьте следующие папки в исключения защиты вашего антивируса в реальном времени:", + "copyPath": "КОПИРОВАТЬ ПУТЬ", + "debugLogsToggled": "Отладочные логи: {status}", + "signInNexusPrompt": "Пожалуйста, войдите в Nexus Mods для просмотра модов", + "noModsFound": "Моды не найдены", + "unknownError": "Неизвестная ошибка", + "noDataFound": "Данные не найдены", + "modInformation": "ИНФОРМАЦИЯ О МОДЕ", + "viewModFilesShareEndorse": "ФАЙЛЫ МОДА, ПОДЕЛИТЬСЯ И ПОДДЕРЖАТЬ", + "files": "ФАЙЛЫ", + "posts": "ПОСТЫ", + "endorseMustDownload": "Вы должны скачать мод, прежде чем рекомендовать его", + "endorseWaitTime": "После скачивания должно пройти некоторое время, прежде чем вы сможете рекомендовать мод", + "endorseLoginRequired": "Вы должны войти в систему, чтобы рекомендовать моды", + "linkCopied": "Ссылка скопирована", + "updatedAt": "Обновлено: ", + "mainFiles": "ОСНОВНЫЕ ФАЙЛЫ", + "miscellaneousFiles": "РАЗНОЕ", + "optionalFiles": "ДОПОЛНИТЕЛЬНЫЕ ФАЙЛЫ", + "archivedFiles": "АРХИВНЫЕ ФАЙЛЫ", + "fileListTitle": "{title} ({count})", + "@fileListTitle": { + "placeholders": { + "title": { "type": "String" }, + "count": { "type": "int" } + } + }, + "verifiedModAuthor": "Верифицированный автор", + "viewingProfile": "ПРОСМОТР ПРОФИЛЯ", + "publishedMods": "ОПУБЛИКОВАННЫЕ МОДЫ", + "userInformation": "ИНФОРМАЦИЯ О ПОЛЬЗОВАТЕЛЕ", + "viewUserStats": "СТАТИСТИКА ПОЛЬЗОВАТЕЛЯ", + "joinedLabel": "Регистрация: ", + "modsLabel": "Модов: ", + "postsLabel": "Постов: ", + "uniqueDlsLabel": "Уник. скачиваний: ", + "viewsLabel": "Просмотров: ", + "kudosLabel": "Благодарности: ", + "contributedLabel": "Вклад: ", + "viewOnNexusMods": "СМОТРЕТЬ НА NEXUS MODS", + "collectionMods": "Моды в коллекции", + "modNameVersion": "{name} ({version})", + "@modNameVersion": { + "placeholders": { + "name": { "type": "String" }, + "version": { "type": "String" } + } + }, + "downloadCollectionMods": "СКАЧАТЬ МОДЫ КОЛЛЕКЦИИ", + "loadingMods": "ЗАГРУЗКА МОДОВ...", + "collections": "КОЛЛЕКЦИИ", + "manageCollectionsSub": "УПРАВЛЕНИЕ КОЛЛЕКЦИЯМИ И КОСМЕТИЧЕСКИМИ МОДАМИ", + "browser": "Браузер", + "paginationInfo": "{page}/{totalPages}", + "@paginationInfo": { + "placeholders": { + "page": { "type": "int" }, + "totalPages": { "type": "int" } + } + }, + "modScope": "ОБЛАСТЬ МОДА", + "serverNotFound": "Сервер, на котором вы играли, не найден.", + "nexusModsAuthorization": "Авторизация NexusMods", + "nexusAuthDescription": "Вам необходимо авторизовать KyberLauncher на NexusMods для использования браузера модов.", + "authorizeKyber": "Авторизовать Kyber", + "noReportsAvailable": "Жалоб нет", + "reportedBy": "Отправил ", + "forLabel": " за ", + "onLabel": " на ", + "descriptionLabel": "Описание", + "evidenceCountLabel": "Доказательства ({count})", + "@evidenceCountLabel": { + "placeholders": { + "count": { "type": "int" } + } + }, + "reportViewTitle": "ПРОСМОТР ЖАЛОБЫ", + "viewReportsAndEvidence": "ПРОСМОТР ЖАЛОБ И ДОКАЗАТЕЛЬСТВ", + "reportsTab": "ЖАЛОБЫ", + "punishmentsTab": "НАКАЗАНИЯ", + "reportRejected": "Жалоба отклонена", + "takeAction": "ПРИНЯТЬ МЕРЫ", + "reportedByPlayer": "От: {name}", + "@reportedByPlayer": { + "placeholders": { + "name": { "type": "String" } + } + }, + "daysDuration": "Дней: {days}", + "@daysDuration": { + "placeholders": { + "days": { "type": "int" } + } + }, + "permanent": "Навсегда", + "bannedByModerator": "Забанен: {name}", + "@bannedByModerator": { + "placeholders": { + "name": { "type": "String" } + } + }, + "durationLabel": "Длительность: {duration}", + "@durationLabel": { + "placeholders": { + "duration": { "type": "String" } + } + }, + "issuedLabel": "Выдано: {date}", + "@issuedLabel": { + "placeholders": { + "date": { "type": "String" } + } + }, + "statusFilter": "СТАТУС", + "reportsTitle": "ЖАЛОБЫ", + "totalReportsHeader": "ВСЕГО ЖАЛОБ", + "latestReportHeader": "ПОСЛЕДНЯЯ ЖАЛОБА", + "latestReasonHeader": "ПОСЛЕДНЯЯ ПРИЧИНА", + "statusHeader": "СТАТУС", + "errorLoadingReports": "Ошибка загрузки жалоб: {message}", + "reasonWithCount": "{reasonName} ({count})", + "armsecCaseFile": "ARMSEC — ДЕЛО", + "evidenceAndDataSummary": "СВОДКА ДАННЫХ И ДОКАЗАТЕЛЬСТВ", + "errorLoadingUser": "Ошибка загрузки пользователя", + "userNotFound": "Пользователь не найден", + "kyberUser": "ПОЛЬЗОВАТЕЛЬ KYBER", + "open": "ОТКРЫТЬ", + "options": "ОПЦИИ", + "@errorLoadingReports": { + "placeholders": { + "message": { "type": "String" } + } + }, + "@reasonWithCount": { + "placeholders": { + "reasonName": { "type": "String" }, + "count": { "type": "String" } + } + }, + "unknownServer": "Неизвестный сервер", + "hostedBy": "Хост: {creator}", + "@hostedBy": { + "placeholders": { + "creator": { "type": "String" } + } + }, + "backToServerBrowser": "Назад к списку серверов", + "eventLog": "Журнал событий", + "lightSide": "Светлая сторона", + "darkSide": "Тёмная сторона", + "reportPlayer": "Пожаловаться", + "region": "РЕГИОН", + "serverType": "ТИП СЕРВЕРА", + "gameType": "ТИП ИГРЫ", + "gameMode": "РЕЖИМ ИГРЫ", + "warning": "ПРЕДУПРЕЖДЕНИЕ", + "warningMessageFallback": "Сообщение с предупреждением недоступно.", + "newButton": "НОВЫЙ", + "moderate": "МОДЕРИРОВАТЬ", + "manage": "УПРАВЛЯТЬ", + "rotation": "РОТАЦИЯ", + "searchPlaceholder": "Поиск...", + "noServersFound": "Серверы не найдены", + "serverBrowser": "Список серверов", + "optionsHeader": "ОПЦИИ", + "moderatorsHeader": "МОДЕРАТОРЫ", + "bannedPlayersHeader": "ЗАБАНЕННЫЕ ИГРОКИ", + "playButton": "ИГРАТЬ", + "spectateButton": "НАБЛЮДАТЬ", + "copyLinkButton": "КОПИРОВАТЬ ССЫЛКУ", + "copiedToClipboard": "Скопировано!", + "exportBans": "ЭКСПОРТ БАНОВ", + "importBans": "ИМПОРТ БАНОВ", + "kickAll": "КИКНУТЬ ВСЕХ", + "banAll": "ЗАБАНИТЬ ВСЕХ", + "eventLogHeader": "Журнал событий", + "lightSideHeader": "Светлая сторона", + "darkSideHeader": "Тёмная сторона", + "userManager": "Менеджер пользователей", + "consoleHint": "/КОМАНДА ИЛИ СООБЩЕНИЕ", + "untilLabel": "ДО: {date}", + "reasonLabel": "ПРИЧИНА: {reason}", + "unban": "РАЗБАНИТЬ", + "modify": "ИЗМЕНИТЬ", + "playerUnbanned": "Игрок разбанен", + "timeElapsed": "Время истекло", + "daysRemaining": "дней: {count}", + "hoursRemaining": "часов: {count}", + "minutesRemaining": "минут: {count}", + "secondsRemaining": "секунд: {count}", + "remaining": "осталось {time}", + "promoteUserTooltip": "Повысить пользователя", + "errorOccurred": "Произошла ошибка", + "swapPlayerTooltip": "Сменить команду", + "kickPlayerTooltip": "Кикнуть игрока", + "banPlayerTooltip": "Забанить игрока", + "off": "ВКЛ", + "on": "ВЫКЛ", + "home": "ДОМ", + "host": "ХОСТ", + "stats": "СТАТА", + "settings": "НАСТРОЙКИ", + "ingamePanel": "МЕНЮ ИГРЫ", + "extractingFileWithProgress": "ИЗВЛЕЧЕНИЕ ФАЙЛОВ ({extracted}/{total})", + "extractingFile": "ИЗВЛЕЧЕНИЕ ФАЙЛОВ", + "downloadingWithProgress": "СКАЧИВАЕТСЯ ({percentage}% {downloaded}/{total})", + "downloadManager": "МЕНЕДЖЕР ЗАГРУЗОК", + "createNewCollection": "СОЗДАТЬ КОЛЛЕКЦИЮ", + "pageNotFoundTitle": "Страница Не Найдена", + "pageNotFoundFallback": "страница не найдена", + "goToHomePage": "Вернуться на домашнюю", + "newCollectionTitle": "Новая Коллекция", + "@extractingFileWithProgress": { + "placeholders": { + "extracted": { + "type": "String" + }, + "total": { + "type": "String" + } + } + }, + "@downloadingWithProgress": { + "placeholders": { + "percentage": { + "type": "String" + }, + "downloaded": { + "type": "String" + }, + "total": { + "type": "String" + } + } + }, + "@untilLabel": { + "placeholders": { + "date": { "type": "String" } + } + }, + "@reasonLabel": { + "placeholders": { + "reason": { "type": "String" } + } + }, + "@daysRemaining": { + "placeholders": { + "count": { "type": "int" } + } + }, + "@hoursRemaining": { + "placeholders": { + "count": { "type": "int" } + } + }, + "@minutesRemaining": { + "placeholders": { + "count": { "type": "int" } + } + }, + "@secondsRemaining": { + "placeholders": { + "count": { "type": "int" } + } + }, + "@remaining": { + "placeholders": { + "time": { "type": "String" } + } + }, + "eaAccount": "Аккаунт EA", + "finish": "ЗАВЕРШИТЬ", + "skip": "ПРОПУСТИТЬ", + "searchFailed": "Ошибка поиска игроков: {message}", + "unexpectedError": "Произошла непредвиденная ошибка: {error}", + "searchKyberUser": "Поиск пользователя Kyber", + "noResultsFound": "Ничего не найдено по запросу \"{query}\".", + "friendsCount": "ДРУЗЬЯ ({count})", + "recentlyViewed": "НЕДАВНО ПРОСМОТРЕННЫЕ", + "userSearchHistory": "ИСТОРИЯ ПОИСКА", + "online": "В сети", + "playingStatus": "Играет в {status}", + "@searchFailed": { + "placeholders": { + "message": { "type": "String" } + } + }, + "@unexpectedError": { + "placeholders": { + "error": { "type": "String" } + } + }, + "@noResultsFound": { + "placeholders": { + "query": { "type": "String" } + } + }, + "@friendsCount": { + "placeholders": { + "count": { "type": "int" } + } + }, + "@playingStatus": { + "placeholders": { + "status": { "type": "String" } + } + }, + "selectCollectionIcon": "Выбрать иконку коллекции", + "collectionLabel": "КОЛЛЕКЦИЯ", + "collectionDescription": "КОЛЛЕКЦИЯ — ЭТО ГОТОВЫЙ К ИГРЕ НАБОР МОДОВ", + "modsCount": "Модов: {count}", + "collectionNamePlaceholder": "Название коллекции", + "saveButton": "СОХРАНИТЬ", + "betterSabersPlugin": "BETTER SABERS (ПЛАГИН)", + "reloadingMods": "Перезагрузка модов", + "generatedBetterSabers": "BetterSabers сгенерирован", + "exportCollectionTar": "ЭКСПОРТ КОЛЛЕКЦИИ (TAR)", + "editCollection": "РЕДАКТИРОВАТЬ КОЛЛЕКЦИЮ", + "createACopy": "СОЗДАТЬ КОПИЮ", + "collectionCopyName": "{title} (Копия)", + "deleteCollection": "УДАЛИТЬ КОЛЛЕКЦИЮ", + "largeModsWarning": "Использование нескольких больших или сложных модов не рекомендуется, так как они часто конфликтуют друг с другом.", + "@modsCount": { + "placeholders": { + "count": { + "type": "int" + } + } + }, + "@collectionCopyName": { + "placeholders": { + "title": { + "type": "String" + } + } + }, + "myMods": "МОИ МОДЫ", + "nameHeader": "НАЗВАНИЕ", + "categoryHeader": "КАТЕГОРИЯ", + "sizeHeader": "РАЗМЕР", + "typeHeader": "ТИП", + "addHeader": "ДОБАВИТЬ", + "selectedCount": "{count}", + "@selectedCount": { + "placeholders": { + "count": { + "type": "int" + } + } + }, + "all": "ВСЁ", + "searchHint": "ПОИСК...", + "noPlayersFound": "Игроки не найдены", + "moveModsDirectory": "ПЕРЕМЕСТИТЬ ДИРЕКТОРИЮ МОДОВ", + "invalidDirectoryWarning": "Текущая директория модов недействительна, так как путь содержит символы, отличные от ASCII (например, кириллицу). ", + "selectNewDirectoryDesc": "Пожалуйста, выберите новую директорию для модов. Сами файлы не будут перемещены — будет изменен только путь в настройках.", + "nonAsciiWarning": "Использование символов, отличных от ASCII, в пути запрещено (например, кириллица, греческий и т.д.).", + "adminPermissionError": "Вы не можете выбрать директорию, требующую прав администратора. Пожалуйста, выберите другое место.", + "selectedDirNonAsciiError": "Выбранная директория содержит символы, отличные от ASCII. Пожалуйста, выберите другую директорию.", + "browse": "Обзор", + "resetToDefault": "Сбросить по умолчанию", + "defaultDirNonAsciiError": "Директория модов по умолчанию содержит символы, отличные от ASCII. Пожалуйста, выберите другую директорию.", + "modInformation": "ИНФОРМАЦИЯ О МОДЕ", + "viewInformationAboutMod": "ПОСМОТРЕТЬ ИНФОРМАЦИЮ О МОДЕ", + "modVersionAuthorDisplay": "{version} - {author}", + "@modVersionAuthorDisplay": { + "placeholders": { + "version": { + "type": "String" + }, + "author": { + "type": "String" + } + } + }, + "descriptionLabel": "ОПИСАНИЕ", + "imagesLabel": "КАРТИНКИ", + "affectedFilesLabel": "ЗАТРОНУТЫЕ ФАЙЛЫ", + "unknownResourceName": "Неизвестное имя ресурса", + "abortingNexusLogin": "Отмена входа в NexusMods", + "installWebViewError": "Пожалуйста, установите WebView, чтобы использовать эту функцию.", + "continueText": "ПРОДОЛЖИТЬ", + "wait": "ПОДОЖДИТЕ", + "win7CompatibilityDetected": "Обнаружен режим совместимости с Windows 7", + "disableWin7CompMode": "Чтобы войти в NexusMods, пожалуйста, отключите режим совместимости с Windows 7 для Kyber Launcher.", + "reEnableWin7CompMode": "После того как вы войдете в систему, вам нужно будет снова включить режим совместимости с Windows 7.", + "checkingWebView": "Проверка установки WebView...", + "nexusLoginIntro": "Для продолжения вам нужно будет войти в свою учетную запись NexusMods во встроенном браузере.", + "nexusLoginDataNotice": "Данные обрабатываются локально и отправляются только в NexusMods.\nВы сможете включить или отключить эту функцию позже в меню настроек.", + "playersHeader": "ИГРОКИ", + "eventsAndAnnouncements": "СОБЫТИЯ И АНОНСЫ", + "viewUpcomingEventsDesc": "ПРОСМОТР ПРЕДСТОЯЩИХ СОБЫТИЙ И АНОНСОВ", + "sortByTitle": "СОРТИРОВАТЬ ПО", + "timeFilterTitle": "ФИЛЬТР ПО ВРЕМЕНИ", + "itemsPerPageTitle": "ОТОБРАЖАТЬ НА СТРАНИЦЕ", + "allTime": "Всё время", + "oneYear": "1 год", + "oneMonth": "1 месяц", + "twoWeeks": "2 недели", + "oneWeek": "1 неделя", + "oneDay": "1 день", + "all": "ВСЁ", + "@hoursFormat": { + "placeholders": { + "count": { + "type": "String" + } + } + }, + "@minutesFormat": { + "placeholders": { + "count": { + "type": "int" + } + } + }, + "@secondsFormat": { + "placeholders": { + "count": { + "type": "int" + } + } + }, + "hoursFormat": "{count} Ч", + "minutesFormat": "{count} МИН", + "secondsFormat": "{count} СЕК", + "noStatsFound": "Статистика не найдена", + "mostPlayed": "БОЛЬШЕ ВСЕГО ИГР", + "totalKillsLabel": "ВСЕГО УБИЙСТВ", + "totalDeathsLabel": "ВСЕГО СМЕРТЕЙ", + "kdRatioLabel": "СООТНОШЕНИЕ У/С", + "assistsLabel": "ПОМОЩИ", + "damageDoneLabel": "НАНЕСЕННЫЙ УРОН", + "suicidesLabel": "САМОУБИЙСТВА", + "gamesWonLabel": "ПОБЕДЫ", + "gamesLostLabel": "ПОРАЖЕНИЯ", + "winRateLabel": "ПРОЦЕНТ ПОБЕД", + "unitsSection": "БОЙЦЫ", + "vehiclesSection": "ТЕХНИКА", + "starfighterSection": "ЗВЕЗДНЫЕ ИСТРЕБИТЕЛИ", + "serverSectionTitle": "СЕРВЕР", + "maxPlayersLabel": "МАКС ИГОКОВ", + "maxSpectatorsLabel": "Макс Спектратор", + "privacySectionTitle": "Приватность", + "passwordLabel": "Пароль", + "passwordPlaceholder": "ПРИМЕР", + "modeHeroesVersusVillains": "Герои против злодеев", + "modeGalacticAssault": "Галактическая битва", + "modeSupremacy": "Превосходство", + "modeCoOpAttack": "Совместная игра (Атака)", + "modeCoOpDefend": "Совместная игра (Защита)", + "modeStrike": "Нападение", + "modeExtraction": "Эвакуация", + "modeBlast": "Схватка", + "modeEwokHunt": "Охота эвоков", + "modeJetpackCargo": "Реактивный груз", + "modeStarfighterAssault": "Битва звездных истребителей", + "modeHeroStarfighters": "Звездные истребители героев", + "modeHeroShowdown": "Столкновение героев", + "mapTatooineMosEisley": "Татуин — Мос-Эйсли", + "mapTatooineJabbasPalace": "Татуин — Дворец Джаббы", + "mapRepublicVenator": "Венатор Республики", + "mapSeparatistDreadnought": "Дредноут сепаратистов", + "mapResurgentStarDestroyer": "Звёздный разрушитель «Возрождающий»", + "mapMc85StarCruiser": "Звёздный крейсер MC85", + "mapNabooPalaceHangar": "Набу — Ангар дворца", + "mapEndorResearchStation9": "Эндор — Исследовательская станция 9", + "mapEndorEwokVillage": "Эндор — Деревня эвоков", + "mapNabooTheedPalace": "Набу — Дворец Тида", + "mapCrait": "Крайт", + "activeRotation": "Активная ротация", + "importLabel": "Импорт", + "importMapRotation": "Импортировать ротацию карт", + "invalidFileFormat": "Неверный формат файла", + "mapRotationImported": "Ротация карт импортирована", + "noMapsActive": "Нет активных карт", + "customMapNotFoundTooltip": "Пользовательская карта не найдена. Это может произойти, если карта не входит в текущую коллекцию модов.\nЭта карта будет проигнорирована при запуске сервера.", + "notFound": "Не найдено" +} \ No newline at end of file diff --git a/Launcher/lib/l10n/app_sv.arb b/Launcher/lib/l10n/app_sv.arb new file mode 100644 index 00000000..1536fce6 --- /dev/null +++ b/Launcher/lib/l10n/app_sv.arb @@ -0,0 +1,643 @@ +{ + "language": "Language", + "accessibility": "ACCESSIBILITY", + "colorblindProfiles": "COLORBLIND PROFILES", + "defaultColor": "DEFAULT", + "protanomaly": "PROTANOMALY", + "deuteranomaly": "DEUTERANOMALY", + "tritanomaly": "TRITANOMALY", + "resetSettings": "RESET SETTINGS", + "reset": "Reset", + "rememberWindowPosition": "Remember Window Position", + "customization": "CUSTOMIZATION", + "patreonExclusive": "PATREON EXCLUSIVE", + "changeHighlightColor": "CHANGE HIGHLIGHT COLOR", + "change": "Change", + "changeBackground": "CHANGE BACKGROUND", + "save": "Save", + "cancel": "Cancel", + "changeColor": "CHANGE COLOR", + "accounts": "ACCOUNTS", + "proxy": "PROXY", + "unavailable": "UNAVAILABLE", + "resetKyberToken": "Reset Kyber Token", + "nexusMods": "NexusMods", + "logout": "Logout", + "login": "Login", + "eaLogout": "EA Logout", + "linkedAccounts": "LINKED ACCOUNTS", + "discord": "Discord", + "connect": "Connect", + "discordUnlink": "{name} | Unlink", + "connectPatreon": "Connect Patreon", + "updates": "UPDATES", + "automaticallyUpdate": "Automatically Update", + "releaseChannel": "Release Channel", + "select": "Select", + "forceUpdate": "Force update", + "updateNow": "UPDATE NOW", + "other": "OTHER", + "licenses": "Licenses", + "showLicenses": "Show Licenses", + "developerMode": "Developer Mode", + "environment": "Environment", + "apiEnvironment": "API Environment", + "setNexusApiToken": "Set Nexus API Token", + "setToken": "Set token", + "setBackgroundImage": "Set Background Image", + "setImage": "Set Image", + "dummyServer": "Dummy Server", + "removeBackground": "Remove Background", + "copyKyberToken": "Copy Kyber Token", + "copy": "Copy", + "activity": "ACTIVITY", + "discordRichPresence": "Discord Rich Presence", + "loggingAndSentry": "Logging & Sentry", + "logs": "Logs", + "exportLogs": "Export Logs", + "optInToSentry": "Opt In To Sentry", + "optOutOfSentry": "Opt Out Of Sentry", + "optIn": "Opt In", + "optOut": "Opt Out", + "launcherDebugMode": "Launcher Debug Mode", + "moduleDebugMode": "Module Debug Mode", + "moduleRpcDebugMode": "Module RPC Debug Mode", + "mods": "MODS", + "changeModDirectory": "Change Mod Directory", + "newDirectory": "New Directory", + "frostyConverter": "Frosty Converter", + "convertYourPacks": "Convert your Packs", + "kyberPreloadedMods": "Kyber Preloaded Mods", + "ingame": "INGAME", + "ingameHotkey": "Ingame Hotkey (Moderation Menu)", + "restartGameWarning": "To apply changes, restart the game", + "enabled": "Enabled", + "disabled": "Disabled", + "proximityChat": "Proximity Chat", + "inputMode": "Input Mode", + "openMic": "Open Mic", + "pushToTalk": "Push to Talk", + "pushToTalkKey": "Push to Talk Key", + "inputVolume": "Input Volume", + "outputVolume": "Output Volume", + "inputDevice": "Input Device", + "outputDevice": "Output Device", + "noDevicesFound": "No devices found", + "selectBackground": "SELECT A BACKGROUND", + "chooseBackgroundDesc": "CHOOSE FROM 8 BACKGROUNDS OR SELECT A CUSTOM BACKGROUND", + "customBackground": "CUSTOM BACKGROUND", + "selectImageDesc": "Select a background image for the launcher", + "minResolutionDesc": "The image resolution should be at least 1920x1080", + "selectImage": "Select Image", + "fileTooLarge": "The file is too large. Please select a file smaller than 10MB", + "resolutionTooLow": "The image resolution is too low. Please select an image with a resolution of at least 1920x1080", + "copyingImage": "Copying image...", + "selectImagePickerTitle": "Select a background image", + "langAccTitle": "LANGUAGE & ACCESSIBILITY", + "langAccDesc": "CHANGE LANGUAGE, PROXY & ACCESSIBILITY SETTINGS", + "modConfigTitle": "MOD CONFIGURATION", + "modConfigDesc": "CONFIGURE MODS SETTINGS, IMPORT FROM FROSTY & MORE", + "creditsTitle": "CREDITS LIST", + "creditsDesc": "VIEW DEVELOPERS, CONTRIBUTORS & PATREON SUPPORTERS", + "ingameSettingsTitle": "INGAME SETTINGS", + "proximityChatDesc": "CONFIGURE PROXIMITY CHAT SETTINGS", + "logsActivityTitle": "LOGS & ACTIVITY", + "logsActivityDesc": "VIEW ACTIVITY & DEBUG LOGGING SETTINGS", + "accountsUpdatesTitle": "ACCOUNTS & UPDATES", + "accountsUpdatesDesc": "LOGOUT, UPDATE SETTINGS & MORE", + "versionText": "VERSION: {version}#CL{build}", + "downloadManager": "DOWNLOAD MANAGER", + "pausedDownloads": "PAUSED DOWNLOADS", + "unCapSpeedPrefix": "UN-CAP DOWNLOAD SPEEDS WITH ", + "nexusModsPremium": "NEXUS MODS PREMIUM", + "downloadProgress": " / {total} ({speed}/s) {percentage}%", + "@downloadProgress": { + "placeholders": { + "total": { "type": "String" }, + "speed": { "type": "String" }, + "percentage": { "type": "int" } + } + }, + "loadMods": "LOAD MODS", + "export": "EXPORT", + "selectMods": "Select mods", + "ignoringDuplicate": "Ignoring {filename}. (Duplicate)", + "@ignoringDuplicate": { + "placeholders": { + "filename": { "type": "String" } + } + }, + "saveCollection": "Save collection", + "icon": "ICON", + "name": "Name", + "author": "Author", + "version": "Version", + "category": "Category", + "exportCollection": "EXPORT COLLECTION", + "exportingCollection": "Exporting collection...", + "selectIcon": "Select icon", + "eaLogin": "EA Login", + "loading": "Loading...", + "waitingForResponse": "Waiting for response...", + "maximaStarting": "Maxima is starting...", + "fetchingData": "Fetching data...", + "genericError": "An error occurred", + "loginFailed": "Login failed", + "userDoesNotExist": "The specified user does not exist", + "passwordIncorrect": "The specified password is incorrect", + "failedToFindAuthCode": "Failed to find auth code. Please try another browser", + "pathCopied": "The path has been copied to your clipboard", + "noAuthCodeReceived": "No authorization code was received", + "authorizationSuccessful": "Authorization successful", + "patreonAuthorizedMessage": "You have been successfully authorized as a Patreon member", + "authorizationFailed": "Authorization failed", + "maximaIntroText": "In order to use this launcher, you need to login to Maxima. This is required to launch and interact with Battlefront 2.\nYou will be redirected to the EA login page and after logging in, you will be redirected back to the launcher.", + "loginWithEa": "Login with EA", + "eaIdDisplay": "EA-ID: {eaId}", + "logout": "Log out", + "authorizePatreon": "Authorize Patreon", + "whitelistConfirmation": "Are you sure you want to add your current account to the whitelist?", + "currentAccountDisplay": "Current account: {displayName}", + "addToWhitelist": "Add to whitelist", + "gameNotOwnedMessage": "It seems like you do not own Battlefront 2. Please purchase the game or use another account.", + "maximaBackgroundServiceError": "Maxima failed to start the background service. This is usually caused by an antivirus program blocking the service. Please add an exception for the launcher and reinstall it.", + "maximaMissingFilesError": "Some files required to run Maxima are missing. This is usually caused by an antivirus program deleting the files. Please add an exception for the launcher and reinstall it.", + "antivirusExclusionInstruction": "Please exclude the following folders from your antivirus' real-time protection:", + "copyPath": "COPY PATH", + "debugLogsToggled": "{status} debug logs", + "signInNexusPrompt": "Please sign in to Nexus Mods to view mods", + "noModsFound": "No mods found", + "unknownError": "Unknown error", + "noDataFound": "No data found", + "modInformation": "MOD INFORMATION", + "viewModFilesShareEndorse": "VIEW MOD FILES, SHARE AND ENDORSE", + "files": "FILES", + "posts": "POSTS", + "endorseMustDownload": "You must download the mod before endorsing it", + "endorseWaitTime": "You must wait some time after downloading the mod before endorsing it", + "endorseLoginRequired": "You must be logged in to endorse mods", + "linkCopied": "Link copied to clipboard", + "updatedAt": "Updated: ", + "mainFiles": "MAIN FILES", + "miscellaneousFiles": "MISCELLANEOUS FILES", + "optionalFiles": "OPTIONAL FILES", + "archivedFiles": "ARCHIVED FILES", + "fileListTitle": "{title} ({count})", + "@fileListTitle": { + "placeholders": { + "title": { "type": "String" }, + "count": { "type": "int" } + } + }, + "verifiedModAuthor": "Verified Mod Author", + "viewingProfile": "VIEWING PROFILE", + "publishedMods": "PUBLISHED MODS", + "userInformation": "USER INFORMATION", + "viewUserStats": "VIEW USER STATS", + "joinedLabel": "Joined: ", + "modsLabel": "Mods: ", + "postsLabel": "Posts: ", + "uniqueDlsLabel": "Unique-DLs: ", + "viewsLabel": "Views: ", + "kudosLabel": "Kudos: ", + "contributedLabel": "Contributed: ", + "viewOnNexusMods": "VIEW ON NEXUS MODS", + "collectionMods": "Collection Mods", + "modNameVersion": "{name} ({version})", + "@modNameVersion": { + "placeholders": { + "name": { "type": "String" }, + "version": { "type": "String" } + } + }, + "downloadCollectionMods": "DOWNLOAD COLLECTION MODS", + "loadingMods": "LOADING MODS...", + "collections": "COLLECTIONS", + "manageCollectionsSub": "MANAGE MOD COLLECTIONS & CURATE COSMETIC MODS", + "browser": "Browser", + "paginationInfo": "{page}/{totalPages}", + "@paginationInfo": { + "placeholders": { + "page": { "type": "int" }, + "totalPages": { "type": "int" } + } + }, + "modScope": "MOD SCOPE", + "serverNotFound": "The server you were playing on could not be found.", + "nexusModsAuthorization": "NexusMods Authorization", + "nexusAuthDescription": "You need to authorize KyberLauncher on NexusMods to use the mod browser.", + "authorizeKyber": "Authorize Kyber", + "noReportsAvailable": "No reports available", + "reportedBy": "Reported by ", + "forLabel": " for ", + "onLabel": " on ", + "descriptionLabel": "Description", + "evidenceCountLabel": "Evidence ({count})", + "@evidenceCountLabel": { + "placeholders": { + "count": { "type": "int" } + } + }, + "reportViewTitle": "REPORT VIEW", + "viewReportsAndEvidence": "VIEW REPORTS AND EVIDENCE", + "reportsTab": "REPORTS", + "punishmentsTab": "PUNISHMENTS", + "reportRejected": "Report rejected", + "takeAction": "TAKE ACTION", + "reportedByPlayer": "By {name}", + "@reportedByPlayer": { + "placeholders": { + "name": { "type": "String" } + } + }, + "daysDuration": "{days} Days", + "@daysDuration": { + "placeholders": { + "days": { "type": "int" } + } + }, + "permanent": "Permanent", + "bannedByModerator": "Banned By {name}", + "@bannedByModerator": { + "placeholders": { + "name": { "type": "String" } + } + }, + "durationLabel": "Duration: {duration}", + "@durationLabel": { + "placeholders": { + "duration": { "type": "String" } + } + }, + "issuedLabel": "Issued: {date}", + "@issuedLabel": { + "placeholders": { + "date": { "type": "String" } + } + }, + "statusFilter": "STATUS", + "reportsTitle": "REPORTS", + "totalReportsHeader": "TOTAL REPORTS", + "latestReportHeader": "LATEST REPORT", + "latestReasonHeader": "LATEST REASON", + "statusHeader": "STATUS", + "errorLoadingReports": "Error loading reports: {message}", + "reasonWithCount": "{reasonName} ({count})", + "armsecCaseFile": "ARMSEC - CASE FILE", + "evidenceAndDataSummary": "EVIDENCE AND DATA SUMMARY", + "errorLoadingUser": "Error loading user", + "userNotFound": "User not found", + "kyberUser": "KYBER USER", + "open": "OPEN", + "options": "OPTIONS", + "@errorLoadingReports": { + "placeholders": { + "message": { "type": "String" } + } + }, + "@reasonWithCount": { + "placeholders": { + "reasonName": { "type": "String" }, + "count": { "type": "String" } + } + }, + "unknownServer": "Unknown Server", + "hostedBy": "Hosted by {creator}", + "@hostedBy": { + "placeholders": { + "creator": { "type": "String" } + } + }, + "backToServerBrowser": "Back to Server Browser", + "eventLog": "Event Log", + "lightSide": "Light Side", + "darkSide": "Dark Side", + "reportPlayer": "Report Player", + "region": "REGION", + "serverType": "SERVER TYPE", + "gameType": "GAME TYPE", + "gameMode": "GAME MODE", + "warning": "WARNING", + "warningMessageFallback": "Warning message not available.", + "newButton": "NEW", + "moderate": "MODERATE", + "manage": "MANAGE", + "rotation": "ROTATION", + "searchPlaceholder": "Search ...", + "noServersFound": "No servers found", + "serverBrowser": "Server Browser", + "optionsHeader": "OPTIONS", + "moderatorsHeader": "MODERATORS", + "bannedPlayersHeader": "BANNED PLAYERS", + "playButton": "PLAY", + "spectateButton": "SPECTATE", + "copyLinkButton": "COPY LINK", + "copiedToClipboard": "Copied to clipboard!", + "exportBans": "EXPORT BANS", + "importBans": "IMPORT BANS", + "kickAll": "KICK ALL", + "banAll": "BAN ALL", + "eventLogHeader": "Event Log", + "lightSideHeader": "Light Side", + "darkSideHeader": "Dark Side", + "userManager": "User Manager", + "consoleHint": "/COMMAND OR SEND MESSAGE", + "untilLabel": "UNTIL: {date}", + "reasonLabel": "REASON: {reason}", + "unban": "UNBAN", + "modify": "MODIFY", + "playerUnbanned": "Player unbanned", + "timeElapsed": "Time has elapsed", + "daysRemaining": "{count} day(s)", + "hoursRemaining": "{count} hour(s)", + "minutesRemaining": "{count} minute(s)", + "secondsRemaining": "{count} second(s)", + "remaining": "{time} remaining", + "promoteUserTooltip": "Promote User", + "errorOccurred": "An error occurred", + "swapPlayerTooltip": "Swap Player", + "kickPlayerTooltip": "Kick player", + "banPlayerTooltip": "Ban player", + "off": "OFF", + "on": "ON", + "home": "HOME", + "host": "HOST", + "stats": "STATS", + "settings": "SETTINGS", + "ingamePanel": "INGAME PANEL", + "extractingFileWithProgress": "EXTRACTING FILE ({extracted}/{total})", + "extractingFile": "EXTRACTING FILE", + "downloadingWithProgress": "DOWNLOADING ({percentage}% {downloaded}/{total})", + "downloadManager": "DOWNLOAD MANAGER", + "createNewCollection": "CREATE NEW COLLECTION", + "pageNotFoundTitle": "Page Not Found", + "pageNotFoundFallback": "page not found", + "goToHomePage": "Go to home page", + "newCollectionTitle": "New Collection", + "@extractingFileWithProgress": { + "placeholders": { + "extracted": { + "type": "String" + }, + "total": { + "type": "String" + } + } + }, + "@downloadingWithProgress": { + "placeholders": { + "percentage": { + "type": "String" + }, + "downloaded": { + "type": "String" + }, + "total": { + "type": "String" + } + } + }, + "@untilLabel": { + "placeholders": { + "date": { "type": "String" } + } + }, + "@reasonLabel": { + "placeholders": { + "reason": { "type": "String" } + } + }, + "@daysRemaining": { + "placeholders": { + "count": { "type": "int" } + } + }, + "@hoursRemaining": { + "placeholders": { + "count": { "type": "int" } + } + }, + "@minutesRemaining": { + "placeholders": { + "count": { "type": "int" } + } + }, + "@secondsRemaining": { + "placeholders": { + "count": { "type": "int" } + } + }, + "@remaining": { + "placeholders": { + "time": { "type": "String" } + } + }, + "eaAccount": "EA Account", + "finish": "FINISH", + "skip": "SKIP", + "searchFailed": "Failed to search players: {message}", + "unexpectedError": "An unexpected error occurred: {error}", + "searchKyberUser": "Search for a kyber user", + "noResultsFound": "No results found for \"{query}\".", + "friendsCount": "FRIENDS ({count})", + "recentlyViewed": "RECENTLY VIEWED", + "userSearchHistory": "USER SEARCH HISTORY", + "online": "Online", + "playingStatus": "Playing {status}", + "@searchFailed": { + "placeholders": { + "message": { "type": "String" } + } + }, + "@unexpectedError": { + "placeholders": { + "error": { "type": "String" } + } + }, + "@noResultsFound": { + "placeholders": { + "query": { "type": "String" } + } + }, + "@friendsCount": { + "placeholders": { + "count": { "type": "int" } + } + }, + "@playingStatus": { + "placeholders": { + "status": { "type": "String" } + } + }, + "selectCollectionIcon": "Select Collection Icon", + "collectionLabel": "COLLECTION", + "collectionDescription": "A COLLECTION IS A READY TO PLAY MOD LIST", + "modsCount": "{count} Mods", + "collectionNamePlaceholder": "Collection Name", + "saveButton": "SAVE", + "betterSabersPlugin": "BETTER SABERS (PLUGIN)", + "reloadingMods": "Reloading mods", + "generatedBetterSabers": "Generated BetterSabers", + "exportCollectionTar": "EXPORT COLLECTION TAR", + "editCollection": "EDIT COLLECTION", + "createACopy": "CREATE A COPY", + "collectionCopyName": "{title} (Copy)", + "deleteCollection": "DELETE COLLECTION", + "largeModsWarning": "Using multiple large or complex mods isn't recommended, as they often conflict with each other.", + "@modsCount": { + "placeholders": { + "count": { + "type": "int" + } + } + }, + "@collectionCopyName": { + "placeholders": { + "title": { + "type": "String" + } + } + }, + "myMods": "MY MODS", + "nameHeader": "NAME", + "categoryHeader": "CATEGORY", + "sizeHeader": "SIZE", + "typeHeader": "TYPE", + "addHeader": "ADD", + "selectedCount": "{count}", + "@selectedCount": { + "placeholders": { + "count": { + "type": "int" + } + } + }, + "all": "ALL", + "searchHint": "SEARCH...", + "noPlayersFound": "No players found", + "moveModsDirectory": "MOVE MODS DIRECTORY", + "invalidDirectoryWarning": "The current mods directory is invalid because it contains non-ASCII characters. ", + "selectNewDirectoryDesc": "Please select the new directory where you want to move the mods directory. Mods will not be moved. Only the directory will be changed.", + "nonAsciiWarning": "Non-ASCII characters in the path are not allowed (e.g. Korean, Greek, etc.).", + "adminPermissionError": "You can't select a directory that requires admin permissions. Please select another directory.", + "selectedDirNonAsciiError": "The selected directory contains non-ASCII characters. Please select a different directory.", + "browse": "Browse", + "resetToDefault": "Reset to default", + "defaultDirNonAsciiError": "The default mods directory contains non-ASCII characters. Please select a different directory.", + "modInformation": "MOD INFORMATION", + "viewInformationAboutMod": "VIEW INFORMATION ABOUT A MOD", + "modVersionAuthorDisplay": "{version} - {author}", + "@modVersionAuthorDisplay": { + "placeholders": { + "version": { + "type": "String" + }, + "author": { + "type": "String" + } + } + }, + "descriptionLabel": "DESCRIPTION", + "imagesLabel": "IMAGES", + "affectedFilesLabel": "AFFECTED FILES", + "unknownResourceName": "Unknown resource name", + "abortingNexusLogin": "Aborting NexusMods login", + "installWebViewError": "Please install WebView to use this feature.", + "continueText": "CONTINUE", + "wait": "WAIT", + "win7CompatibilityDetected": "Windows 7 Compatibility Mode detected", + "disableWin7CompMode": "To login to NexusMods, please disable Windows 7 Compatibility Mode for Kyber Launcher.", + "reEnableWin7CompMode": "After you have logged in, you need to re-enable Windows 7 Compatibility Mode.", + "checkingWebView": "Checking WebView installation...", + "nexusLoginIntro": "To continue, you will need to log in with your NexusMods account in the browser that is about to open.", + "nexusLoginDataNotice": "The data is processed locally and will only be sent to Nexusmods.\nYou can also enable/disable this feature later in the settings menu.", + "playersHeader": "PLAYERS", + "eventsAndAnnouncements": "EVENTS & ANNOUNCEMENTS", + "viewUpcomingEventsDesc": "VIEW UPCOMING EVENTS & RECEIVE ANNOUNCEMENTS", + "sortByTitle": "SORT BY", + "timeFilterTitle": "TIME FILTER", + "itemsPerPageTitle": "ITEMS PER PAGE", + "allTime": "All time", + "oneYear": "1 year", + "oneMonth": "1 month", + "twoWeeks": "2 weeks", + "oneWeek": "1 week", + "oneDay": "1 day", + "all": "ALL", + "@hoursFormat": { + "placeholders": { + "count": { + "type": "String" + } + } + }, + "@minutesFormat": { + "placeholders": { + "count": { + "type": "int" + } + } + }, + "@secondsFormat": { + "placeholders": { + "count": { + "type": "int" + } + } + }, + "hoursFormat": "{count} HRS", + "minutesFormat": "{count} MIN", + "secondsFormat": "{count} SEC", + "noStatsFound": "No stats found", + "mostPlayed": "MOST PLAYED", + "totalKillsLabel": "TOTAL KILLS", + "totalDeathsLabel": "TOTAL DEATHS", + "kdRatioLabel": "K/D RATIO", + "assistsLabel": "ASSISTS", + "damageDoneLabel": "DAMAGE DONE", + "suicidesLabel": "SUICIDES", + "gamesWonLabel": "GAMES WON", + "gamesLostLabel": "GAMES LOST", + "winRateLabel": "WIN RATE", + "unitsSection": "UNITS", + "vehiclesSection": "VEHICLES", + "starfighterSection": "STARFIGHTER", + "serverSectionTitle": "SERVER", + "maxPlayersLabel": "MAX PLAYERS", + "maxSpectatorsLabel": "Max Spectators", + "privacySectionTitle": "Privacy", + "passwordLabel": "Password", + "passwordPlaceholder": "EXAMPLE", + "modeHeroesVersusVillains": "Heroes Versus Villains", + "modeGalacticAssault": "Galactic Assault", + "modeSupremacy": "Supremacy", + "modeCoOpAttack": "CO-OP Attack", + "modeCoOpDefend": "CO-OP Defend", + "modeStrike": "Strike", + "modeExtraction": "Extraction", + "modeBlast": "Blast", + "modeEwokHunt": "Ewok Hunt", + "modeJetpackCargo": "Jetpack Cargo", + "modeStarfighterAssault": "Starfighter Assault", + "modeHeroStarfighters": "Hero Starfighters", + "modeHeroShowdown": "Hero Showdown", + "mapTatooineMosEisley": "Tatooine - Mos Eisley", + "mapTatooineJabbasPalace": "Tatooine - Jabba's Palace", + "mapRepublicVenator": "Republic Venator", + "mapSeparatistDreadnought": "Separatist Dreadnought", + "mapResurgentStarDestroyer": "Resurgent Star Destroyer", + "mapMc85StarCruiser": "MC85 Star Cruiser", + "mapNabooPalaceHangar": "Naboo - Palace Hangar", + "mapEndorResearchStation9": "Endor - Research Station 9", + "mapEndorEwokVillage": "Endor - Ewok Village", + "mapNabooTheedPalace": "Naboo - Theed Palace", + "mapCrait": "Crait", + "activeRotation": "Active Rotation", + "importLabel": "Import", + "importMapRotation": "Import Map Rotation", + "invalidFileFormat": "Invalid file format", + "mapRotationImported": "Map rotation imported", + "noMapsActive": "No maps active", + "customMapNotFoundTooltip": "Custom map not found, this can happen if the map is not part of the current mod collection.\nThis map will be ignored when starting the server.", + "notFound": "Not found" +} \ No newline at end of file diff --git a/Launcher/lib/l10n/app_uk.arb b/Launcher/lib/l10n/app_uk.arb new file mode 100644 index 00000000..1536fce6 --- /dev/null +++ b/Launcher/lib/l10n/app_uk.arb @@ -0,0 +1,643 @@ +{ + "language": "Language", + "accessibility": "ACCESSIBILITY", + "colorblindProfiles": "COLORBLIND PROFILES", + "defaultColor": "DEFAULT", + "protanomaly": "PROTANOMALY", + "deuteranomaly": "DEUTERANOMALY", + "tritanomaly": "TRITANOMALY", + "resetSettings": "RESET SETTINGS", + "reset": "Reset", + "rememberWindowPosition": "Remember Window Position", + "customization": "CUSTOMIZATION", + "patreonExclusive": "PATREON EXCLUSIVE", + "changeHighlightColor": "CHANGE HIGHLIGHT COLOR", + "change": "Change", + "changeBackground": "CHANGE BACKGROUND", + "save": "Save", + "cancel": "Cancel", + "changeColor": "CHANGE COLOR", + "accounts": "ACCOUNTS", + "proxy": "PROXY", + "unavailable": "UNAVAILABLE", + "resetKyberToken": "Reset Kyber Token", + "nexusMods": "NexusMods", + "logout": "Logout", + "login": "Login", + "eaLogout": "EA Logout", + "linkedAccounts": "LINKED ACCOUNTS", + "discord": "Discord", + "connect": "Connect", + "discordUnlink": "{name} | Unlink", + "connectPatreon": "Connect Patreon", + "updates": "UPDATES", + "automaticallyUpdate": "Automatically Update", + "releaseChannel": "Release Channel", + "select": "Select", + "forceUpdate": "Force update", + "updateNow": "UPDATE NOW", + "other": "OTHER", + "licenses": "Licenses", + "showLicenses": "Show Licenses", + "developerMode": "Developer Mode", + "environment": "Environment", + "apiEnvironment": "API Environment", + "setNexusApiToken": "Set Nexus API Token", + "setToken": "Set token", + "setBackgroundImage": "Set Background Image", + "setImage": "Set Image", + "dummyServer": "Dummy Server", + "removeBackground": "Remove Background", + "copyKyberToken": "Copy Kyber Token", + "copy": "Copy", + "activity": "ACTIVITY", + "discordRichPresence": "Discord Rich Presence", + "loggingAndSentry": "Logging & Sentry", + "logs": "Logs", + "exportLogs": "Export Logs", + "optInToSentry": "Opt In To Sentry", + "optOutOfSentry": "Opt Out Of Sentry", + "optIn": "Opt In", + "optOut": "Opt Out", + "launcherDebugMode": "Launcher Debug Mode", + "moduleDebugMode": "Module Debug Mode", + "moduleRpcDebugMode": "Module RPC Debug Mode", + "mods": "MODS", + "changeModDirectory": "Change Mod Directory", + "newDirectory": "New Directory", + "frostyConverter": "Frosty Converter", + "convertYourPacks": "Convert your Packs", + "kyberPreloadedMods": "Kyber Preloaded Mods", + "ingame": "INGAME", + "ingameHotkey": "Ingame Hotkey (Moderation Menu)", + "restartGameWarning": "To apply changes, restart the game", + "enabled": "Enabled", + "disabled": "Disabled", + "proximityChat": "Proximity Chat", + "inputMode": "Input Mode", + "openMic": "Open Mic", + "pushToTalk": "Push to Talk", + "pushToTalkKey": "Push to Talk Key", + "inputVolume": "Input Volume", + "outputVolume": "Output Volume", + "inputDevice": "Input Device", + "outputDevice": "Output Device", + "noDevicesFound": "No devices found", + "selectBackground": "SELECT A BACKGROUND", + "chooseBackgroundDesc": "CHOOSE FROM 8 BACKGROUNDS OR SELECT A CUSTOM BACKGROUND", + "customBackground": "CUSTOM BACKGROUND", + "selectImageDesc": "Select a background image for the launcher", + "minResolutionDesc": "The image resolution should be at least 1920x1080", + "selectImage": "Select Image", + "fileTooLarge": "The file is too large. Please select a file smaller than 10MB", + "resolutionTooLow": "The image resolution is too low. Please select an image with a resolution of at least 1920x1080", + "copyingImage": "Copying image...", + "selectImagePickerTitle": "Select a background image", + "langAccTitle": "LANGUAGE & ACCESSIBILITY", + "langAccDesc": "CHANGE LANGUAGE, PROXY & ACCESSIBILITY SETTINGS", + "modConfigTitle": "MOD CONFIGURATION", + "modConfigDesc": "CONFIGURE MODS SETTINGS, IMPORT FROM FROSTY & MORE", + "creditsTitle": "CREDITS LIST", + "creditsDesc": "VIEW DEVELOPERS, CONTRIBUTORS & PATREON SUPPORTERS", + "ingameSettingsTitle": "INGAME SETTINGS", + "proximityChatDesc": "CONFIGURE PROXIMITY CHAT SETTINGS", + "logsActivityTitle": "LOGS & ACTIVITY", + "logsActivityDesc": "VIEW ACTIVITY & DEBUG LOGGING SETTINGS", + "accountsUpdatesTitle": "ACCOUNTS & UPDATES", + "accountsUpdatesDesc": "LOGOUT, UPDATE SETTINGS & MORE", + "versionText": "VERSION: {version}#CL{build}", + "downloadManager": "DOWNLOAD MANAGER", + "pausedDownloads": "PAUSED DOWNLOADS", + "unCapSpeedPrefix": "UN-CAP DOWNLOAD SPEEDS WITH ", + "nexusModsPremium": "NEXUS MODS PREMIUM", + "downloadProgress": " / {total} ({speed}/s) {percentage}%", + "@downloadProgress": { + "placeholders": { + "total": { "type": "String" }, + "speed": { "type": "String" }, + "percentage": { "type": "int" } + } + }, + "loadMods": "LOAD MODS", + "export": "EXPORT", + "selectMods": "Select mods", + "ignoringDuplicate": "Ignoring {filename}. (Duplicate)", + "@ignoringDuplicate": { + "placeholders": { + "filename": { "type": "String" } + } + }, + "saveCollection": "Save collection", + "icon": "ICON", + "name": "Name", + "author": "Author", + "version": "Version", + "category": "Category", + "exportCollection": "EXPORT COLLECTION", + "exportingCollection": "Exporting collection...", + "selectIcon": "Select icon", + "eaLogin": "EA Login", + "loading": "Loading...", + "waitingForResponse": "Waiting for response...", + "maximaStarting": "Maxima is starting...", + "fetchingData": "Fetching data...", + "genericError": "An error occurred", + "loginFailed": "Login failed", + "userDoesNotExist": "The specified user does not exist", + "passwordIncorrect": "The specified password is incorrect", + "failedToFindAuthCode": "Failed to find auth code. Please try another browser", + "pathCopied": "The path has been copied to your clipboard", + "noAuthCodeReceived": "No authorization code was received", + "authorizationSuccessful": "Authorization successful", + "patreonAuthorizedMessage": "You have been successfully authorized as a Patreon member", + "authorizationFailed": "Authorization failed", + "maximaIntroText": "In order to use this launcher, you need to login to Maxima. This is required to launch and interact with Battlefront 2.\nYou will be redirected to the EA login page and after logging in, you will be redirected back to the launcher.", + "loginWithEa": "Login with EA", + "eaIdDisplay": "EA-ID: {eaId}", + "logout": "Log out", + "authorizePatreon": "Authorize Patreon", + "whitelistConfirmation": "Are you sure you want to add your current account to the whitelist?", + "currentAccountDisplay": "Current account: {displayName}", + "addToWhitelist": "Add to whitelist", + "gameNotOwnedMessage": "It seems like you do not own Battlefront 2. Please purchase the game or use another account.", + "maximaBackgroundServiceError": "Maxima failed to start the background service. This is usually caused by an antivirus program blocking the service. Please add an exception for the launcher and reinstall it.", + "maximaMissingFilesError": "Some files required to run Maxima are missing. This is usually caused by an antivirus program deleting the files. Please add an exception for the launcher and reinstall it.", + "antivirusExclusionInstruction": "Please exclude the following folders from your antivirus' real-time protection:", + "copyPath": "COPY PATH", + "debugLogsToggled": "{status} debug logs", + "signInNexusPrompt": "Please sign in to Nexus Mods to view mods", + "noModsFound": "No mods found", + "unknownError": "Unknown error", + "noDataFound": "No data found", + "modInformation": "MOD INFORMATION", + "viewModFilesShareEndorse": "VIEW MOD FILES, SHARE AND ENDORSE", + "files": "FILES", + "posts": "POSTS", + "endorseMustDownload": "You must download the mod before endorsing it", + "endorseWaitTime": "You must wait some time after downloading the mod before endorsing it", + "endorseLoginRequired": "You must be logged in to endorse mods", + "linkCopied": "Link copied to clipboard", + "updatedAt": "Updated: ", + "mainFiles": "MAIN FILES", + "miscellaneousFiles": "MISCELLANEOUS FILES", + "optionalFiles": "OPTIONAL FILES", + "archivedFiles": "ARCHIVED FILES", + "fileListTitle": "{title} ({count})", + "@fileListTitle": { + "placeholders": { + "title": { "type": "String" }, + "count": { "type": "int" } + } + }, + "verifiedModAuthor": "Verified Mod Author", + "viewingProfile": "VIEWING PROFILE", + "publishedMods": "PUBLISHED MODS", + "userInformation": "USER INFORMATION", + "viewUserStats": "VIEW USER STATS", + "joinedLabel": "Joined: ", + "modsLabel": "Mods: ", + "postsLabel": "Posts: ", + "uniqueDlsLabel": "Unique-DLs: ", + "viewsLabel": "Views: ", + "kudosLabel": "Kudos: ", + "contributedLabel": "Contributed: ", + "viewOnNexusMods": "VIEW ON NEXUS MODS", + "collectionMods": "Collection Mods", + "modNameVersion": "{name} ({version})", + "@modNameVersion": { + "placeholders": { + "name": { "type": "String" }, + "version": { "type": "String" } + } + }, + "downloadCollectionMods": "DOWNLOAD COLLECTION MODS", + "loadingMods": "LOADING MODS...", + "collections": "COLLECTIONS", + "manageCollectionsSub": "MANAGE MOD COLLECTIONS & CURATE COSMETIC MODS", + "browser": "Browser", + "paginationInfo": "{page}/{totalPages}", + "@paginationInfo": { + "placeholders": { + "page": { "type": "int" }, + "totalPages": { "type": "int" } + } + }, + "modScope": "MOD SCOPE", + "serverNotFound": "The server you were playing on could not be found.", + "nexusModsAuthorization": "NexusMods Authorization", + "nexusAuthDescription": "You need to authorize KyberLauncher on NexusMods to use the mod browser.", + "authorizeKyber": "Authorize Kyber", + "noReportsAvailable": "No reports available", + "reportedBy": "Reported by ", + "forLabel": " for ", + "onLabel": " on ", + "descriptionLabel": "Description", + "evidenceCountLabel": "Evidence ({count})", + "@evidenceCountLabel": { + "placeholders": { + "count": { "type": "int" } + } + }, + "reportViewTitle": "REPORT VIEW", + "viewReportsAndEvidence": "VIEW REPORTS AND EVIDENCE", + "reportsTab": "REPORTS", + "punishmentsTab": "PUNISHMENTS", + "reportRejected": "Report rejected", + "takeAction": "TAKE ACTION", + "reportedByPlayer": "By {name}", + "@reportedByPlayer": { + "placeholders": { + "name": { "type": "String" } + } + }, + "daysDuration": "{days} Days", + "@daysDuration": { + "placeholders": { + "days": { "type": "int" } + } + }, + "permanent": "Permanent", + "bannedByModerator": "Banned By {name}", + "@bannedByModerator": { + "placeholders": { + "name": { "type": "String" } + } + }, + "durationLabel": "Duration: {duration}", + "@durationLabel": { + "placeholders": { + "duration": { "type": "String" } + } + }, + "issuedLabel": "Issued: {date}", + "@issuedLabel": { + "placeholders": { + "date": { "type": "String" } + } + }, + "statusFilter": "STATUS", + "reportsTitle": "REPORTS", + "totalReportsHeader": "TOTAL REPORTS", + "latestReportHeader": "LATEST REPORT", + "latestReasonHeader": "LATEST REASON", + "statusHeader": "STATUS", + "errorLoadingReports": "Error loading reports: {message}", + "reasonWithCount": "{reasonName} ({count})", + "armsecCaseFile": "ARMSEC - CASE FILE", + "evidenceAndDataSummary": "EVIDENCE AND DATA SUMMARY", + "errorLoadingUser": "Error loading user", + "userNotFound": "User not found", + "kyberUser": "KYBER USER", + "open": "OPEN", + "options": "OPTIONS", + "@errorLoadingReports": { + "placeholders": { + "message": { "type": "String" } + } + }, + "@reasonWithCount": { + "placeholders": { + "reasonName": { "type": "String" }, + "count": { "type": "String" } + } + }, + "unknownServer": "Unknown Server", + "hostedBy": "Hosted by {creator}", + "@hostedBy": { + "placeholders": { + "creator": { "type": "String" } + } + }, + "backToServerBrowser": "Back to Server Browser", + "eventLog": "Event Log", + "lightSide": "Light Side", + "darkSide": "Dark Side", + "reportPlayer": "Report Player", + "region": "REGION", + "serverType": "SERVER TYPE", + "gameType": "GAME TYPE", + "gameMode": "GAME MODE", + "warning": "WARNING", + "warningMessageFallback": "Warning message not available.", + "newButton": "NEW", + "moderate": "MODERATE", + "manage": "MANAGE", + "rotation": "ROTATION", + "searchPlaceholder": "Search ...", + "noServersFound": "No servers found", + "serverBrowser": "Server Browser", + "optionsHeader": "OPTIONS", + "moderatorsHeader": "MODERATORS", + "bannedPlayersHeader": "BANNED PLAYERS", + "playButton": "PLAY", + "spectateButton": "SPECTATE", + "copyLinkButton": "COPY LINK", + "copiedToClipboard": "Copied to clipboard!", + "exportBans": "EXPORT BANS", + "importBans": "IMPORT BANS", + "kickAll": "KICK ALL", + "banAll": "BAN ALL", + "eventLogHeader": "Event Log", + "lightSideHeader": "Light Side", + "darkSideHeader": "Dark Side", + "userManager": "User Manager", + "consoleHint": "/COMMAND OR SEND MESSAGE", + "untilLabel": "UNTIL: {date}", + "reasonLabel": "REASON: {reason}", + "unban": "UNBAN", + "modify": "MODIFY", + "playerUnbanned": "Player unbanned", + "timeElapsed": "Time has elapsed", + "daysRemaining": "{count} day(s)", + "hoursRemaining": "{count} hour(s)", + "minutesRemaining": "{count} minute(s)", + "secondsRemaining": "{count} second(s)", + "remaining": "{time} remaining", + "promoteUserTooltip": "Promote User", + "errorOccurred": "An error occurred", + "swapPlayerTooltip": "Swap Player", + "kickPlayerTooltip": "Kick player", + "banPlayerTooltip": "Ban player", + "off": "OFF", + "on": "ON", + "home": "HOME", + "host": "HOST", + "stats": "STATS", + "settings": "SETTINGS", + "ingamePanel": "INGAME PANEL", + "extractingFileWithProgress": "EXTRACTING FILE ({extracted}/{total})", + "extractingFile": "EXTRACTING FILE", + "downloadingWithProgress": "DOWNLOADING ({percentage}% {downloaded}/{total})", + "downloadManager": "DOWNLOAD MANAGER", + "createNewCollection": "CREATE NEW COLLECTION", + "pageNotFoundTitle": "Page Not Found", + "pageNotFoundFallback": "page not found", + "goToHomePage": "Go to home page", + "newCollectionTitle": "New Collection", + "@extractingFileWithProgress": { + "placeholders": { + "extracted": { + "type": "String" + }, + "total": { + "type": "String" + } + } + }, + "@downloadingWithProgress": { + "placeholders": { + "percentage": { + "type": "String" + }, + "downloaded": { + "type": "String" + }, + "total": { + "type": "String" + } + } + }, + "@untilLabel": { + "placeholders": { + "date": { "type": "String" } + } + }, + "@reasonLabel": { + "placeholders": { + "reason": { "type": "String" } + } + }, + "@daysRemaining": { + "placeholders": { + "count": { "type": "int" } + } + }, + "@hoursRemaining": { + "placeholders": { + "count": { "type": "int" } + } + }, + "@minutesRemaining": { + "placeholders": { + "count": { "type": "int" } + } + }, + "@secondsRemaining": { + "placeholders": { + "count": { "type": "int" } + } + }, + "@remaining": { + "placeholders": { + "time": { "type": "String" } + } + }, + "eaAccount": "EA Account", + "finish": "FINISH", + "skip": "SKIP", + "searchFailed": "Failed to search players: {message}", + "unexpectedError": "An unexpected error occurred: {error}", + "searchKyberUser": "Search for a kyber user", + "noResultsFound": "No results found for \"{query}\".", + "friendsCount": "FRIENDS ({count})", + "recentlyViewed": "RECENTLY VIEWED", + "userSearchHistory": "USER SEARCH HISTORY", + "online": "Online", + "playingStatus": "Playing {status}", + "@searchFailed": { + "placeholders": { + "message": { "type": "String" } + } + }, + "@unexpectedError": { + "placeholders": { + "error": { "type": "String" } + } + }, + "@noResultsFound": { + "placeholders": { + "query": { "type": "String" } + } + }, + "@friendsCount": { + "placeholders": { + "count": { "type": "int" } + } + }, + "@playingStatus": { + "placeholders": { + "status": { "type": "String" } + } + }, + "selectCollectionIcon": "Select Collection Icon", + "collectionLabel": "COLLECTION", + "collectionDescription": "A COLLECTION IS A READY TO PLAY MOD LIST", + "modsCount": "{count} Mods", + "collectionNamePlaceholder": "Collection Name", + "saveButton": "SAVE", + "betterSabersPlugin": "BETTER SABERS (PLUGIN)", + "reloadingMods": "Reloading mods", + "generatedBetterSabers": "Generated BetterSabers", + "exportCollectionTar": "EXPORT COLLECTION TAR", + "editCollection": "EDIT COLLECTION", + "createACopy": "CREATE A COPY", + "collectionCopyName": "{title} (Copy)", + "deleteCollection": "DELETE COLLECTION", + "largeModsWarning": "Using multiple large or complex mods isn't recommended, as they often conflict with each other.", + "@modsCount": { + "placeholders": { + "count": { + "type": "int" + } + } + }, + "@collectionCopyName": { + "placeholders": { + "title": { + "type": "String" + } + } + }, + "myMods": "MY MODS", + "nameHeader": "NAME", + "categoryHeader": "CATEGORY", + "sizeHeader": "SIZE", + "typeHeader": "TYPE", + "addHeader": "ADD", + "selectedCount": "{count}", + "@selectedCount": { + "placeholders": { + "count": { + "type": "int" + } + } + }, + "all": "ALL", + "searchHint": "SEARCH...", + "noPlayersFound": "No players found", + "moveModsDirectory": "MOVE MODS DIRECTORY", + "invalidDirectoryWarning": "The current mods directory is invalid because it contains non-ASCII characters. ", + "selectNewDirectoryDesc": "Please select the new directory where you want to move the mods directory. Mods will not be moved. Only the directory will be changed.", + "nonAsciiWarning": "Non-ASCII characters in the path are not allowed (e.g. Korean, Greek, etc.).", + "adminPermissionError": "You can't select a directory that requires admin permissions. Please select another directory.", + "selectedDirNonAsciiError": "The selected directory contains non-ASCII characters. Please select a different directory.", + "browse": "Browse", + "resetToDefault": "Reset to default", + "defaultDirNonAsciiError": "The default mods directory contains non-ASCII characters. Please select a different directory.", + "modInformation": "MOD INFORMATION", + "viewInformationAboutMod": "VIEW INFORMATION ABOUT A MOD", + "modVersionAuthorDisplay": "{version} - {author}", + "@modVersionAuthorDisplay": { + "placeholders": { + "version": { + "type": "String" + }, + "author": { + "type": "String" + } + } + }, + "descriptionLabel": "DESCRIPTION", + "imagesLabel": "IMAGES", + "affectedFilesLabel": "AFFECTED FILES", + "unknownResourceName": "Unknown resource name", + "abortingNexusLogin": "Aborting NexusMods login", + "installWebViewError": "Please install WebView to use this feature.", + "continueText": "CONTINUE", + "wait": "WAIT", + "win7CompatibilityDetected": "Windows 7 Compatibility Mode detected", + "disableWin7CompMode": "To login to NexusMods, please disable Windows 7 Compatibility Mode for Kyber Launcher.", + "reEnableWin7CompMode": "After you have logged in, you need to re-enable Windows 7 Compatibility Mode.", + "checkingWebView": "Checking WebView installation...", + "nexusLoginIntro": "To continue, you will need to log in with your NexusMods account in the browser that is about to open.", + "nexusLoginDataNotice": "The data is processed locally and will only be sent to Nexusmods.\nYou can also enable/disable this feature later in the settings menu.", + "playersHeader": "PLAYERS", + "eventsAndAnnouncements": "EVENTS & ANNOUNCEMENTS", + "viewUpcomingEventsDesc": "VIEW UPCOMING EVENTS & RECEIVE ANNOUNCEMENTS", + "sortByTitle": "SORT BY", + "timeFilterTitle": "TIME FILTER", + "itemsPerPageTitle": "ITEMS PER PAGE", + "allTime": "All time", + "oneYear": "1 year", + "oneMonth": "1 month", + "twoWeeks": "2 weeks", + "oneWeek": "1 week", + "oneDay": "1 day", + "all": "ALL", + "@hoursFormat": { + "placeholders": { + "count": { + "type": "String" + } + } + }, + "@minutesFormat": { + "placeholders": { + "count": { + "type": "int" + } + } + }, + "@secondsFormat": { + "placeholders": { + "count": { + "type": "int" + } + } + }, + "hoursFormat": "{count} HRS", + "minutesFormat": "{count} MIN", + "secondsFormat": "{count} SEC", + "noStatsFound": "No stats found", + "mostPlayed": "MOST PLAYED", + "totalKillsLabel": "TOTAL KILLS", + "totalDeathsLabel": "TOTAL DEATHS", + "kdRatioLabel": "K/D RATIO", + "assistsLabel": "ASSISTS", + "damageDoneLabel": "DAMAGE DONE", + "suicidesLabel": "SUICIDES", + "gamesWonLabel": "GAMES WON", + "gamesLostLabel": "GAMES LOST", + "winRateLabel": "WIN RATE", + "unitsSection": "UNITS", + "vehiclesSection": "VEHICLES", + "starfighterSection": "STARFIGHTER", + "serverSectionTitle": "SERVER", + "maxPlayersLabel": "MAX PLAYERS", + "maxSpectatorsLabel": "Max Spectators", + "privacySectionTitle": "Privacy", + "passwordLabel": "Password", + "passwordPlaceholder": "EXAMPLE", + "modeHeroesVersusVillains": "Heroes Versus Villains", + "modeGalacticAssault": "Galactic Assault", + "modeSupremacy": "Supremacy", + "modeCoOpAttack": "CO-OP Attack", + "modeCoOpDefend": "CO-OP Defend", + "modeStrike": "Strike", + "modeExtraction": "Extraction", + "modeBlast": "Blast", + "modeEwokHunt": "Ewok Hunt", + "modeJetpackCargo": "Jetpack Cargo", + "modeStarfighterAssault": "Starfighter Assault", + "modeHeroStarfighters": "Hero Starfighters", + "modeHeroShowdown": "Hero Showdown", + "mapTatooineMosEisley": "Tatooine - Mos Eisley", + "mapTatooineJabbasPalace": "Tatooine - Jabba's Palace", + "mapRepublicVenator": "Republic Venator", + "mapSeparatistDreadnought": "Separatist Dreadnought", + "mapResurgentStarDestroyer": "Resurgent Star Destroyer", + "mapMc85StarCruiser": "MC85 Star Cruiser", + "mapNabooPalaceHangar": "Naboo - Palace Hangar", + "mapEndorResearchStation9": "Endor - Research Station 9", + "mapEndorEwokVillage": "Endor - Ewok Village", + "mapNabooTheedPalace": "Naboo - Theed Palace", + "mapCrait": "Crait", + "activeRotation": "Active Rotation", + "importLabel": "Import", + "importMapRotation": "Import Map Rotation", + "invalidFileFormat": "Invalid file format", + "mapRotationImported": "Map rotation imported", + "noMapsActive": "No maps active", + "customMapNotFoundTooltip": "Custom map not found, this can happen if the map is not part of the current mod collection.\nThis map will be ignored when starting the server.", + "notFound": "Not found" +} \ No newline at end of file diff --git a/Launcher/lib/main.dart b/Launcher/lib/main.dart index d7d6ec0b..68823f60 100644 --- a/Launcher/lib/main.dart +++ b/Launcher/lib/main.dart @@ -9,6 +9,7 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_inappwebview/flutter_inappwebview.dart'; import 'package:flutter_js/flutter_js.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; +import 'package:kyber_launcher/gen/l10n/app_localizations.dart'; import 'package:form_builder_validators/localization/l10n.dart'; import 'package:grpc/grpc.dart'; import 'package:hive_ce_flutter/hive_flutter.dart'; @@ -249,103 +250,123 @@ class _AppState extends State { child: HiveListener( box: box, keys: const ['locale', 'activeColor'], - builder: (_) => FluentApp.router( - title: 'KYBER Launcher', - color: kActiveColor, - darkTheme: FluentThemeData( - accentColor: kActiveColor.toAccentColor( - darkFactor: 0, - darkerFactor: 0, - darkestFactor: 0, - lighterFactor: 0, - lightestFactor: 0, - lightFactor: 0, - ), - activeColor: kActiveColor, - brightness: Brightness.dark, - fontFamily: FontFamily.battlefrontUI, - radioButtonTheme: RadioButtonThemeData( - foregroundColor: WidgetStateProperty.resolveWith((states) { - if (states.contains(WidgetState.hovered)) { - return kInactiveColor; - } - return kActiveColor; - }), - ), - scrollbarTheme: ScrollbarThemeData( - thickness: 3, - hoveringThickness: 3, - scrollbarPressingColor: kActiveColor, - trackBorderColor: Colors.transparent, - hoveringTrackBorderColor: kWhiteBackgroundColor, - hoveringMainAxisMargin: 0, - crossAxisMargin: 0, - padding: EdgeInsets.zero, - hoveringPadding: EdgeInsets.zero, - hoveringCrossAxisMargin: 0, - mainAxisMargin: 0, - backgroundColor: Colors.transparent, - ), - typography: Typography.fromBrightness(brightness: .dark).apply( - fontFamily: FontFamily.battlefrontUI, - ), - ), - backButtonDispatcher: RootBackButtonDispatcher(), - themeMode: ThemeMode.dark, - locale: AppLocale.getLocale(), - localizationsDelegates: const [ - ...GlobalMaterialLocalizations.delegates, - FormBuilderLocalizations.delegate, - ], - supportedLocales: const [Locale('en')], - debugShowCheckedModeBanner: false, - builder: (context, child) { - child = WindowController( - child: GraphqlProvider( - child: child!, + builder: (_) { + final currentLocale = AppLocale.getLocale(); + final fontFamily = currentLocale.languageCode == 'en' + ? FontFamily.battlefrontUI + : FontFamily.iBMPlexMono; + + return FluentApp.router( + title: 'KYBER Launcher', + color: kActiveColor, + darkTheme: FluentThemeData( + accentColor: kActiveColor.toAccentColor( + darkFactor: 0, + darkerFactor: 0, + darkestFactor: 0, + lighterFactor: 0, + lightestFactor: 0, + lightFactor: 0, ), - ); - - return Builder( - builder: (context) { - return DisableAcrylic( - child: MultiBlocProvider( - providers: [ - BlocProvider(create: (_) => StatusCubit()), - BlocProvider(create: (_) => MaximaCubit()), - BlocProvider(create: (_) => MaximaRtmCubit()), - BlocProvider(create: (_) => MapRotationCubit()), - BlocProvider(create: (_) => TutorialCubit()), - BlocProvider(create: (_) => KyberStatusCubit()), - BlocProvider(create: (_) => ModBrowserCubit()), - BlocProvider(create: (_) => ServerListCubit()), - BlocProvider(create: (_) => ModerationServersCubit()), - BlocProvider(create: (_) => ModerationCubit()), - BlocProvider(create: (_) => ServerBrowserCubit()), - BlocProvider(create: (_) => EventCubit()), - BlocProvider(create: (_) => KyberProxyCubit()), - BlocProvider(create: (_) => ModsListCubit()), - BlocProvider(create: (_) => StatsCubit()), - BlocProvider(create: (_) => IngameViewCubit()), - BlocProvider(create: (_) => DownloadCubit(), lazy: false), - BlocProvider( - create: (_) => LightswitchCubit(), - lazy: false, + activeColor: kActiveColor, + brightness: Brightness.dark, + fontFamily: fontFamily, + radioButtonTheme: RadioButtonThemeData( + foregroundColor: WidgetStateProperty.resolveWith((states) { + if (states.contains(WidgetState.hovered)) { + return kInactiveColor; + } + return kActiveColor; + }), + ), + scrollbarTheme: ScrollbarThemeData( + thickness: 3, + hoveringThickness: 3, + scrollbarPressingColor: kActiveColor, + trackBorderColor: Colors.transparent, + hoveringTrackBorderColor: kWhiteBackgroundColor, + hoveringMainAxisMargin: 0, + crossAxisMargin: 0, + padding: EdgeInsets.zero, + hoveringPadding: EdgeInsets.zero, + hoveringCrossAxisMargin: 0, + mainAxisMargin: 0, + backgroundColor: Colors.transparent, + ), + typography: Typography.fromBrightness(brightness: .dark).apply( + fontFamily: fontFamily, + ), + ), + backButtonDispatcher: RootBackButtonDispatcher(), + themeMode: ThemeMode.dark, + locale: currentLocale, + localizationsDelegates: const [ + AppLocalizations.delegate, + ...GlobalMaterialLocalizations.delegates, + FormBuilderLocalizations.delegate, + ], + // NOTE: Added all requested languages support here + supportedLocales: const [ + Locale('en'), // English + Locale('de'), // German + Locale('fr'), // French + Locale('es'), // Spanish + Locale('pl'), // Polish + Locale('ru'), // Russian + Locale('pt'), // Portuguese + Locale('uk'), // Ukrainian + Locale('sv'), // Swedish + Locale('nl'), // Dutch + ], + debugShowCheckedModeBanner: false, + builder: (context, child) { + child = WindowController( + child: GraphqlProvider( + child: child!, + ), + ); + + return Builder( + builder: (context) { + return DisableAcrylic( + child: MultiBlocProvider( + providers: [ + BlocProvider(create: (_) => StatusCubit()), + BlocProvider(create: (_) => MaximaCubit()), + BlocProvider(create: (_) => MaximaRtmCubit()), + BlocProvider(create: (_) => MapRotationCubit()), + BlocProvider(create: (_) => TutorialCubit()), + BlocProvider(create: (_) => KyberStatusCubit()), + BlocProvider(create: (_) => ModBrowserCubit()), + BlocProvider(create: (_) => ServerListCubit()), + BlocProvider(create: (_) => ModerationServersCubit()), + BlocProvider(create: (_) => ModerationCubit()), + BlocProvider(create: (_) => ServerBrowserCubit()), + BlocProvider(create: (_) => EventCubit()), + BlocProvider(create: (_) => KyberProxyCubit()), + BlocProvider(create: (_) => ModsListCubit()), + BlocProvider(create: (_) => StatsCubit()), + BlocProvider(create: (_) => IngameViewCubit()), + BlocProvider(create: (_) => DownloadCubit(), lazy: false), + BlocProvider( + create: (_) => LightswitchCubit(), + lazy: false, + ), + ], + child: KyberBackground( + child: child ?? const SizedBox.shrink(), ), - ], - child: KyberBackground( - child: child ?? const SizedBox.shrink(), ), - ), - ); - }, - ); - }, - routeInformationParser: router.routeInformationParser, - routerDelegate: router.routerDelegate, - routeInformationProvider: router.routeInformationProvider, - ), + ); + }, + ); + }, + routeInformationParser: router.routeInformationParser, + routerDelegate: router.routerDelegate, + routeInformationProvider: router.routeInformationProvider, + ); + }, ), ); } -} +} \ No newline at end of file diff --git a/Launcher/lib/shared/ui/elements/filter_dropdown.dart b/Launcher/lib/shared/ui/elements/filter_dropdown.dart index fdc8e2a1..b72c341a 100644 --- a/Launcher/lib/shared/ui/elements/filter_dropdown.dart +++ b/Launcher/lib/shared/ui/elements/filter_dropdown.dart @@ -4,6 +4,7 @@ import 'package:kyber_launcher/core/core.dart'; import 'package:kyber_launcher/features/mod_browser/screens/mod_details.dart'; import 'package:kyber_launcher/gen/assets.gen.dart'; import 'package:kyber_launcher/gen/fonts.gen.dart'; +import 'package:kyber_launcher/gen/l10n/app_localizations.dart'; import 'package:kyber_launcher/shared/ui/ui.dart'; import 'package:window_manager/window_manager.dart'; @@ -369,6 +370,10 @@ class _FilterSelector extends StatelessWidget { @override Widget build(BuildContext context) { + final l10n = AppLocalizations.of(context)!; + final isEn = Localizations.localeOf(context).languageCode == 'en'; + final currentFont = isEn ? FontFamily.battlefrontUI : 'BattlefrontGlobal'; + return GridView.builder( shrinkWrap: true, gridDelegate: mt.SliverGridDelegateWithMaxCrossAxisExtent( @@ -417,13 +422,13 @@ class _FilterSelector extends StatelessWidget { style: TextStyle( color: hovered || isAllSelected ? Colors.black : Colors.white, fontSize: 14, - fontFamily: FontFamily.battlefrontUI, + fontFamily: currentFont, ), - child: const Text( - 'ALL', + child: Text( + l10n.all, style: TextStyle( fontSize: 14, - fontFamily: FontFamily.battlefrontUI, + fontFamily: currentFont, ), textAlign: TextAlign.center, ), @@ -476,7 +481,7 @@ class _FilterSelector extends StatelessWidget { style: TextStyle( color: hovered || isSelected ? Colors.black : Colors.white, fontSize: 14, - fontFamily: FontFamily.battlefrontUI, + fontFamily: currentFont, ), child: Text( mode.title.toUpperCase(), @@ -501,22 +506,26 @@ class _KyberSearchInput extends StatelessWidget { @override Widget build(BuildContext context) { + final l10n = AppLocalizations.of(context)!; + final isEn = Localizations.localeOf(context).languageCode == 'en'; + final currentFont = isEn ? FontFamily.battlefrontUI : 'BattlefrontGlobal'; + return mt.TextFormField( - style: const mt.TextStyle( - fontFamily: FontFamily.battlefrontUI, + style: mt.TextStyle( + fontFamily: currentFont, fontSize: 15, height: 1, ), - decoration: const mt.InputDecoration( + decoration: mt.InputDecoration( isDense: true, border: mt.InputBorder.none, enabledBorder: mt.InputBorder.none, focusedBorder: mt.InputBorder.none, - contentPadding: EdgeInsets.symmetric(horizontal: 10, vertical: 0.5), - hintText: 'SEARCH...', + contentPadding: const EdgeInsets.symmetric(horizontal: 10, vertical: 0.5), + hintText: l10n.searchHint, hintStyle: TextStyle( color: kInactiveColor, - fontFamily: FontFamily.battlefrontUI, + fontFamily: currentFont, fontSize: 15, height: 1, ), @@ -524,4 +533,4 @@ class _KyberSearchInput extends StatelessWidget { onChanged: onChanged, ); } -} +} \ No newline at end of file diff --git a/Launcher/lib/shared/ui/elements/table/table_elements/kyber_table_switch.dart b/Launcher/lib/shared/ui/elements/table/table_elements/kyber_table_switch.dart index 1c5ff706..2aed9587 100644 --- a/Launcher/lib/shared/ui/elements/table/table_elements/kyber_table_switch.dart +++ b/Launcher/lib/shared/ui/elements/table/table_elements/kyber_table_switch.dart @@ -1,6 +1,7 @@ import 'package:fluent_ui/fluent_ui.dart'; import 'package:kyber_launcher/core/config/colors.dart'; import 'package:kyber_launcher/gen/fonts.gen.dart'; +import 'package:kyber_launcher/gen/l10n/app_localizations.dart'; import 'package:tinycolor2/tinycolor2.dart'; class KyberTableSwitch extends StatefulWidget { @@ -44,32 +45,41 @@ class _KyberTableSwitchState extends State { @override Widget build(BuildContext context) { + final l10n = AppLocalizations.of(context)!; + final isEn = Localizations.localeOf(context).languageCode == 'en'; + final currentFont = isEn ? FontFamily.battlefrontUI : 'BattlefrontGlobal'; + return SizedBox( height: 34, child: Row( children: [ Expanded( child: buildItem( - widget.disabledText ?? 'OFF', + widget.disabledText ?? l10n.off, widget.value == false, + currentFont, ), ), Expanded( - child: buildItem(widget.enabledText ?? 'ON', widget.value == true), + child: buildItem( + widget.enabledText ?? l10n.on, + widget.value == true, + currentFont, + ), ), ], ), ); } - Widget buildItem(String text, bool enabled) { + Widget buildItem(String text, bool enabled, String currentFont) { return AnimatedContainer( duration: Duration(milliseconds: enabled ? 100 : 20), decoration: BoxDecoration( color: enabled ? widget.hover && !disabled - ? kActiveColor - : kWhiteColor.withOpacity(disabled ? .7 : 1) + ? kActiveColor + : kWhiteColor.withOpacity(disabled ? .7 : 1) : null, borderRadius: BorderRadius.circular(2), boxShadow: enabled && widget.hover && !disabled @@ -86,26 +96,27 @@ class _KyberTableSwitchState extends State { child: Text( text.toUpperCase(), style: FluentTheme.of(context).typography.body!.copyWith( - fontSize: 16, - fontWeight: FontWeight.bold, - shadows: widget.hover && !disabled && !enabled - ? [ - Shadow( - color: kActiveColor.withOpacity(.8), - blurRadius: 8, - ), - ] - : null, - color: enabled - ? widget.hover && !disabled - ? Colors.black.mix(kActiveColor, 35) - : Colors.black - : widget.hover - ? kActiveColor - : null, - ), + fontFamily: currentFont, + fontSize: 16, + fontWeight: FontWeight.bold, + shadows: widget.hover && !disabled && !enabled + ? [ + Shadow( + color: kActiveColor.withOpacity(.8), + blurRadius: 8, + ), + ] + : null, + color: enabled + ? widget.hover && !disabled + ? Colors.black.mix(kActiveColor, 35) + : Colors.black + : widget.hover + ? kActiveColor + : null, + ), ), ), ); } -} +} \ No newline at end of file diff --git a/Launcher/lib/shared/ui/navigation_bar/navigation_bar_list.dart b/Launcher/lib/shared/ui/navigation_bar/navigation_bar_list.dart index e2737a84..5070f19e 100644 --- a/Launcher/lib/shared/ui/navigation_bar/navigation_bar_list.dart +++ b/Launcher/lib/shared/ui/navigation_bar/navigation_bar_list.dart @@ -6,6 +6,8 @@ import 'package:kyber_launcher/core/config/colors.dart'; import 'package:kyber_launcher/core/routing/app_router.dart'; import 'package:kyber_launcher/features/download_manager/models/download_state.dart'; import 'package:kyber_launcher/features/download_manager/providers/download_manager_cubit.dart'; +import 'package:kyber_launcher/gen/fonts.gen.dart'; +import 'package:kyber_launcher/gen/l10n/app_localizations.dart'; import 'package:kyber_launcher/shared/ui/navigation_bar/navigation_bar_seperator.dart'; import 'package:kyber_launcher/shared/ui/navigation_bar/widgets/navigation_bar_item.dart'; import 'package:kyber_launcher/shared/ui/navigation_bar/widgets/navigation_bar_sub_item.dart'; @@ -36,18 +38,19 @@ class _NavigationBarListState extends State { bool _showPositioned = false; int? _hoveringIndex; - List getItems() => [ - NavigationBarEntry('HOME', 'home'), - NavigationBarEntry('HOST', 'server_host'), - NavigationBarEntry('STATS', 'stats'), - NavigationBarEntry('MODS', 'mods'), - NavigationBarEntry('SETTINGS', 'settings'), - ]; + List getItems(AppLocalizations l10n) => [ + NavigationBarEntry(l10n.home, 'home'), + NavigationBarEntry(l10n.host, 'server_host'), + NavigationBarEntry(l10n.stats, 'stats'), + NavigationBarEntry(l10n.mods, 'mods'), + NavigationBarEntry(l10n.settings, 'settings'), + ]; @override void didUpdateWidget(covariant NavigationBarList oldWidget) { if (oldWidget.route != widget.route) { - final items = getItems(); + final l10n = AppLocalizations.of(context)!; + final items = getItems(l10n); final index = items.indexWhere( (element) => element.route == widget.route.split('/').last.split('?').first, @@ -64,6 +67,10 @@ class _NavigationBarListState extends State { @override Widget build(BuildContext context) { + final l10n = AppLocalizations.of(context)!; + final isEn = Localizations.localeOf(context).languageCode == 'en'; + final currentFont = isEn ? FontFamily.battlefrontUI : 'BattlefrontGlobal'; + return Container( height: 50, padding: const EdgeInsets.only(left: 20, top: 10), @@ -104,15 +111,15 @@ class _NavigationBarListState extends State { scrollDirection: Axis.horizontal, separatorBuilder: (context, index) => Transform.rotate( - angle: 18 * 3.14 / 180, - child: UnconstrainedBox( - child: Container( - height: 20, - width: 2, - color: kGrayColor, - ), - ), + angle: 18 * 3.14 / 180, + child: UnconstrainedBox( + child: Container( + height: 20, + width: 2, + color: kGrayColor, ), + ), + ), itemBuilder: (context, index) => NavigationBarSubItem( isLast: index == routes.length - 1, route: routes.elementAt(index), @@ -152,7 +159,7 @@ class _NavigationBarListState extends State { ); } - final items = getItems(); + final items = getItems(l10n); return Stack( clipBehavior: Clip.none, @@ -172,7 +179,7 @@ class _NavigationBarListState extends State { index == _activeItem || index == _activeItem + 1; final hover = _hoveringIndex == index - 1 || - _hoveringIndex == index; + _hoveringIndex == index; return NavigationBarSeperator( active: active, hover: _hovering && hover, @@ -244,4 +251,4 @@ class _NavigationBarListState extends State { ), ); } -} +} \ No newline at end of file diff --git a/Launcher/pubspec.yaml b/Launcher/pubspec.yaml index 5b2f0a51..5d8b2d9a 100644 --- a/Launcher/pubspec.yaml +++ b/Launcher/pubspec.yaml @@ -52,6 +52,7 @@ dependencies: url: https://github.com/GSirma/flutter_js.git flutter_localizations: sdk: flutter + intl: any flutter_markdown: ^0.7.1 flutter_staggered_grid_view: ^0.7.0 flutter_svg: ^2.0.6 @@ -209,3 +210,4 @@ flutter: - assets/js/bundle.js - assets/videos/ uses-material-design: true + generate: true