tor updates

This commit is contained in:
Matthew Fosse 2024-01-03 16:00:00 -05:00
parent e6fa2a58db
commit c4c4afb74a
42 changed files with 251 additions and 161 deletions

BIN
assets/images/tor_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View file

@ -220,6 +220,7 @@ class BackupService {
final currentLanguageCode = data[PreferencesKey.currentLanguageCode] as String?;
final displayActionListMode = data[PreferencesKey.displayActionListModeKey] as int?;
final fiatApiMode = data[PreferencesKey.currentFiatApiModeKey] as int?;
final torConnectionMode = data[PreferencesKey.currentTorConnectionModeKey] as int?;
final shouldStartTorOnLaunch = data[PreferencesKey.shouldStartTorOnLaunch] as bool?;
final currentPinLength = data[PreferencesKey.currentPinLength] as int?;
final currentTheme = data[PreferencesKey.currentTheme] as int?;
@ -317,6 +318,9 @@ class BackupService {
if (fiatApiMode != null)
await _sharedPreferences.setInt(PreferencesKey.currentFiatApiModeKey, fiatApiMode);
if (torConnectionMode != null)
await _sharedPreferences.setInt(PreferencesKey.currentTorConnectionModeKey, torConnectionMode);
if (shouldStartTorOnLaunch != null)
await _sharedPreferences.setBool(
PreferencesKey.shouldStartTorOnLaunch, shouldStartTorOnLaunch);
@ -556,6 +560,8 @@ class BackupService {
_sharedPreferences.getInt(PreferencesKey.moneroTransactionPriority),
PreferencesKey.currentFiatApiModeKey:
_sharedPreferences.getInt(PreferencesKey.currentFiatApiModeKey),
PreferencesKey.currentTorConnectionModeKey:
_sharedPreferences.getInt(PreferencesKey.currentTorConnectionModeKey),
PreferencesKey.shouldStartTorOnLaunch:
_sharedPreferences.getBool(PreferencesKey.shouldStartTorOnLaunch),
PreferencesKey.selectedCake2FAPreset:

View file

@ -298,8 +298,9 @@ Future<void> setup({
);
if (DeviceInfo.instance.isMobile && settingsStore.shouldStartTorOnLaunch) {
Tor.instance.enable();
Tor.instance.start();
await Tor.init();
await Tor.instance.enable();
await Tor.instance.start();
}
if (_isSetupFinished) {

View file

@ -48,7 +48,7 @@ class PreferencesKey {
static const moneroWalletPasswordUpdateV1Base = 'monero_wallet_update_v1';
static const syncModeKey = 'sync_mode';
static const syncAllKey = 'sync_all';
static const torConnectionKey = 'tor_connection';
static const currentTorConnectionModeKey = 'current_tor_connection_mode';
static const pinTimeOutDuration = 'pin_timeout_duration';
static const lastAuthTimeMilliseconds = 'last_auth_time_milliseconds';
static const lastPopupDate = 'last_popup_date';

View file

@ -34,7 +34,7 @@ Future<void> startFiatRateUpdate(
crypto: appStore.wallet!.currency,
fiat: settingsStore.fiatCurrency,
torOnly: settingsStore.fiatApiMode == FiatApiMode.torOnly,
onionOnly: settingsStore.currentTorConnection == TorConnectionType.onionOnly,
onionOnly: settingsStore.torConnectionMode == TorConnectionMode.onionOnly,
);
}
@ -56,7 +56,7 @@ Future<void> startFiatRateUpdate(
crypto: currency,
fiat: settingsStore.fiatCurrency,
torOnly: settingsStore.fiatApiMode == FiatApiMode.torOnly,
onionOnly: settingsStore.currentTorConnection == TorConnectionType.onionOnly,
onionOnly: settingsStore.torConnectionMode == TorConnectionMode.onionOnly,
);
}.call();
}

View file

@ -21,7 +21,7 @@ void startCurrentFiatApiModeChangeReaction(
crypto: appStore.wallet!.currency,
fiat: settingsStore.fiatCurrency,
torOnly: fiatApiMode == FiatApiMode.torOnly,
onionOnly: settingsStore.currentTorConnection == TorConnectionType.onionOnly,
onionOnly: settingsStore.torConnectionMode == TorConnectionMode.onionOnly,
);
});
}

View file

