feat(spoof): port session spoofing screen with device presets

This commit is contained in:
klockky
2026-04-11 00:21:31 +03:00
parent 8536ec2508
commit 23ef587015
13 changed files with 2398 additions and 541 deletions
+45 -8
View File
@@ -5,6 +5,7 @@ import '../core/config/config.dart';
import '../core/config/countries.dart';
import '../core/protocol/opcode_map.dart';
import '../core/protocol/packet.dart';
import '../core/storage/spoofing_service.dart';
import '../core/transport/connection.dart';
import '../core/transport/dispatcher.dart';
import '../core/transport/receiver.dart';
@@ -112,7 +113,7 @@ class Api {
Future<Packet> sendHandshake() async {
final deviceInfo = DeviceInfoPlugin();
final deviceType = (Platform.isLinux || Platform.isWindows)
String deviceType = (Platform.isLinux || Platform.isWindows)
? 'DESKTOP'
: (Platform.isAndroid)
? 'ANDROID'
@@ -120,10 +121,16 @@ class Api {
String osVersion = '';
String deviceName = 'Unknown';
String architecture = 'arm64';
String appVersion = SpoofingService.hardcodedAppVersion;
int buildNumber = SpoofingService.hardcodedBuildNumber;
String screen = '1920x1080';
tz.initializeTimeZones();
final timeZoneName = await FlutterTimezone.getLocalTimezone();
final timezone = timeZoneName.identifier;
String timezone = timeZoneName.identifier;
String locale = 'ru';
String deviceLocale = Platform.localeName.substring(0, 2);
String deviceId = 'a1b2c3d4e5f6a7b8';
if (Platform.isLinux) {
final linuxInfo = await deviceInfo.linuxInfo;
@@ -150,24 +157,54 @@ class Api {
);
}
final spoofed = await SpoofingService.getSpoofedSessionData();
if (spoofed != null) {
deviceType = (spoofed['device_type'] as String?) ?? deviceType;
final sDeviceName = spoofed['device_name'] as String?;
if (sDeviceName != null && sDeviceName.isNotEmpty) {
deviceName = sDeviceName;
}
final sOsVersion = spoofed['os_version'] as String?;
if (sOsVersion != null && sOsVersion.isNotEmpty) osVersion = sOsVersion;
final sScreen = spoofed['screen'] as String?;
if (sScreen != null && sScreen.isNotEmpty) screen = sScreen;
final sTimezone = spoofed['timezone'] as String?;
if (sTimezone != null && sTimezone.isNotEmpty) timezone = sTimezone;
final sLocale = spoofed['locale'] as String?;
if (sLocale != null && sLocale.isNotEmpty) {
locale = sLocale;
deviceLocale = sLocale.split(RegExp(r'[-_]')).first;
}
final sDeviceId = spoofed['device_id'] as String?;
if (sDeviceId != null && sDeviceId.isNotEmpty) deviceId = sDeviceId;
appVersion = (spoofed['app_version'] as String?) ?? appVersion;
architecture = (spoofed['arch'] as String?) ?? architecture;
final sBuild = spoofed['build_number'];
if (sBuild is int) {
buildNumber = sBuild;
} else if (sBuild is String) {
buildNumber = int.tryParse(sBuild) ?? buildNumber;
}
}
_userAgent = {
'deviceType': deviceType,
'locale': 'ru',
'deviceLocale': Platform.localeName.substring(0, 2),
'locale': locale,
'deviceLocale': deviceLocale,
'osVersion': osVersion,
'deviceName': deviceName,
'appVersion': '26.8.1',
'screen': '1920x1080',
'appVersion': appVersion,
'screen': screen,
'timezone': timezone,
'pushDeviceType': 'GCM',
'arch': architecture,
'buildNumber': 6606,
'buildNumber': buildNumber,
};
final payload = <dynamic, dynamic>{
'mt_instanceid': '550e8400-e29b-41d4-a716-446655440000',
'clientSessionId': 42,
'deviceId': 'a1b2c3d4e5f6a7b8',
'deviceId': deviceId,
'userAgent': _userAgent,
};
File diff suppressed because it is too large Load Diff
-31
View File
@@ -1,31 +0,0 @@
class SpoofData {
static const List<String> deviceNames = [
'Samsung Galaxy S23',
'Xiaomi 13 Pro',
'Google Pixel 7',
'OnePlus 11',
];
static const List<String> osVersions = ['12', '13', '14'];
static const List<String> resolutions = [
'1080x2400',
'1440x3200',
'720x1600',
];
static const List<String> deviceIds = [
'a1b2c3d4e5f6',
'f8e7d6c5b4a3',
'9876543210ab',
'1234567890cd',
];
static const List<String> architectures = ['arm64-v8a', 'armeabi-v7a'];
static const String deviceType = 'android';
static const String timezone = 'Europe/Moscow';
static const String locale = 'ru_RU';
static const String appVersion = '26.10.1';
static const String buildNumber = '6728';
}
+27
View File
@@ -0,0 +1,27 @@
import 'package:shared_preferences/shared_preferences.dart';
class SpoofingService {
static const String hardcodedAppVersion = '26.8.1';
static const int hardcodedBuildNumber = 6606;
static Future<Map<String, dynamic>?> getSpoofedSessionData() async {
final prefs = await SharedPreferences.getInstance();
final isEnabled = prefs.getBool('spoofing_enabled') ?? false;
if (!isEnabled) return null;
return {
'device_name': prefs.getString('spoof_devicename'),
'os_version': prefs.getString('spoof_osversion'),
'screen': prefs.getString('spoof_screen'),
'timezone': prefs.getString('spoof_timezone'),
'locale': prefs.getString('spoof_locale'),
'device_id': prefs.getString('spoof_deviceid'),
'device_type': prefs.getString('spoof_devicetype'),
'app_version': prefs.getString('spoof_appversion') ?? hardcodedAppVersion,
'arch': prefs.getString('spoof_arch') ?? 'arm64-v8a',
'build_number':
prefs.getInt('spoof_buildnumber') ?? hardcodedBuildNumber,
};
}
}
+2 -2
View File
@@ -12,7 +12,7 @@ import 'code_confirmation_screen.dart';
import 'select_country_screen.dart';
import 'proxy_settings_sheet.dart';
import 'server_settings_sheet.dart';
import 'spoff_redacted_screen.dart';
import '../profile/spoof_screen.dart';
import '../../widgets/custom_notification.dart';
import '../../../main.dart';
@@ -522,7 +522,7 @@ class _LoginScreenState extends State<LoginScreen> {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const SpoffRedactedScreen(),
builder: (context) => const SpoofScreen(),
),
);
},
@@ -1,217 +0,0 @@
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:komet/core/config/spoof_data.dart';
import 'package:komet/frontend/widgets/custom_notification.dart';
class SpoffRedactedScreen extends StatefulWidget {
const SpoffRedactedScreen({super.key});
@override
State<SpoffRedactedScreen> createState() => _SpoffRedactedScreenState();
}
class _SpoffRedactedScreenState extends State<SpoffRedactedScreen> {
final TextEditingController _deviceNameController = TextEditingController();
final TextEditingController _osVersionController = TextEditingController();
final TextEditingController _resolutionController = TextEditingController();
final TextEditingController _deviceIdController = TextEditingController();
final TextEditingController _architectureController = TextEditingController();
@override
void initState() {
super.initState();
_loadSettings();
}
Future<void> _loadSettings() async {
final prefs = await SharedPreferences.getInstance();
final random = Random();
setState(() {
_deviceNameController.text =
prefs.getString('spoof_device_name') ??
SpoofData.deviceNames[random.nextInt(SpoofData.deviceNames.length)];
_osVersionController.text =
prefs.getString('spoof_os_version') ??
SpoofData.osVersions[random.nextInt(SpoofData.osVersions.length)];
_resolutionController.text =
prefs.getString('spoof_resolution') ??
SpoofData.resolutions[random.nextInt(SpoofData.resolutions.length)];
_deviceIdController.text =
prefs.getString('spoof_device_id') ??
SpoofData.deviceIds[random.nextInt(SpoofData.deviceIds.length)];
_architectureController.text =
prefs.getString('spoof_architecture') ??
SpoofData.architectures[random.nextInt(
SpoofData.architectures.length,
)];
});
}
Future<void> _saveSettings() async {
final prefs = await SharedPreferences.getInstance();
await prefs.setString('spoof_device_name', _deviceNameController.text);
await prefs.setString('spoof_os_version', _osVersionController.text);
await prefs.setString('spoof_resolution', _resolutionController.text);
await prefs.setString('spoof_device_id', _deviceIdController.text);
await prefs.setString('spoof_architecture', _architectureController.text);
if (mounted) {
showCustomNotification(context, 'Настройки сохранены');
}
}
@override
void dispose() {
_deviceNameController.dispose();
_osVersionController.dispose();
_resolutionController.dispose();
_deviceIdController.dispose();
_architectureController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
final cs = Theme.of(context).colorScheme;
return Scaffold(
backgroundColor: cs.surface,
appBar: AppBar(
iconTheme: IconThemeData(color: cs.onSurface),
title: Text(
'Подделка спуфа',
style: GoogleFonts.inter(
color: cs.onSurface,
fontWeight: FontWeight.w500,
),
),
backgroundColor: cs.surface,
actions: [
IconButton(
icon: Icon(Icons.check, color: cs.primary),
onPressed: _saveSettings,
),
],
),
body: SingleChildScrollView(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
_buildTextField(
controller: TextEditingController(text: SpoofData.deviceType),
label: 'Тип устройства',
cs: cs,
readOnly: true,
),
const SizedBox(height: 16),
_buildTextField(
controller: _deviceNameController,
label: 'Имя устройства',
cs: cs,
),
const SizedBox(height: 16),
_buildTextField(
controller: _osVersionController,
label: 'Версия ОС',
cs: cs,
),
const SizedBox(height: 16),
_buildTextField(
controller: _resolutionController,
label: 'Разрешение экрана',
cs: cs,
),
const SizedBox(height: 16),
_buildTextField(
controller: TextEditingController(text: SpoofData.timezone),
label: 'Часовой пояс',
cs: cs,
readOnly: true,
),
const SizedBox(height: 16),
_buildTextField(
controller: TextEditingController(text: SpoofData.locale),
label: 'Локаль',
cs: cs,
readOnly: true,
),
const SizedBox(height: 16),
_buildTextField(
controller: _deviceIdController,
label: 'ID устройства',
cs: cs,
),
const SizedBox(height: 16),
_buildTextField(
controller: TextEditingController(text: SpoofData.appVersion),
label: 'Версия приложения',
cs: cs,
readOnly: true,
),
const SizedBox(height: 16),
_buildTextField(
controller: TextEditingController(text: SpoofData.buildNumber),
label: 'Build Number',
cs: cs,
readOnly: true,
),
const SizedBox(height: 16),
_buildTextField(
controller: _architectureController,
label: 'Архитектура',
cs: cs,
),
const SizedBox(height: 32),
],
),
),
);
}
Widget _buildTextField({
required TextEditingController controller,
required String label,
required ColorScheme cs,
bool readOnly = false,
}) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
label,
style: GoogleFonts.inter(
color: cs.onSurfaceVariant,
fontWeight: FontWeight.w500,
fontSize: 14,
),
),
const SizedBox(height: 8),
TextField(
controller: controller,
readOnly: readOnly,
style: GoogleFonts.inter(
color: readOnly ? cs.onSurfaceVariant : cs.onSurface,
fontSize: 15,
),
decoration: InputDecoration(
filled: true,
fillColor: readOnly
? cs.surfaceContainerHighest
: cs.surfaceContainerHigh,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: BorderSide.none,
),
contentPadding: const EdgeInsets.symmetric(
horizontal: 16,
vertical: 14,
),
),
),
],
);
}
}
@@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
import 'package:material_symbols_icons/symbols.dart';
import 'package:package_info_plus/package_info_plus.dart';
import '../../../core/storage/app_database.dart';
import '../../../l10n/app_localizations.dart';
import '../auth/proxy_settings_sheet.dart';
import 'debug_menu_screen.dart';
import 'devices_screen.dart';
@@ -143,7 +144,7 @@ class _SettingsTabState extends State<SettingsTab> {
),
_SettingsItem(
icon: Symbols.shield_lock,
label: 'Подделка данных',
label: AppLocalizations.of(context)!.profileMenuSpoof,
onTap: () {
Navigator.push(
context,
File diff suppressed because it is too large Load Diff
+46 -2
View File
@@ -12,7 +12,7 @@
"loginEdit": "Change",
"loginDone": "Done",
"loginReadTermsNotification": "Please read the terms of use first",
"loginSpoofRedacted": "Spoof redaction",
"loginSpoofRedacted": "Spoofing",
"loginProxy": "Proxy",
"loginChangeServer": "Change server",
"serverSettingsTitle": "Server",
@@ -54,5 +54,49 @@
"proxyApply": "Apply and reconnect",
"proxyDisable": "Disable proxy",
"proxySettingsSaved": "Proxy settings applied",
"proxyInvalidHostOrPort": "Enter a valid proxy host and port (165535)"
"proxyInvalidHostOrPort": "Enter a valid proxy host and port (165535)",
"spoofScreenTitle": "Session spoofing",
"spoofInfoHint": "Tap \"Generate\":\n• Short tap: random preset.\n• Long press: real device data.",
"spoofMethodTitle": "Spoofing method",
"spoofMethodPartial": "Partial",
"spoofMethodFull": "Full",
"spoofMethodPartialDescription": "Recommended method. Random data is used, but your real timezone and locale are kept for plausibility.",
"spoofMethodFullDescription": "All data including timezone and locale is generated randomly. Use this method at your own risk!",
"spoofDeviceTypeTitle": "Device type",
"spoofDeviceTypeDescription": "Choose a device type for preset generation. Tapping \"Generate\" will only use presets of the selected type.",
"spoofDeviceTypeLabel": "Device type",
"spoofMainSectionTitle": "Main data",
"spoofFieldDeviceName": "Device name",
"spoofFieldOsVersion": "OS version",
"spoofRegionalSectionTitle": "Regional data",
"spoofFieldScreen": "Screen resolution",
"spoofFieldTimezone": "Timezone",
"spoofFieldLocale": "Locale",
"spoofIdentifiersSectionTitle": "Identifiers",
"spoofIdentifiersDescription": "mt_instanceid and clientSessionId are generated automatically on every app launch. Only the Device ID can be changed.",
"spoofFieldDeviceId": "Device ID",
"spoofRegenerateIdTooltip": "Generate a new ID",
"spoofFieldAppVersion": "App version",
"spoofFieldBuildNumber": "Build number",
"spoofFieldArchitecture": "Architecture",
"spoofButtonGenerate": "Generate",
"spoofButtonApply": "Apply",
"spoofDialogUnsureTitle": "Are you sure?",
"spoofDialogUnsureContent": "The app may become unstable due to API incompatibility",
"spoofDialogCancel": "Cancel",
"spoofDialogYes": "Yes",
"spoofDialogApplyTitle": "Apply settings?",
"spoofDialogApplyContent": "Need to reconnect the app, ok?",
"spoofDialogApplyDeny": "No",
"spoofDialogApplyConfirm": "Ok!",
"spoofErrorApplyFailed": "Failed to apply settings: {error}",
"@spoofErrorApplyFailed": {
"placeholders": {
"error": {
"type": "String"
}
}
},
"profileMenuSpoof": "Spoofing"
}
+233 -12
View File
@@ -62,7 +62,8 @@ import 'app_localizations_ru.dart';
/// be consistent with the languages listed in the AppLocalizations.supportedLocales
/// property.
abstract class AppLocalizations {
AppLocalizations(String locale) : localeName = intl.Intl.canonicalizedLocale(locale.toString());
AppLocalizations(String locale)
: localeName = intl.Intl.canonicalizedLocale(locale.toString());
final String localeName;
@@ -70,7 +71,8 @@ abstract class AppLocalizations {
return Localizations.of<AppLocalizations>(context, AppLocalizations);
}
static const LocalizationsDelegate<AppLocalizations> delegate = _AppLocalizationsDelegate();
static const LocalizationsDelegate<AppLocalizations> delegate =
_AppLocalizationsDelegate();
/// A list of this localizations delegate along with the default localizations
/// delegates.
@@ -82,7 +84,8 @@ abstract class AppLocalizations {
/// Additional delegates can be added by appending to this list in
/// MaterialApp. This list does not have to be used at all if a custom list
/// of delegates is preferred or required.
static const List<LocalizationsDelegate<dynamic>> localizationsDelegates = <LocalizationsDelegate<dynamic>>[
static const List<LocalizationsDelegate<dynamic>> localizationsDelegates =
<LocalizationsDelegate<dynamic>>[
delegate,
GlobalMaterialLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
@@ -92,7 +95,7 @@ abstract class AppLocalizations {
/// A list of this localizations delegate's supported locales.
static const List<Locale> supportedLocales = <Locale>[
Locale('en'),
Locale('ru')
Locale('ru'),
];
/// No description provided for @loginTitle.
@@ -170,7 +173,7 @@ abstract class AppLocalizations {
/// No description provided for @loginSpoofRedacted.
///
/// In en, this message translates to:
/// **'Spoof redaction'**
/// **'Spoofing'**
String get loginSpoofRedacted;
/// No description provided for @loginProxy.
@@ -376,9 +379,226 @@ abstract class AppLocalizations {
/// In en, this message translates to:
/// **'Enter a valid proxy host and port (165535)'**
String get proxyInvalidHostOrPort;
/// No description provided for @spoofScreenTitle.
///
/// In en, this message translates to:
/// **'Session spoofing'**
String get spoofScreenTitle;
/// No description provided for @spoofInfoHint.
///
/// In en, this message translates to:
/// **'Tap \"Generate\":\n• Short tap: random preset.\n• Long press: real device data.'**
String get spoofInfoHint;
/// No description provided for @spoofMethodTitle.
///
/// In en, this message translates to:
/// **'Spoofing method'**
String get spoofMethodTitle;
/// No description provided for @spoofMethodPartial.
///
/// In en, this message translates to:
/// **'Partial'**
String get spoofMethodPartial;
/// No description provided for @spoofMethodFull.
///
/// In en, this message translates to:
/// **'Full'**
String get spoofMethodFull;
/// No description provided for @spoofMethodPartialDescription.
///
/// In en, this message translates to:
/// **'Recommended method. Random data is used, but your real timezone and locale are kept for plausibility.'**
String get spoofMethodPartialDescription;
/// No description provided for @spoofMethodFullDescription.
///
/// In en, this message translates to:
/// **'All data including timezone and locale is generated randomly. Use this method at your own risk!'**
String get spoofMethodFullDescription;
/// No description provided for @spoofDeviceTypeTitle.
///
/// In en, this message translates to:
/// **'Device type'**
String get spoofDeviceTypeTitle;
/// No description provided for @spoofDeviceTypeDescription.
///
/// In en, this message translates to:
/// **'Choose a device type for preset generation. Tapping \"Generate\" will only use presets of the selected type.'**
String get spoofDeviceTypeDescription;
/// No description provided for @spoofDeviceTypeLabel.
///
/// In en, this message translates to:
/// **'Device type'**
String get spoofDeviceTypeLabel;
/// No description provided for @spoofMainSectionTitle.
///
/// In en, this message translates to:
/// **'Main data'**
String get spoofMainSectionTitle;
/// No description provided for @spoofFieldDeviceName.
///
/// In en, this message translates to:
/// **'Device name'**
String get spoofFieldDeviceName;
/// No description provided for @spoofFieldOsVersion.
///
/// In en, this message translates to:
/// **'OS version'**
String get spoofFieldOsVersion;
/// No description provided for @spoofRegionalSectionTitle.
///
/// In en, this message translates to:
/// **'Regional data'**
String get spoofRegionalSectionTitle;
/// No description provided for @spoofFieldScreen.
///
/// In en, this message translates to:
/// **'Screen resolution'**
String get spoofFieldScreen;
/// No description provided for @spoofFieldTimezone.
///
/// In en, this message translates to:
/// **'Timezone'**
String get spoofFieldTimezone;
/// No description provided for @spoofFieldLocale.
///
/// In en, this message translates to:
/// **'Locale'**
String get spoofFieldLocale;
/// No description provided for @spoofIdentifiersSectionTitle.
///
/// In en, this message translates to:
/// **'Identifiers'**
String get spoofIdentifiersSectionTitle;
/// No description provided for @spoofIdentifiersDescription.
///
/// In en, this message translates to:
/// **'mt_instanceid and clientSessionId are generated automatically on every app launch. Only the Device ID can be changed.'**
String get spoofIdentifiersDescription;
/// No description provided for @spoofFieldDeviceId.
///
/// In en, this message translates to:
/// **'Device ID'**
String get spoofFieldDeviceId;
/// No description provided for @spoofRegenerateIdTooltip.
///
/// In en, this message translates to:
/// **'Generate a new ID'**
String get spoofRegenerateIdTooltip;
/// No description provided for @spoofFieldAppVersion.
///
/// In en, this message translates to:
/// **'App version'**
String get spoofFieldAppVersion;
/// No description provided for @spoofFieldBuildNumber.
///
/// In en, this message translates to:
/// **'Build number'**
String get spoofFieldBuildNumber;
/// No description provided for @spoofFieldArchitecture.
///
/// In en, this message translates to:
/// **'Architecture'**
String get spoofFieldArchitecture;
/// No description provided for @spoofButtonGenerate.
///
/// In en, this message translates to:
/// **'Generate'**
String get spoofButtonGenerate;
/// No description provided for @spoofButtonApply.
///
/// In en, this message translates to:
/// **'Apply'**
String get spoofButtonApply;
/// No description provided for @spoofDialogUnsureTitle.
///
/// In en, this message translates to:
/// **'Are you sure?'**
String get spoofDialogUnsureTitle;
/// No description provided for @spoofDialogUnsureContent.
///
/// In en, this message translates to:
/// **'The app may become unstable due to API incompatibility'**
String get spoofDialogUnsureContent;
/// No description provided for @spoofDialogCancel.
///
/// In en, this message translates to:
/// **'Cancel'**
String get spoofDialogCancel;
/// No description provided for @spoofDialogYes.
///
/// In en, this message translates to:
/// **'Yes'**
String get spoofDialogYes;
/// No description provided for @spoofDialogApplyTitle.
///
/// In en, this message translates to:
/// **'Apply settings?'**
String get spoofDialogApplyTitle;
/// No description provided for @spoofDialogApplyContent.
///
/// In en, this message translates to:
/// **'Need to reconnect the app, ok?'**
String get spoofDialogApplyContent;
/// No description provided for @spoofDialogApplyDeny.
///
/// In en, this message translates to:
/// **'No'**
String get spoofDialogApplyDeny;
/// No description provided for @spoofDialogApplyConfirm.
///
/// In en, this message translates to:
/// **'Ok!'**
String get spoofDialogApplyConfirm;
/// No description provided for @spoofErrorApplyFailed.
///
/// In en, this message translates to:
/// **'Failed to apply settings: {error}'**
String spoofErrorApplyFailed(String error);
/// No description provided for @profileMenuSpoof.
///
/// In en, this message translates to:
/// **'Spoofing'**
String get profileMenuSpoof;
}
class _AppLocalizationsDelegate extends LocalizationsDelegate<AppLocalizations> {
class _AppLocalizationsDelegate
extends LocalizationsDelegate<AppLocalizations> {
const _AppLocalizationsDelegate();
@override
@@ -387,25 +607,26 @@ class _AppLocalizationsDelegate extends LocalizationsDelegate<AppLocalizations>
}
@override
bool isSupported(Locale locale) => <String>['en', 'ru'].contains(locale.languageCode);
bool isSupported(Locale locale) =>
<String>['en', 'ru'].contains(locale.languageCode);
@override
bool shouldReload(_AppLocalizationsDelegate old) => false;
}
AppLocalizations lookupAppLocalizations(Locale locale) {
// Lookup logic when only language code is specified.
switch (locale.languageCode) {
case 'en': return AppLocalizationsEn();
case 'ru': return AppLocalizationsRu();
case 'en':
return AppLocalizationsEn();
case 'ru':
return AppLocalizationsRu();
}
throw FlutterError(
'AppLocalizations.delegate failed to load unsupported locale "$locale". This is likely '
'an issue with the localizations generation tool. Please file an issue '
'on GitHub with a reproducible sample app and the gen-l10n configuration '
'that was used.'
'that was used.',
);
}
+123 -4
View File
@@ -12,7 +12,8 @@ class AppLocalizationsEn extends AppLocalizations {
String get loginTitle => 'Sign in to Komet';
@override
String get loginSubtitle => 'Check your country code and enter your\nphone number.';
String get loginSubtitle =>
'Check your country code and enter your\nphone number.';
@override
String get loginCountry => 'Country';
@@ -45,7 +46,7 @@ class AppLocalizationsEn extends AppLocalizations {
String get loginReadTermsNotification => 'Please read the terms of use first';
@override
String get loginSpoofRedacted => 'Spoof redaction';
String get loginSpoofRedacted => 'Spoofing';
@override
String get loginProxy => 'Proxy';
@@ -102,7 +103,8 @@ class AppLocalizationsEn extends AppLocalizations {
String get selectCountrySearchHint => 'Search countries…';
@override
String get codeConfirmationSmsSent => 'We sent an SMS with a verification code to your phone number.';
String get codeConfirmationSmsSent =>
'We sent an SMS with a verification code to your phone number.';
@override
String codeResendInSeconds(int seconds) {
@@ -149,5 +151,122 @@ class AppLocalizationsEn extends AppLocalizations {
String get proxySettingsSaved => 'Proxy settings applied';
@override
String get proxyInvalidHostOrPort => 'Enter a valid proxy host and port (165535)';
String get proxyInvalidHostOrPort =>
'Enter a valid proxy host and port (165535)';
@override
String get spoofScreenTitle => 'Session spoofing';
@override
String get spoofInfoHint =>
'Tap \"Generate\":\n• Short tap: random preset.\n• Long press: real device data.';
@override
String get spoofMethodTitle => 'Spoofing method';
@override
String get spoofMethodPartial => 'Partial';
@override
String get spoofMethodFull => 'Full';
@override
String get spoofMethodPartialDescription =>
'Recommended method. Random data is used, but your real timezone and locale are kept for plausibility.';
@override
String get spoofMethodFullDescription =>
'All data including timezone and locale is generated randomly. Use this method at your own risk!';
@override
String get spoofDeviceTypeTitle => 'Device type';
@override
String get spoofDeviceTypeDescription =>
'Choose a device type for preset generation. Tapping \"Generate\" will only use presets of the selected type.';
@override
String get spoofDeviceTypeLabel => 'Device type';
@override
String get spoofMainSectionTitle => 'Main data';
@override
String get spoofFieldDeviceName => 'Device name';
@override
String get spoofFieldOsVersion => 'OS version';
@override
String get spoofRegionalSectionTitle => 'Regional data';
@override
String get spoofFieldScreen => 'Screen resolution';
@override
String get spoofFieldTimezone => 'Timezone';
@override
String get spoofFieldLocale => 'Locale';
@override
String get spoofIdentifiersSectionTitle => 'Identifiers';
@override
String get spoofIdentifiersDescription =>
'mt_instanceid and clientSessionId are generated automatically on every app launch. Only the Device ID can be changed.';
@override
String get spoofFieldDeviceId => 'Device ID';
@override
String get spoofRegenerateIdTooltip => 'Generate a new ID';
@override
String get spoofFieldAppVersion => 'App version';
@override
String get spoofFieldBuildNumber => 'Build number';
@override
String get spoofFieldArchitecture => 'Architecture';
@override
String get spoofButtonGenerate => 'Generate';
@override
String get spoofButtonApply => 'Apply';
@override
String get spoofDialogUnsureTitle => 'Are you sure?';
@override
String get spoofDialogUnsureContent =>
'The app may become unstable due to API incompatibility';
@override
String get spoofDialogCancel => 'Cancel';
@override
String get spoofDialogYes => 'Yes';
@override
String get spoofDialogApplyTitle => 'Apply settings?';
@override
String get spoofDialogApplyContent => 'Need to reconnect the app, ok?';
@override
String get spoofDialogApplyDeny => 'No';
@override
String get spoofDialogApplyConfirm => 'Ok!';
@override
String spoofErrorApplyFailed(String error) {
return 'Failed to apply settings: $error';
}
@override
String get profileMenuSpoof => 'Spoofing';
}
+127 -6
View File
@@ -12,7 +12,8 @@ class AppLocalizationsRu extends AppLocalizations {
String get loginTitle => 'Войдите в Komet';
@override
String get loginSubtitle => 'Проверьте код страны и введите свой\nномер телефона.';
String get loginSubtitle =>
'Проверьте код страны и введите свой\nномер телефона.';
@override
String get loginCountry => 'Страна';
@@ -42,10 +43,11 @@ class AppLocalizationsRu extends AppLocalizations {
String get loginDone => 'Готово';
@override
String get loginReadTermsNotification => 'Сначала прочитайте условия использования';
String get loginReadTermsNotification =>
'Сначала прочитайте условия использования';
@override
String get loginSpoofRedacted => 'Подделка спуфа';
String get loginSpoofRedacted => 'Подмена данных';
@override
String get loginProxy => 'Прокси';
@@ -69,7 +71,8 @@ class AppLocalizationsRu extends AppLocalizations {
String get serverUseDefault => 'Сбросить к умолчанию';
@override
String get serverInvalidHostOrPort => 'Укажите корректный хост и порт (1–65535)';
String get serverInvalidHostOrPort =>
'Укажите корректный хост и порт (1–65535)';
@override
String get serverSettingsSaved => 'Настройки сервера применены';
@@ -102,7 +105,8 @@ class AppLocalizationsRu extends AppLocalizations {
String get selectCountrySearchHint => 'Поиск страны…';
@override
String get codeConfirmationSmsSent => 'Мы отправили SMS с кодом подтверждения на ваш номер телефона.';
String get codeConfirmationSmsSent =>
'Мы отправили SMS с кодом подтверждения на ваш номер телефона.';
@override
String codeResendInSeconds(int seconds) {
@@ -149,5 +153,122 @@ class AppLocalizationsRu extends AppLocalizations {
String get proxySettingsSaved => 'Настройки прокси применены';
@override
String get proxyInvalidHostOrPort => 'Укажите корректный хост и порт прокси (1–65535)';
String get proxyInvalidHostOrPort =>
'Укажите корректный хост и порт прокси (1–65535)';
@override
String get spoofScreenTitle => 'Подмена данных сессии';
@override
String get spoofInfoHint =>
'Нажмите \"Сгенерировать\":\n• Короткое нажатие: случайный пресет.\n• Длинное нажатие: реальные данные.';
@override
String get spoofMethodTitle => 'Метод подмены';
@override
String get spoofMethodPartial => 'Частичный';
@override
String get spoofMethodFull => 'Полный';
@override
String get spoofMethodPartialDescription =>
'Рекомендуемый метод. Используются случайные данные, но ваш реальный часовой пояс и локаль для большей правдоподобности.';
@override
String get spoofMethodFullDescription =>
'Все данные, включая часовой пояс и локаль, генерируются случайно. Использование этого метода на ваш страх и риск!';
@override
String get spoofDeviceTypeTitle => 'Тип устройства';
@override
String get spoofDeviceTypeDescription =>
'Выберите тип устройства для генерации пресетов. При нажатии \"Сгенерировать\" будут использоваться только пресеты выбранного типа.';
@override
String get spoofDeviceTypeLabel => 'Тип устройства';
@override
String get spoofMainSectionTitle => 'Основные данные';
@override
String get spoofFieldDeviceName => 'Имя устройства';
@override
String get spoofFieldOsVersion => 'Версия ОС';
@override
String get spoofRegionalSectionTitle => 'Региональные данные';
@override
String get spoofFieldScreen => 'Разрешение экрана';
@override
String get spoofFieldTimezone => 'Часовой пояс';
@override
String get spoofFieldLocale => 'Локаль';
@override
String get spoofIdentifiersSectionTitle => 'Идентификаторы';
@override
String get spoofIdentifiersDescription =>
'mt_instanceid и clientSessionId генерируются автоматически при каждом запуске приложения. Изменить можно только Device ID.';
@override
String get spoofFieldDeviceId => 'ID Устройства';
@override
String get spoofRegenerateIdTooltip => 'Сгенерировать новый ID';
@override
String get spoofFieldAppVersion => 'Версия приложения';
@override
String get spoofFieldBuildNumber => 'Build Number';
@override
String get spoofFieldArchitecture => 'Архитектура';
@override
String get spoofButtonGenerate => 'Сгенерировать';
@override
String get spoofButtonApply => 'Применить';
@override
String get spoofDialogUnsureTitle => 'Ты уверен?';
@override
String get spoofDialogUnsureContent =>
'Приложение может начать работать нестабильно из-за несовместимости API';
@override
String get spoofDialogCancel => 'Отмена';
@override
String get spoofDialogYes => 'Да';
@override
String get spoofDialogApplyTitle => 'Применить настройки?';
@override
String get spoofDialogApplyContent => 'Нужно перезайти в приложение, ок?';
@override
String get spoofDialogApplyDeny => 'Не';
@override
String get spoofDialogApplyConfirm => 'Ок!';
@override
String spoofErrorApplyFailed(String error) {
return 'Ошибка при применении настроек: $error';
}
@override
String get profileMenuSpoof => 'Подмена данных';
}
+46 -2
View File
@@ -12,7 +12,7 @@
"loginEdit": "Изменить",
"loginDone": "Готово",
"loginReadTermsNotification": "Сначала прочитайте условия использования",
"loginSpoofRedacted": "Подделка спуфа",
"loginSpoofRedacted": "Подмена данных",
"loginProxy": "Прокси",
"loginChangeServer": "Смена сервера",
"serverSettingsTitle": "Сервер",
@@ -54,5 +54,49 @@
"proxyApply": "Применить и переподключиться",
"proxyDisable": "Отключить прокси",
"proxySettingsSaved": "Настройки прокси применены",
"proxyInvalidHostOrPort": "Укажите корректный хост и порт прокси (1–65535)"
"proxyInvalidHostOrPort": "Укажите корректный хост и порт прокси (1–65535)",
"spoofScreenTitle": "Подмена данных сессии",
"spoofInfoHint": "Нажмите \"Сгенерировать\":\n• Короткое нажатие: случайный пресет.\n• Длинное нажатие: реальные данные.",
"spoofMethodTitle": "Метод подмены",
"spoofMethodPartial": "Частичный",
"spoofMethodFull": "Полный",
"spoofMethodPartialDescription": "Рекомендуемый метод. Используются случайные данные, но ваш реальный часовой пояс и локаль для большей правдоподобности.",
"spoofMethodFullDescription": "Все данные, включая часовой пояс и локаль, генерируются случайно. Использование этого метода на ваш страх и риск!",
"spoofDeviceTypeTitle": "Тип устройства",
"spoofDeviceTypeDescription": "Выберите тип устройства для генерации пресетов. При нажатии \"Сгенерировать\" будут использоваться только пресеты выбранного типа.",
"spoofDeviceTypeLabel": "Тип устройства",
"spoofMainSectionTitle": "Основные данные",
"spoofFieldDeviceName": "Имя устройства",
"spoofFieldOsVersion": "Версия ОС",
"spoofRegionalSectionTitle": "Региональные данные",
"spoofFieldScreen": "Разрешение экрана",
"spoofFieldTimezone": "Часовой пояс",
"spoofFieldLocale": "Локаль",
"spoofIdentifiersSectionTitle": "Идентификаторы",
"spoofIdentifiersDescription": "mt_instanceid и clientSessionId генерируются автоматически при каждом запуске приложения. Изменить можно только Device ID.",
"spoofFieldDeviceId": "ID Устройства",
"spoofRegenerateIdTooltip": "Сгенерировать новый ID",
"spoofFieldAppVersion": "Версия приложения",
"spoofFieldBuildNumber": "Build Number",
"spoofFieldArchitecture": "Архитектура",
"spoofButtonGenerate": "Сгенерировать",
"spoofButtonApply": "Применить",
"spoofDialogUnsureTitle": "Ты уверен?",
"spoofDialogUnsureContent": "Приложение может начать работать нестабильно из-за несовместимости API",
"spoofDialogCancel": "Отмена",
"spoofDialogYes": "Да",
"spoofDialogApplyTitle": "Применить настройки?",
"spoofDialogApplyContent": "Нужно перезайти в приложение, ок?",
"spoofDialogApplyDeny": "Не",
"spoofDialogApplyConfirm": "Ок!",
"spoofErrorApplyFailed": "Ошибка при применении настроек: {error}",
"@spoofErrorApplyFailed": {
"placeholders": {
"error": {
"type": "String"
}
}
},
"profileMenuSpoof": "Подмена данных"
}