@ -23,7 +23,7 @@ void startCurrentFiatChangeReaction(
crypto: cryptoCurrency,
fiat: fiatCurrency,
torOnly: settingsStore.fiatApiMode == FiatApiMode.torOnly,
onionOnly: settingsStore.currentTorConnection == TorConnectionType.onionOnly,
onionOnly: settingsStore.torConnectionMode == TorConnectionMode.onionOnly,
);
});
}

View file

@ -96,7 +96,7 @@ void startCurrentWalletChangeReaction(
crypto: wallet.currency,
fiat: settingsStore.fiatCurrency,
torOnly: settingsStore.fiatApiMode == FiatApiMode.torOnly,
onionOnly: settingsStore.currentTorConnection == TorConnectionType.onionOnly,
onionOnly: settingsStore.torConnectionMode == TorConnectionMode.onionOnly,
);
Iterable<Erc20Token>? currencies;
@ -115,7 +115,7 @@ void startCurrentWalletChangeReaction(
crypto: currency,
fiat: settingsStore.fiatCurrency,
torOnly: settingsStore.fiatApiMode == FiatApiMode.torOnly,
onionOnly: settingsStore.currentTorConnection == TorConnectionType.onionOnly,
onionOnly: settingsStore.torConnectionMode == TorConnectionMode.onionOnly,
);
}.call();
}

View file

@ -101,6 +101,24 @@ class _DashboardPageView extends BasePage {
@override
Widget get endDrawer => MenuWidget(dashboardViewModel);
// @override
// Widget leading(BuildContext context) {
// final torButton = Image.asset(
// 'assets/images/tor_icon.png',
// // color: Theme.of(context).extension<DashboardPageTheme>()!.pageTitleTextColor,
// );
// return Container(
// alignment: Alignment.centerRight,
// width: 40,
// child: TextButton(
// onPressed: () => Navigator.of(context, rootNavigator: true).pushNamed(Routes.connectionSync),
// child: Semantics(label: S.of(context).tor_connection, child: torButton),
// ),
// );
// }
@override
Widget middle(BuildContext context) {
return SyncIndicator(

View file

@ -1,10 +1,14 @@
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/themes/extensions/sync_indicator_theme.dart';
import 'package:cake_wallet/view_model/settings/tor_connection.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
import 'package:cake_wallet/core/sync_status_title.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:cw_core/sync_status.dart';
import 'package:cake_wallet/src/screens/dashboard/widgets/sync_indicator_icon.dart';
import 'package:tor/tor.dart';
class SyncIndicator extends StatelessWidget {
SyncIndicator({required this.dashboardViewModel, required this.onTap});
@ -14,71 +18,80 @@ class SyncIndicator extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Observer(
builder: (_) {
final syncIndicatorWidth = 237.0;
final status = dashboardViewModel.status;
final statusText = status != null ? syncStatusTitle(status) : '';
final progress = status != null ? status.progress() : 0.0;
final indicatorOffset = progress * syncIndicatorWidth;
final indicatorWidth = progress < 1
? indicatorOffset > 0 ? indicatorOffset : 0.0
: syncIndicatorWidth;
return Observer(builder: (_) {
final syncIndicatorWidth = 237.0;
final status = dashboardViewModel.status;
final statusText = status != null ? syncStatusTitle(status) : '';
final progress = status != null ? status.progress() : 0.0;
final indicatorOffset = progress * syncIndicatorWidth;
final indicatorWidth = progress < 1
? indicatorOffset > 0
? indicatorOffset
: 0.0
: syncIndicatorWidth;
return ClipRRect(
return ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(15)),
child: GestureDetector(
child: GestureDetector(
onTap: onTap,
child: Container(
height: 30,
width: syncIndicatorWidth,
color: Theme.of(context).extension<SyncIndicatorTheme>()!.notSyncedBackgroundColor,
child: Stack(
alignment: Alignment.center,
children: <Widget>[
progress <= 1
? Positioned(
left: 0,
top: 0,
bottom: 0,
child: Container(
width: indicatorWidth,
height: 30,
color: Theme.of(context).extension<SyncIndicatorTheme>()!.syncedBackgroundColor,
)
)
: Offstage(),
Padding(
padding: EdgeInsets.only(
left: 24,
right: 24
),
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
SyncIndicatorIcon(isSynced:status is SyncedSyncStatus),
Padding(
padding: EdgeInsets.only(left: 6),
child: Text(
statusText,
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w500,
color: Theme.of(context).extension<SyncIndicatorTheme>()!.textColor
),
child: Container(
height: 30,
width: syncIndicatorWidth,
color: Theme.of(context).extension<SyncIndicatorTheme>()!.notSyncedBackgroundColor,
child: Stack(
alignment: Alignment.center,
children: <Widget>[
progress <= 1
? Positioned(
left: 0,
top: 0,
bottom: 0,
child: Container(
width: indicatorWidth,
height: 30,
color: Theme.of(context)
.extension<SyncIndicatorTheme>()!
.syncedBackgroundColor,
))
: Offstage(),
Row(
children: [
Container(
width: 15,
margin: EdgeInsets.only(left: 12, bottom: 2),
child: Image.asset(
'assets/images/tor_icon.png',
color: dashboardViewModel.isTorConnected ? null : Colors.white,
),
)
),
],
),
)
],
Padding(
padding: EdgeInsets.only(left: 24, right: 24),
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
SyncIndicatorIcon(isSynced: status is SyncedSyncStatus),
Padding(
padding: EdgeInsets.only(left: 6),
child: Text(
statusText,
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w500,
color:
Theme.of(context).extension<SyncIndicatorTheme>()!.textColor),
),
)
],
),
)
],
),
),
),
));
}
);
));
});
}
}
}

View file

@ -103,20 +103,25 @@ class ConnectionSyncPage extends BasePage {
selectedItem: dashboardViewModel.torConnection,
onItemSelected: dashboardViewModel.setTorConnection,
decoration: BoxDecoration(
borderRadius: BorderRadius.only(topLeft: Radius.circular(25), topRight: Radius.circular(25)),
borderRadius: BorderRadius.only(
topLeft: Radius.circular(25), topRight: Radius.circular(25)),
color: const Color.fromARGB(255, 236, 244, 255),
),
);
}),
TorStatus(
decoration: BoxDecoration(
borderRadius: BorderRadius.only(bottomLeft: Radius.circular(25), bottomRight: Radius.circular(25)),
color: const Color.fromARGB(255, 236, 244, 255),
),
title: S.current.tor_status,
isSelected: false,
onTap: (context) {},
),
Observer(builder: (context) {
return TorStatus(
connected: dashboardViewModel.isTorConnected,
decoration: BoxDecoration(
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(25), bottomRight: Radius.circular(25)),
color: const Color.fromARGB(255, 236, 244, 255),
),
title: S.current.tor_status,
isSelected: false,
onTap: (context) {},
);
}),
]),
),
],

View file

@ -9,29 +9,19 @@ class TorStatus extends StandardListRow {
{required String title,
required void Function(BuildContext context) onTap,
required bool isSelected,
required this.connected,
BoxDecoration? decoration})
: super(title: title, onTap: onTap, isSelected: isSelected, decoration: decoration);
final bool connected;
@override
Widget buildTrailing(BuildContext context) {
return NodeIndicator(
isLive: Tor.instance.started && Tor.instance.enabled,
isLive: connected,
showText: true,
);
}
// @override
// Widget buildTrailing(BuildContext context) {
// return GestureDetector(
// onTap: () {},
// child: Container(
// padding: EdgeInsets.all(10),
// decoration: BoxDecoration(
// shape: BoxShape.circle,
// color: Theme.of(context).extension<ReceivePageTheme>()!.iconsBackgroundColor),
// child: Icon(Icons.edit,
// size: 14, color: Theme.of(context).extension<ReceivePageTheme>()!.iconsColor)));
// }
}
class NodeHeaderListRow extends StandardListRow {

View file

@ -60,6 +60,7 @@ abstract class SettingsStoreBase with Store {
required WalletListOrderType initialWalletListOrder,
required bool initialWalletListAscending,
required FiatApiMode initialFiatMode,
required TorConnectionMode initialTorConnectionMode,
required bool initialShouldStartTorOnLaunch,
required bool initialAllowBiometricalAuthentication,
required String initialTotpSecretKey,
@ -71,7 +72,6 @@ abstract class SettingsStoreBase with Store {
required String initialLanguageCode,
required SyncMode initialSyncMode,
required bool initialSyncAll,
required TorConnection initialTorConnection,
// required String initialCurrentLocale,
required this.appVersion,
required this.deviceName,
@ -121,6 +121,7 @@ abstract class SettingsStoreBase with Store {
autoGenerateSubaddressStatus = initialAutoGenerateSubaddressStatus,
moneroSeedType = initialMoneroSeedType,
fiatApiMode = initialFiatMode,
torConnectionMode = initialTorConnectionMode,
shouldStartTorOnLaunch = initialShouldStartTorOnLaunch,
allowBiometricalAuthentication = initialAllowBiometricalAuthentication,
selectedCake2FAPreset = initialCake2FAPresetOptions,
@ -153,7 +154,6 @@ abstract class SettingsStoreBase with Store {
initialShouldRequireTOTP2FAForAllSecurityAndBackupSettings,
currentSyncMode = initialSyncMode,
currentSyncAll = initialSyncAll,
currentTorConnection = initialTorConnection,
priority = ObservableMap<WalletType, TransactionPriority>(),
defaultBuyProviders = ObservableMap<WalletType, ProviderType>(),
defaultSellProviders = ObservableMap<WalletType, ProviderType>() {
@ -317,6 +317,21 @@ abstract class SettingsStoreBase with Store {
(FiatApiMode mode) =>
sharedPreferences.setInt(PreferencesKey.currentFiatApiModeKey, mode.serialize()));
reaction((_) => torConnectionMode, (TorConnectionMode mode) async {
await sharedPreferences.setInt(PreferencesKey.currentTorConnectionModeKey, mode.serialize());
if (mode == TorConnectionMode.enabled || mode == TorConnectionMode.onionOnly) {
// init and start the proxy
await Tor.init();
await Tor.instance.enable();
shouldStartTorOnLaunch = true;
} else {
Tor.instance.disable();
shouldStartTorOnLaunch = false;
}
});
reaction((_) => shouldStartTorOnLaunch,
(bool value) => sharedPreferences.setBool(PreferencesKey.shouldStartTorOnLaunch, value));
@ -436,21 +451,6 @@ abstract class SettingsStoreBase with Store {
_backgroundTasks.registerSyncTask(changeExisting: true);
});
reaction((_) => currentTorConnection, (TorConnection torConnection) async {
sharedPreferences.setInt(PreferencesKey.torConnectionKey, torConnection.type.index);
if (torConnection.type == TorConnectionType.enabled) {
// Start the proxy
await Tor.init();
await Tor.instance.start();
shouldStartTorOnLaunch = true;
} else {
Tor.instance.disable();
shouldStartTorOnLaunch = false;
}
});
reaction(
(_) => exchangeStatus,
(ExchangeApiMode mode) =>
@ -549,6 +549,9 @@ abstract class SettingsStoreBase with Store {
@observable
FiatApiMode fiatApiMode;
@observable
TorConnectionMode torConnectionMode;
@observable
bool shouldStartTorOnLaunch;
@ -690,9 +693,6 @@ abstract class SettingsStoreBase with Store {
@observable
SyncMode currentSyncMode;
@observable
TorConnection currentTorConnection;
@observable
bool currentSyncAll;
@ -802,6 +802,9 @@ abstract class SettingsStoreBase with Store {
final currentFiatApiMode = FiatApiMode.deserialize(
raw: sharedPreferences.getInt(PreferencesKey.currentFiatApiModeKey) ??
FiatApiMode.enabled.raw);
final currentTorConnectionMode = TorConnectionMode.deserialize(
raw: sharedPreferences.getInt(PreferencesKey.currentTorConnectionModeKey) ??
FiatApiMode.enabled.raw);
final shouldStartTorOnLaunch =
sharedPreferences.getBool(PreferencesKey.shouldStartTorOnLaunch) ?? false;
final allowBiometricalAuthentication =
@ -963,10 +966,6 @@ abstract class SettingsStoreBase with Store {
});
final savedSyncAll = sharedPreferences.getBool(PreferencesKey.syncAllKey) ?? true;
final savedTorConnection = TorConnection.all.firstWhere((element) {
return element.type.index == (sharedPreferences.getInt(PreferencesKey.torConnectionKey) ?? 0);
});
return SettingsStore(
sharedPreferences: sharedPreferences,
initialShouldShowMarketPlaceInDashboard: shouldShowMarketPlaceInDashboard,
@ -986,6 +985,7 @@ abstract class SettingsStoreBase with Store {
initialWalletListOrder: walletListOrder,
initialWalletListAscending: walletListAscending,
initialFiatMode: currentFiatApiMode,
initialTorConnectionMode: currentTorConnectionMode,
initialShouldStartTorOnLaunch: shouldStartTorOnLaunch,
initialAllowBiometricalAuthentication: allowBiometricalAuthentication,
initialCake2FAPresetOptions: selectedCake2FAPreset,

View file

@ -44,6 +44,7 @@ import 'package:eth_sig_util/util/utils.dart';
import 'package:flutter/services.dart';
import 'package:mobx/mobx.dart';
import 'package:cake_wallet/entities/provider_types.dart';
import 'package:tor/tor.dart';
part 'dashboard_view_model.g.dart';
@ -336,15 +337,13 @@ abstract class DashboardViewModelBase with Store {
bool hasExchangeAction;
@computed
bool get isEnabledBuyAction =>
!settingsStore.disableBuy && availableBuyProviders.isNotEmpty;
bool get isEnabledBuyAction => !settingsStore.disableBuy && availableBuyProviders.isNotEmpty;
@observable
bool hasBuyAction;
@computed
bool get isEnabledSellAction =>
!settingsStore.disableSell && availableSellProviders.isNotEmpty;
bool get isEnabledSellAction => !settingsStore.disableSell && availableSellProviders.isNotEmpty;
@observable
bool hasSellAction;
@ -462,10 +461,16 @@ abstract class DashboardViewModelBase with Store {
void setSyncMode(SyncMode syncMode) => settingsStore.currentSyncMode = syncMode;
@computed
TorConnection get torConnection => settingsStore.currentTorConnection;
TorConnectionMode get torConnectionMode => settingsStore.torConnectionMode;
@action
void setTorConnection(TorConnection torConnection) => settingsStore.currentTorConnection = torConnection;
void setTorConnectionMode(TorConnectionMode mode) => settingsStore.torConnectionMode = mode;
@computed
bool get isTorConnected =>
(settingsStore.torConnectionMode == TorConnectionMode.enabled ||
settingsStore.torConnectionMode == TorConnectionMode.onionOnly) &&
(Tor.instance.port != -1);
@computed
bool get syncAll => settingsStore.currentSyncAll;
@ -475,7 +480,8 @@ abstract class DashboardViewModelBase with Store {
Future<List<String>> checkAffectedWallets() async {
// await load file
final vulnerableSeedsString = await rootBundle.loadString('assets/text/cakewallet_weak_bitcoin_seeds_hashed_sorted_version1.txt');
final vulnerableSeedsString = await rootBundle
.loadString('assets/text/cakewallet_weak_bitcoin_seeds_hashed_sorted_version1.txt');
final vulnerableSeeds = vulnerableSeedsString.split("\n");
final walletInfoSource = await CakeHive.openBox<WalletInfo>(WalletInfo.boxName);

View file

@ -90,7 +90,7 @@ abstract class HomeSettingsViewModelBase with Store {
crypto: token,
fiat: _settingsStore.fiatCurrency,
torOnly: _settingsStore.fiatApiMode == FiatApiMode.torOnly,
onionOnly: _settingsStore.currentTorConnection == TorConnectionType.onionOnly,
onionOnly: _settingsStore.torConnectionMode == TorConnectionMode.onionOnly,
);
} catch (_) {}
}

View file

@ -1,14 +1,39 @@
enum TorConnectionType { enabled, disabled, onionOnly }
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cw_core/enumerable_item.dart';
class TorConnection {
TorConnection(this.name, this.type);
class TorConnectionMode extends EnumerableItem<int> with Serializable<int> {
const TorConnectionMode({required String title, required int raw}) : super(title: title, raw: raw);
final String name;
final TorConnectionType type;
static const all = [TorConnectionMode.enabled, TorConnectionMode.disabled, TorConnectionMode.onionOnly];
static final all = [
TorConnection("Enabled", TorConnectionType.enabled),
TorConnection("Disabled", TorConnectionType.disabled),
TorConnection("Onion Only", TorConnectionType.onionOnly),
];
static const enabled = TorConnectionMode(raw: 0, title: 'Enabled');
static const disabled = TorConnectionMode(raw: 1, title: 'Disabled');
static const onionOnly = TorConnectionMode(raw: 2, title: 'Onion only');
static TorConnectionMode deserialize({required int raw}) {
switch (raw) {
case 0:
return enabled;
case 1:
return disabled;
case 2:
return onionOnly;
default:
throw Exception('Unexpected token: $raw for TorConnectionMode deserialize');
}
}
@override
String toString() {
switch (this) {
case TorConnectionMode.enabled:
return S.current.enabled;
case TorConnectionMode.disabled:
return S.current.disabled;
case TorConnectionMode.onionOnly:
return S.current.onion_only;
default:
return '';
}
}
}

View file

@ -763,5 +763,6 @@
"start_tor_on_launch": "ابدأ في الإطلاق",
"tor_status": "وضع تور",
"connected": "متصل",
"disconnected": "انقطع الاتصال"
"disconnected": "انقطع الاتصال",
"onion_only": "البصل فقط"
}

View file

@ -759,5 +759,6 @@
"start_tor_on_launch": "Стартирайте Tor при стартиране",
"tor_status": "TOR статус",
"connected": "Свързани",
"disconnected": "Изключен"
"disconnected": "Изключен",
"onion_only": "Само лук"
}

View file

@ -759,5 +759,6 @@
"start_tor_on_launch": "Začněte tor při spuštění",
"tor_status": "TOR status",
"connected": "Připojeno",
"disconnected": "Odpojené"
"disconnected": "Odpojené",
"onion_only": "Pouze cibule"
}

View file

@ -767,5 +767,6 @@
"start_tor_on_launch": "Starten Sie den Start",
"tor_status": "Torstatus",
"connected": "In Verbindung gebracht",
"disconnected": "Getrennt"
"disconnected": "Getrennt",
"onion_only": "Nur Zwiebel"
}

View file

@ -768,5 +768,6 @@
"start_tor_on_launch": "Start Tor on launch",
"tor_status": "Tor status",
"connected": "Connected",
"disconnected": "Disconnected"
"disconnected": "Disconnected",
"onion_only": "Onion only"
}

View file

@ -767,5 +767,6 @@
"start_tor_on_launch": "Iniciar tor en el lanzamiento",
"tor_status": "Estado de Tor",
"connected": "Conectado",
"disconnected": "Desconectado"
"disconnected": "Desconectado",
"onion_only": "Solo cebolla"
}

View file

@ -767,5 +767,6 @@
"start_tor_on_launch": "Démarrez Tor sur le lancement",
"tor_status": "Statut",
"connected": "Connecté",
"disconnected": "Débranché"
"disconnected": "Débranché",
"onion_only": "Oignon seulement"
}

View file

@ -749,5 +749,6 @@
"start_tor_on_launch": "Fara tor a kan shimfiɗa",
"tor_status": "State State",
"connected": "Wanda aka haɗa",
"disconnected": "Katse"
"disconnected": "Katse",
"onion_only": "Albasa kawai"
}

View file

@ -767,5 +767,6 @@
"start_tor_on_launch": "लॉन्च पर टोर शुरू करें",
"tor_status": "टोर की स्थिति",
"connected": "जुड़े हुए",
"disconnected": "डिस्कनेक्ट किया गया"
"disconnected": "डिस्कनेक्ट किया गया",
"onion_only": "केवल प्याज"
}

View file

@ -765,5 +765,6 @@
"start_tor_on_launch": "Započnite s lansiranjem",
"tor_status": "Status tor",
"connected": "Povezan",
"disconnected": "Isključen"
"disconnected": "Isključen",
"onion_only": "Samo luk"
}

View file

@ -755,5 +755,6 @@
"start_tor_on_launch": "Mulailah untuk peluncuran",
"tor_status": "Status Tor",
"connected": "Terhubung",
"disconnected": "Terputus"
"disconnected": "Terputus",
"onion_only": "Bawang saja"
}

View file

@ -767,5 +767,6 @@
"start_tor_on_launch": "Inizia Tor al lancio",
"tor_status": "Stato di tor",
"connected": "Collegato",
"disconnected": "Disconnesso"
"disconnected": "Disconnesso",
"onion_only": "Solo cipolla"
}

View file

@ -767,5 +767,6 @@
"start_tor_on_launch": "起動時にTORを開始します",
"tor_status": "TORステータス",
"connected": "接続",
"disconnected": "切断された"
"disconnected": "切断された",
"onion_only": "オニオンのみ"
}

View file

@ -765,5 +765,6 @@
"start_tor_on_launch": "출시시 TOR를 시작하십시오",
"tor_status": "Tor 상태",
"connected": "연결",
"disconnected": "연결이 끊어졌습니다"
"disconnected": "연결이 끊어졌습니다",
"onion_only": "양파 만"
}

View file

@ -765,5 +765,6 @@
"start_tor_on_launch": "Tor ကိုဖွင့်ပါ",
"tor_status": "Tor Status",
"connected": "ချိတ်ဆက်ထားသော",
"disconnected": "ချို့ယွင်းချက်"
"disconnected": "ချို့ယွင်းချက်",
"onion_only": "သာကြက်သွန်"
}

View file

@ -767,5 +767,6 @@
"start_tor_on_launch": "Begin met de lancering",
"tor_status": "Tor Status",
"connected": "Verbonden",
"disconnected": "Losgekoppeld"
"disconnected": "Losgekoppeld",
"onion_only": "Alleen ui"
}

View file

@ -767,5 +767,6 @@
"start_tor_on_launch": "Rozpocznij TOR OD LATH",
"tor_status": "Status TOR",
"connected": "Połączony",
"disconnected": "Bezładny"
"disconnected": "Bezładny",
"onion_only": "Tylko cebula"
}

View file

@ -766,5 +766,6 @@
"start_tor_on_launch": "Inicie o TOR no lançamento",
"tor_status": "Status de tor",
"connected": "Conectado",
"disconnected": "Desconectado"
"disconnected": "Desconectado",
"onion_only": "Apenas cebola"
}

View file

@ -767,5 +767,6 @@
"start_tor_on_launch": "Запустить",
"tor_status": "ТОР Статус",
"connected": "Связанный",
"disconnected": "Отключен"
"disconnected": "Отключен",
"onion_only": "Только лук"
}

View file

@ -765,5 +765,6 @@
"start_tor_on_launch": "เริ่มต้นเมื่อเปิดตัว",
"tor_status": "สถานะ tor",
"connected": "ซึ่งเชื่อมต่อกัน",
"disconnected": "ตัดการเชื่อมต่อ"
"disconnected": "ตัดการเชื่อมต่อ",
"onion_only": "หัวหอมเท่านั้น"
}

View file

@ -761,5 +761,6 @@
"start_tor_on_launch": "Simulan ang tor sa paglulunsad",
"tor_status": "Katayuan ng tor",
"connected": "Konektado",
"disconnected": "Naka -disconnect"
"disconnected": "Naka -disconnect",
"onion_only": "Onion lamang"
}

View file

@ -765,5 +765,6 @@
"start_tor_on_launch": "Lansmanda Tor'a Başla",
"tor_status": "Tor Durumu",
"connected": "Bağlı",
"disconnected": "Bağlantı kesildi"
"disconnected": "Bağlantı kesildi",
"onion_only": "Sadece soğan"
}

View file

@ -767,5 +767,6 @@
"start_tor_on_launch": "Почніть Тор на запуску",
"tor_status": "Статус Tor",
"connected": "З'єднаний",
"disconnected": "Роз'єднаний"
"disconnected": "Роз'єднаний",
"onion_only": "Тільки цибуля"
}

View file

@ -759,5 +759,6 @@
"start_tor_on_launch": "لانچ پر ٹور شروع کریں",
"tor_status": "ٹور کی حیثیت",
"connected": "منسلک",
"disconnected": "منقطع"
"disconnected": "منقطع",
"onion_only": "صرف پیاز"
}

View file

@ -761,5 +761,6 @@
"start_tor_on_launch": "Bẹrẹ tor lori ifilole",
"tor_status": "Ipo Tor",
"connected": "Sopọ",
"disconnected": "Ge asopọ"
"disconnected": "Ge asopọ",
"onion_only": "Alubosa nikan"
}

View file

@ -766,5 +766,6 @@
"start_tor_on_launch": "启动TOR发射",
"tor_status": "TOR状态",
"connected": "连接的",
"disconnected": "断开连接"
"disconnected": "断开连接",
"onion_only": "仅洋葱"
}