From d87fb6105a7fd26194581a0eb6d6856e4033b466 Mon Sep 17 00:00:00 2001 From: Ryanmello07 <67509637+Ryanmello07@users.noreply.github.com> Date: Thu, 2 Jul 2026 18:55:22 -0700 Subject: [PATCH 001/109] chore: point iOS package to forked urnetwork-sdk checkout Updates the local binary target path from to so it resolves against the Ryanmello07/urnetwork-sdk fork checked out next to this repo. --- app/URnetworkSdk/Package.swift | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/URnetworkSdk/Package.swift b/app/URnetworkSdk/Package.swift index 4b45d39b..49dad768 100644 --- a/app/URnetworkSdk/Package.swift +++ b/app/URnetworkSdk/Package.swift @@ -1,7 +1,9 @@ // swift-tools-version:5.3 import PackageDescription -// see https://developer.apple.com/documentation/xcode/distributing-binary-frameworks-as-swift-packages +// Points to the local checkout of the forked SDK: +// https://github.com/Ryanmello07/urnetwork-sdk +// Build the XCFramework from that repo before using this package. let package = Package( name: "URnetworkSdk", products: [ @@ -13,7 +15,7 @@ let package = Package( targets: [ .binaryTarget( name: "URnetworkSdkBinary", - path: "../../../sdk/build/apple/URnetworkSdk.xcframework" + path: "../../../urnetwork-sdk/build/apple/URnetworkSdk.xcframework" ), .testTarget( name: "URnetworkSdkTests", From 6ed515ed97ef18803461ccdfe049c00c307db349 Mon Sep 17 00:00:00 2001 From: Ryanmello07 <67509637+Ryanmello07@users.noreply.github.com> Date: Fri, 3 Jul 2026 05:24:29 -0700 Subject: [PATCH 002/109] ci: add beta build workflow for unsigned iOS IPA Builds the forked urnetwork-sdk for iOS, then archives and packages an unsigned IPA on pushes to beta/custom-server and workflow_dispatch. Uploads the IPA to a GitHub prerelease on the forked iOS repo. --- .github/workflows/beta-build.yml | 95 ++++++++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 .github/workflows/beta-build.yml diff --git a/.github/workflows/beta-build.yml b/.github/workflows/beta-build.yml new file mode 100644 index 00000000..e8b1458e --- /dev/null +++ b/.github/workflows/beta-build.yml @@ -0,0 +1,95 @@ +name: Beta Build — iOS + +on: + push: + branches: + - beta/custom-server + workflow_dispatch: + +permissions: + contents: write + +env: + WARP_VERSION: beta-${{ github.run_number }} + +jobs: + build-ios: + name: Build unsigned iOS app + runs-on: macos-latest + steps: + - name: Check out iOS repo + uses: actions/checkout@v4 + + - name: Check out forked SDK + uses: actions/checkout@v4 + with: + repository: Ryanmello07/urnetwork-sdk + ref: beta/custom-server + path: urnetwork-sdk + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version-file: urnetwork-sdk/go.mod + + - name: Build SDK for iOS + working-directory: urnetwork-sdk/build + env: + BRINGYOUR_HOME: ${{ github.workspace }}/urnetwork-sdk + WARP_HOME: ${{ github.workspace }}/urnetwork-sdk + WARP_VERSION_HOME: ${{ github.workspace }}/urnetwork-sdk/version + run: | + export PATH="$PATH:/usr/local/go/bin:$HOME/go/bin" + make init build_apple + + - name: Resolve Swift packages + working-directory: app + run: | + xcodebuild -resolvePackageDependencies \ + -project app.xcodeproj \ + -scheme URnetwork \ + -derivedDataPath DerivedData + + - name: Archive unsigned iOS app + working-directory: app + run: | + set -o pipefail + xcodebuild archive \ + -project app.xcodeproj \ + -scheme URnetwork \ + -configuration Release \ + -destination 'generic/platform=iOS' \ + -archivePath build/URnetwork.xcarchive \ + -derivedDataPath DerivedData \ + CURRENT_PROJECT_VERSION=${{ github.run_number }} \ + MARKETING_VERSION=beta \ + CODE_SIGNING_ALLOWED=NO \ + CODE_SIGNING_REQUIRED=NO \ + CODE_SIGN_IDENTITY="" \ + PROVISIONING_PROFILE_SPECIFIER="" \ + 2>&1 | tee xcodebuild-archive.log + + - name: Package unsigned IPA + working-directory: app + run: | + APP=$(find build/URnetwork.xcarchive -name 'URnetwork.app' -type d | head -n 1) + if [ -z "$APP" ]; then + echo "Could not find URnetwork.app in archive" + exit 1 + fi + mkdir -p Payload + cp -R "$APP" Payload/ + zip -r URnetwork-beta.ipa Payload + + - name: Create prerelease + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + TAG: beta-custom-server-${{ github.run_number }}-${{ github.run_attempt }} + run: | + gh release delete "$TAG" --repo "$GITHUB_REPOSITORY" --yes || true + gh release create "$TAG" \ + --repo "$GITHUB_REPOSITORY" \ + --prerelease \ + --title "URnetwork iOS beta custom server ${{ github.run_number }}" \ + --generate-notes \ + app/URnetwork-beta.ipa From 21545f2abd105bcdb6c865f168490417165aba4f Mon Sep 17 00:00:00 2001 From: Ryanmello07 <67509637+Ryanmello07@users.noreply.github.com> Date: Fri, 3 Jul 2026 05:25:41 -0700 Subject: [PATCH 003/109] ci: use stable Go version for beta build --- .github/workflows/beta-build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/beta-build.yml b/.github/workflows/beta-build.yml index e8b1458e..f502749a 100644 --- a/.github/workflows/beta-build.yml +++ b/.github/workflows/beta-build.yml @@ -30,7 +30,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v5 with: - go-version-file: urnetwork-sdk/go.mod + go-version: stable - name: Build SDK for iOS working-directory: urnetwork-sdk/build From fa47d178ac67775d507b0318384890da326602fc Mon Sep 17 00:00:00 2001 From: Ryanmello07 <67509637+Ryanmello07@users.noreply.github.com> Date: Fri, 3 Jul 2026 05:30:12 -0700 Subject: [PATCH 004/109] ci: clone urnetwork/connect next to SDK for go.mod replace --- .github/workflows/beta-build.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/beta-build.yml b/.github/workflows/beta-build.yml index f502749a..2358a92c 100644 --- a/.github/workflows/beta-build.yml +++ b/.github/workflows/beta-build.yml @@ -32,6 +32,9 @@ jobs: with: go-version: stable + - name: Check out connect dependency + run: git clone --depth 1 https://github.com/urnetwork/connect.git ../connect + - name: Build SDK for iOS working-directory: urnetwork-sdk/build env: From 8a23ee36b70dd6b9b92ffa3d299217629106397f Mon Sep 17 00:00:00 2001 From: Ryanmello07 <67509637+Ryanmello07@users.noreply.github.com> Date: Fri, 3 Jul 2026 05:35:11 -0700 Subject: [PATCH 005/109] ci: clone connect into workspace root for app workflows --- .github/workflows/beta-build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/beta-build.yml b/.github/workflows/beta-build.yml index 2358a92c..2af1f6e7 100644 --- a/.github/workflows/beta-build.yml +++ b/.github/workflows/beta-build.yml @@ -33,7 +33,7 @@ jobs: go-version: stable - name: Check out connect dependency - run: git clone --depth 1 https://github.com/urnetwork/connect.git ../connect + run: git clone --depth 1 https://github.com/urnetwork/connect.git connect - name: Build SDK for iOS working-directory: urnetwork-sdk/build From da1661f9b7cb075b32f6b7d2bd7ea780ea8ee28b Mon Sep 17 00:00:00 2001 From: Ryanmello07 <67509637+Ryanmello07@users.noreply.github.com> Date: Fri, 3 Jul 2026 05:45:42 -0700 Subject: [PATCH 006/109] ci: check out glog dependency and use forked connect for beta build --- .github/workflows/beta-build.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/beta-build.yml b/.github/workflows/beta-build.yml index 2af1f6e7..679ce30c 100644 --- a/.github/workflows/beta-build.yml +++ b/.github/workflows/beta-build.yml @@ -33,7 +33,10 @@ jobs: go-version: stable - name: Check out connect dependency - run: git clone --depth 1 https://github.com/urnetwork/connect.git connect + run: git clone --depth 1 --branch beta/custom-server https://github.com/Ryanmello07/connect.git connect + + - name: Check out glog dependency + run: git clone --depth 1 https://github.com/urnetwork/glog.git glog - name: Build SDK for iOS working-directory: urnetwork-sdk/build From e991594fb9c91eae1530bb57b0f93e08ae52ee4f Mon Sep 17 00:00:00 2001 From: Ryanmello07 <67509637+Ryanmello07@users.noreply.github.com> Date: Fri, 3 Jul 2026 06:19:50 -0700 Subject: [PATCH 007/109] ci: check out iOS repo as sibling of urnetwork-sdk so Package.swift can find the built xcframework --- .github/workflows/beta-build.yml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/workflows/beta-build.yml b/.github/workflows/beta-build.yml index 679ce30c..d71bb720 100644 --- a/.github/workflows/beta-build.yml +++ b/.github/workflows/beta-build.yml @@ -19,6 +19,8 @@ jobs: steps: - name: Check out iOS repo uses: actions/checkout@v4 + with: + path: urnetwork-ios - name: Check out forked SDK uses: actions/checkout@v4 @@ -49,7 +51,7 @@ jobs: make init build_apple - name: Resolve Swift packages - working-directory: app + working-directory: urnetwork-ios/app run: | xcodebuild -resolvePackageDependencies \ -project app.xcodeproj \ @@ -57,7 +59,7 @@ jobs: -derivedDataPath DerivedData - name: Archive unsigned iOS app - working-directory: app + working-directory: urnetwork-ios/app run: | set -o pipefail xcodebuild archive \ @@ -76,7 +78,7 @@ jobs: 2>&1 | tee xcodebuild-archive.log - name: Package unsigned IPA - working-directory: app + working-directory: urnetwork-ios/app run: | APP=$(find build/URnetwork.xcarchive -name 'URnetwork.app' -type d | head -n 1) if [ -z "$APP" ]; then @@ -98,4 +100,4 @@ jobs: --prerelease \ --title "URnetwork iOS beta custom server ${{ github.run_number }}" \ --generate-notes \ - app/URnetwork-beta.ipa + urnetwork-ios/app/URnetwork-beta.ipa From 7ee9b98af8e9407f1339ac866cf796b3ac021495 Mon Sep 17 00:00:00 2001 From: Ryanmello07 <67509637+Ryanmello07@users.noreply.github.com> Date: Sat, 4 Jul 2026 03:48:12 -0700 Subject: [PATCH 008/109] feat(sdk-service): add authWalletChallenge to UrApiService --- .../UrApiService/MockUrApiService.swift | 4 +++ .../Services/UrApiService/UrApiService.swift | 29 +++++++++++++++++++ .../UrApiService/UrApiServiceProtocol.swift | 1 + 3 files changed, 34 insertions(+) diff --git a/app/network/Shared/Services/UrApiService/MockUrApiService.swift b/app/network/Shared/Services/UrApiService/MockUrApiService.swift index 332cbc5b..d1fa120c 100644 --- a/app/network/Shared/Services/UrApiService/MockUrApiService.swift +++ b/app/network/Shared/Services/UrApiService/MockUrApiService.swift @@ -40,6 +40,10 @@ class MockUrApiService: UrApiServiceProtocol { func createNetwork(_ args: SdkNetworkCreateArgs) async throws -> LoginNetworkResult { return LoginNetworkResult.failure(LoginError.appleLoginFailed) } + + func authWalletChallenge(_ args: SdkAuthWalletChallengeArgs) async throws -> SdkAuthWalletChallengeResult { + return SdkAuthWalletChallengeResult() + } func validateReferralCode(_ code: String) async throws -> SdkValidateReferralCodeResult { SdkValidateReferralCodeResult() diff --git a/app/network/Shared/Services/UrApiService/UrApiService.swift b/app/network/Shared/Services/UrApiService/UrApiService.swift index 7d22ef68..d9a2f08b 100644 --- a/app/network/Shared/Services/UrApiService/UrApiService.swift +++ b/app/network/Shared/Services/UrApiService/UrApiService.swift @@ -514,6 +514,35 @@ extension UrApiService { } } + + func authWalletChallenge(_ args: SdkAuthWalletChallengeArgs) async throws -> SdkAuthWalletChallengeResult { + return try await withCheckedThrowingContinuation { continuation in + + let callback = AuthWalletChallengeCallback { result, err in + + if let err = err { + continuation.resume(throwing: err) + return + } + + guard let result = result else { + continuation.resume(throwing: NSError(domain: "UrApiService", code: 0, userInfo: [NSLocalizedDescriptionKey: "No result found in callback"])) + return + } + + if let resultError = result.error { + continuation.resume(throwing: NSError(domain: "UrApiService", code: 0, userInfo: [NSLocalizedDescriptionKey: resultError.message])) + return + } + + continuation.resume(returning: result) + + } + + api.authWalletChallenge(args, callback: callback) + + } + } } diff --git a/app/network/Shared/Services/UrApiService/UrApiServiceProtocol.swift b/app/network/Shared/Services/UrApiService/UrApiServiceProtocol.swift index 1cde857a..b5a15379 100644 --- a/app/network/Shared/Services/UrApiService/UrApiServiceProtocol.swift +++ b/app/network/Shared/Services/UrApiService/UrApiServiceProtocol.swift @@ -37,6 +37,7 @@ protocol UrApiServiceProtocol { func upgradeGuest(_ args: SdkUpgradeGuestArgs) async throws -> LoginNetworkResult func createAuthCode() async throws -> SdkAuthCodeCreateResult func authCodeLogin(_ args: SdkAuthCodeLoginArgs) async throws -> SdkAuthCodeLoginResult + func authWalletChallenge(_ args: SdkAuthWalletChallengeArgs) async throws -> SdkAuthWalletChallengeResult /** * Subscription From b1eff2d515995b94eae58df081c3ac2975163699 Mon Sep 17 00:00:00 2001 From: Ryanmello07 <67509637+Ryanmello07@users.noreply.github.com> Date: Sat, 4 Jul 2026 03:49:07 -0700 Subject: [PATCH 009/109] feat(login): add Solana wallet-challenge fetch to LoginInitialViewModel --- .../LoginInitial/LoginInitialViewModel.swift | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/app/network/Authenticate/LoginInitial/LoginInitialViewModel.swift b/app/network/Authenticate/LoginInitial/LoginInitialViewModel.swift index cf70a141..80828929 100644 --- a/app/network/Authenticate/LoginInitial/LoginInitialViewModel.swift +++ b/app/network/Authenticate/LoginInitial/LoginInitialViewModel.swift @@ -112,6 +112,27 @@ extension LoginInitialView { func setIsSigningMessage(_ isSigning: Bool) -> Void { isSigningMessage = isSigning } + + @Published private(set) var solanaChallengeMessage: String? + + /// Fetches a fresh, server-issued wallet-auth challenge and stores its + /// message template for the wallet to sign. Must be called again for + /// every sign attempt — the server invalidates a challenge the moment + /// it is checked, whether the check succeeds or fails. + func prepareSolanaChallenge() async -> Bool { + let args = SdkAuthWalletChallengeArgs() + args.blockchain = "solana" + + do { + let result = try await urApiService.authWalletChallenge(args) + solanaChallengeMessage = result.messageTemplate + return true + } catch { + solanaChallengeMessage = nil + setLoginErrorMessage("There was an error connecting to your wallet") + return false + } + } let termsLink = "https://ur.io/terms" From 30e768ce4c1ac3ce959e39a68916bc0c128df4c4 Mon Sep 17 00:00:00 2001 From: Ryanmello07 <67509637+Ryanmello07@users.noreply.github.com> Date: Sat, 4 Jul 2026 03:50:26 -0700 Subject: [PATCH 010/109] fix(login): sign server-issued challenge for Solana login (iOS) --- .../LoginInitial/LoginInitialView.swift | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/app/network/Authenticate/LoginInitial/LoginInitialView.swift b/app/network/Authenticate/LoginInitial/LoginInitialView.swift index fae6ce4c..e90c9223 100644 --- a/app/network/Authenticate/LoginInitial/LoginInitialView.swift +++ b/app/network/Authenticate/LoginInitial/LoginInitialView.swift @@ -74,7 +74,12 @@ struct LoginInitialView: View { loginErrorMessage: viewModel.loginErrorMessage, deviceExists: deviceExists, presentSignInWithSolanaSheet: { - viewModel.setPresentSigninWithSolanaSheet(true) + Task { + let ok = await viewModel.prepareSolanaChallenge() + if ok { + viewModel.setPresentSigninWithSolanaSheet(true) + } + } }, presentAuthCodeLoginSheet: { viewModel.setPresentAuthCodeLoginSheet(true) @@ -105,7 +110,12 @@ struct LoginInitialView: View { loginErrorMessage: viewModel.loginErrorMessage, deviceExists: deviceExists, presentSignInWithSolanaSheet: { - viewModel.setPresentSigninWithSolanaSheet(true) + Task { + let ok = await viewModel.prepareSolanaChallenge() + if ok { + viewModel.setPresentSigninWithSolanaSheet(true) + } + } }, presentAuthCodeLoginSheet: { viewModel.setPresentAuthCodeLoginSheet(true) @@ -129,7 +139,7 @@ struct LoginInitialView: View { setIsSigningMessage: viewModel.setIsSigningMessage, signButtonText: "Sign in with Solana", signButtonLabelText: "Sign in", - message: connectWalletProviderViewModel.welcomeMessage, + message: viewModel.solanaChallengeMessage ?? "", dismiss: { viewModel.setPresentSigninWithSolanaSheet(false) } @@ -234,7 +244,7 @@ struct LoginInitialView: View { Task { await handleSolanaWalletResult( - message: connectWalletProviderViewModel.welcomeMessage, + message: viewModel.solanaChallengeMessage ?? "", signature: signature, publicKey: pk ) From 91d1ab52af510492a50e22e6a13e349e18df70a1 Mon Sep 17 00:00:00 2001 From: Ryanmello07 <67509637+Ryanmello07@users.noreply.github.com> Date: Sat, 4 Jul 2026 03:51:35 -0700 Subject: [PATCH 011/109] fix(login): re-sign a fresh challenge before navigating to create-network (iOS) --- .../LoginInitial/LoginInitialView.swift | 53 +++++++++++++++---- .../LoginInitial/LoginInitialViewModel.swift | 2 + 2 files changed, 44 insertions(+), 11 deletions(-) diff --git a/app/network/Authenticate/LoginInitial/LoginInitialView.swift b/app/network/Authenticate/LoginInitial/LoginInitialView.swift index e90c9223..da2053e8 100644 --- a/app/network/Authenticate/LoginInitial/LoginInitialView.swift +++ b/app/network/Authenticate/LoginInitial/LoginInitialView.swift @@ -262,37 +262,53 @@ struct LoginInitialView: View { private func handleSolanaWalletResult(message: String, signature: String, publicKey: String) async { print("handleSolanaWalletResult") - + + if viewModel.isSigningForCreateNetwork { + viewModel.isSigningForCreateNetwork = false + viewModel.setIsSigningMessage(false) + viewModel.presentSigninWithSolanaSheet = false + + let createArgsResult = viewModel.createSolanaAuthLoginArgs(message: message, signature: signature, publicKey: publicKey) + switch createArgsResult { + case .success(let args): + navigate(.createNetwork(args)) + case .failure(let error): + print("error create args result: \(error.localizedDescription)") + viewModel.setLoginErrorMessage("There was an error logging in") + } + return + } + guard viewModel.beginLoginAction(.solana) else { return } - + defer { viewModel.endLoginAction(.solana) } - + let createArgsResult = viewModel.createSolanaAuthLoginArgs(message: message, signature: signature, publicKey: publicKey) switch createArgsResult { case .success(let args): - + if deviceManager.device != nil { - + let upgradeArgs = self.createUpgradeSolanaWalletArgs(args) - + let result = await guestUpgradeViewModel.linkGuestToExistingLogin(args: upgradeArgs) - + await self.handleAuthLoginResult(result) viewModel.presentSigninWithSolanaSheet = false viewModel.setIsSigningMessage(false) - - + + } else { let result = await viewModel.authLogin(args: args) await self.handleAuthLoginResult(result) viewModel.presentSigninWithSolanaSheet = false viewModel.setIsSigningMessage(false) } - + case .failure(let error): print("error create args result: \(error.localizedDescription)") viewModel.setIsSigningMessage(false) @@ -399,7 +415,22 @@ struct LoginInitialView: View { case .create(let authLoginArgs): viewModel.setIsCheckingUserAuth(false) - navigate(.createNetwork(authLoginArgs)) + + if authLoginArgs.walletAuth != nil { + // the wallet challenge behind authLoginArgs was already + // consumed by the /auth/login call that just returned this + // .create case — fetch and sign a brand-new one before + // creating the network + viewModel.isSigningForCreateNetwork = true + let ok = await viewModel.prepareSolanaChallenge() + if ok { + viewModel.setPresentSigninWithSolanaSheet(true) + } else { + viewModel.isSigningForCreateNetwork = false + } + } else { + navigate(.createNetwork(authLoginArgs)) + } break case .verificationRequired(let userAuth): diff --git a/app/network/Authenticate/LoginInitial/LoginInitialViewModel.swift b/app/network/Authenticate/LoginInitial/LoginInitialViewModel.swift index 80828929..c6618224 100644 --- a/app/network/Authenticate/LoginInitial/LoginInitialViewModel.swift +++ b/app/network/Authenticate/LoginInitial/LoginInitialViewModel.swift @@ -115,6 +115,8 @@ extension LoginInitialView { @Published private(set) var solanaChallengeMessage: String? + @Published var isSigningForCreateNetwork: Bool = false + /// Fetches a fresh, server-issued wallet-auth challenge and stores its /// message template for the wallet to sign. Must be called again for /// every sign attempt — the server invalidates a challenge the moment From faf62cc824a383aa2818815e25dc14aaee4f7181 Mon Sep 17 00:00:00 2001 From: Ryanmello07 <67509637+Ryanmello07@users.noreply.github.com> Date: Sat, 4 Jul 2026 04:09:58 -0700 Subject: [PATCH 012/109] fix: wrap authWalletChallenge SDK callback in SdkCallback helper --- app/network/Shared/Services/UrApiService/UrApiService.swift | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/network/Shared/Services/UrApiService/UrApiService.swift b/app/network/Shared/Services/UrApiService/UrApiService.swift index d9a2f08b..4959ff91 100644 --- a/app/network/Shared/Services/UrApiService/UrApiService.swift +++ b/app/network/Shared/Services/UrApiService/UrApiService.swift @@ -969,6 +969,12 @@ private class AuthLoginCallback: SdkCallback, SdkAuthWalletChallengeCallbackProtocol { + func result(_ result: SdkAuthWalletChallengeResult?, err: Error?) { + handleResult(result, err: err) + } +} + private class ValidateReferralCallback: SdkCallback, SdkValidateReferralCodeCallbackProtocol { func result(_ result: SdkValidateReferralCodeResult?, err: Error?) { handleResult(result, err: err) From c603552ee4b0fb749bbd80cbfec4f658e667a25c Mon Sep 17 00:00:00 2001 From: Ryanmello07 <67509637+Ryanmello07@users.noreply.github.com> Date: Sat, 11 Jul 2026 07:01:57 -0700 Subject: [PATCH 013/109] ios: fix AuthNetworkClientArgs field rename after connect/sdk merge Upstream renamed AuthNetworkClientArgs.Description to DeviceDescription in the Go SDK specifically because 'description' collides with the read-only NSObject.description property in the gomobile Swift bindings - which is exactly the compile error this fixes ("cannot assign to property: 'description' is immutable"). --- app/network/Shared/ViewModels/DeviceManager.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/network/Shared/ViewModels/DeviceManager.swift b/app/network/Shared/ViewModels/DeviceManager.swift index 6c17e71e..d473ea58 100644 --- a/app/network/Shared/ViewModels/DeviceManager.swift +++ b/app/network/Shared/ViewModels/DeviceManager.swift @@ -905,7 +905,7 @@ extension DeviceManager { let result: Void = try await withCheckedThrowingContinuation { continuation in let authArgs = SdkAuthNetworkClientArgs() - authArgs.description = deviceDescription + authArgs.deviceDescription = deviceDescription authArgs.deviceSpec = deviceSpecs let callback = AuthNetworkClientCallback { [weak self] result, error in From a024cad8a66a419d2cd9bc35f4b65f295bb2b863 Mon Sep 17 00:00:00 2001 From: Ryanmello07 <67509637+Ryanmello07@users.noreply.github.com> Date: Sat, 11 Jul 2026 08:15:25 -0700 Subject: [PATCH 014/109] ios: add Change Network API selector (iOS + macOS) Ports Android's NetworkServerSelector.kt to iOS/macOS. Lets a user point the app at a different network domain (self-hosted or alternate) instead of the hardcoded ur.network, or set explicit API/connect url overrides. - NetworkConfig.swift: names the official host/env/store/wallet/sso defaults that were previously inlined in DeviceManager with a "TODO: this should be moved into a config" comment. Values are unchanged - this only names them so a reset button has something to reset to. Note ios's wallet="circle" default is intentionally left alone (not copied from Android's "solana"/"ethos" flavor values, which govern something else - Solana wallet sign-in already works correctly here independent of this field). - DeviceManager.swift: exposes the underlying SdkNetworkSpaceManager (previously a local var, only used once at init) plus current/ configured api+connect url accessors, and an applyNetworkSpace(...) method that switches the active network space. No SDK/Go changes needed - NetworkSpaceManager/NetworkSpace already exist in the shared Go SDK and are used identically by Android. - NetworkServerUtils.swift: Swift port of the Kotlin normalization helpers (host/url normalization, insecure-scheme detection, derived service url preview). - NetworkServerSheet(+ViewModel): the settings sheet itself, modeled on the existing UpdateReferralNetworkSheet pattern already used elsewhere in this codebase. - LoginInitialView.swift: adds a "Change Network API" entry point next to (but outside the iOS-only gating of) "Try Guest Mode", available on both iOS and macOS per direction - unlike Solana sign-in, which stays iOS-only since it needs a browser-based flow on desktop that isn't built yet. --- .../LoginInitial/LoginInitialView.swift | 39 ++++ app/network/Shared/Config/NetworkConfig.swift | 31 +++ .../Shared/Utilities/NetworkServerUtils.swift | 92 +++++++++ .../Shared/ViewModels/DeviceManager.swift | 91 +++++++-- .../NetworkServerSheet.swift | 178 ++++++++++++++++++ .../NetworkServerSheetViewModel.swift | 96 ++++++++++ 6 files changed, 516 insertions(+), 11 deletions(-) create mode 100644 app/network/Shared/Config/NetworkConfig.swift create mode 100644 app/network/Shared/Utilities/NetworkServerUtils.swift create mode 100644 app/network/Shared/Views/NetworkServerSheet/NetworkServerSheet.swift create mode 100644 app/network/Shared/Views/NetworkServerSheet/NetworkServerSheetViewModel.swift diff --git a/app/network/Authenticate/LoginInitial/LoginInitialView.swift b/app/network/Authenticate/LoginInitial/LoginInitialView.swift index da2053e8..8bf59177 100644 --- a/app/network/Authenticate/LoginInitial/LoginInitialView.swift +++ b/app/network/Authenticate/LoginInitial/LoginInitialView.swift @@ -548,6 +548,7 @@ struct LoginInitialView: View { private struct LoginInitialFormView: View { @EnvironmentObject var themeManager: ThemeManager + @EnvironmentObject var deviceManager: DeviceManager @Binding var userAuth: String let handleUserAuth: () async -> Void @@ -562,6 +563,7 @@ private struct LoginInitialFormView: View { let presentAuthCodeLoginSheet: () -> Void @Binding var presentGuestNetworkSheet: Bool + @State private var presentNetworkServerSheet = false var body: some View { @@ -680,9 +682,46 @@ private struct LoginInitialFormView: View { } #endif + + Spacer() + .frame(height: 8) + + HStack { + Button(action: { + presentNetworkServerSheet = true + }) { + Text("Change Network API") + .font(themeManager.currentTheme.secondaryBodyFont) + .foregroundColor(themeManager.currentTheme.textMutedColor) + } + .buttonStyle(.plain) + .disabled(isLoginActionInFlight) + + Spacer() + } } .frame(maxWidth: 400) + .sheet(isPresented: $presentNetworkServerSheet) { + NetworkServerSheet( + initialHostName: deviceManager.activeHostName, + currentApiUrl: deviceManager.activeApiUrl, + currentConnectUrl: deviceManager.activePlatformUrl, + configuredApiUrl: deviceManager.configuredApiUrl, + configuredConnectUrl: deviceManager.configuredPlatformUrl, + managerAvailable: deviceManager.networkSpaceManager != nil, + onApply: { hostName, apiUrl, connectUrl in + deviceManager.applyNetworkSpace(hostName: hostName, apiUrl: apiUrl, connectUrl: connectUrl) + }, + dismiss: { + presentNetworkServerSheet = false + } + ) + .environmentObject(themeManager) + #if os(macOS) + .frame(minWidth: 420, minHeight: 480) + #endif + } } } diff --git a/app/network/Shared/Config/NetworkConfig.swift b/app/network/Shared/Config/NetworkConfig.swift new file mode 100644 index 00000000..32ee44d0 --- /dev/null +++ b/app/network/Shared/Config/NetworkConfig.swift @@ -0,0 +1,31 @@ +// +// NetworkConfig.swift +// URnetwork +// +// Default network space values for the official URnetwork server. These are +// the same values `DeviceManager.initializeNetworkSpace()` used to hardcode +// inline; naming them here lets the network-server selector (see +// `NetworkServerSheet`) reset back to these defaults, mirroring how the +// Android app's per-flavor `BuildConfig.BRINGYOUR_BUNDLE_*` constants work. +// +// Note: `wallet` is `"circle"` here, not `"solana"` - this is intentionally +// different from Android's flavors and governs the app's own concept of a +// wallet, separate from the "Sign in with Solana" wallet-login feature. +// Do not change this to match Android without confirming what it controls. +// + +import Foundation + +struct NetworkConfig { + static let officialHostName = "ur.network" + static let officialEnvName = "main" + static let officialLinkHostName = "ur.io" + static let officialMigrationHostName = "bringyour.com" + + static let envSecret = "" + static let store = "" + static let wallet = "circle" + static let ssoGoogle = false + static let netExposeServerIps = true + static let netExposeServerHostNames = true +} diff --git a/app/network/Shared/Utilities/NetworkServerUtils.swift b/app/network/Shared/Utilities/NetworkServerUtils.swift new file mode 100644 index 00000000..58ade3ff --- /dev/null +++ b/app/network/Shared/Utilities/NetworkServerUtils.swift @@ -0,0 +1,92 @@ +// +// NetworkServerUtils.swift +// URnetwork +// +// Swift port of Android's `NetworkServerSelector.kt` normalization helpers, +// used by `NetworkServerSheet` to validate/derive urls for a custom network +// server. Kept as free functions so they're trivially testable and shared +// between iOS and macOS. +// + +import Foundation + +enum NetworkServerUtils { + + static func normalizeNetworkHost(_ raw: String) -> String { + var value = raw.trimmingCharacters(in: .whitespacesAndNewlines).lowercased() + if let range = value.range(of: "://") { + value = String(value[range.upperBound...]) + } + for separator in ["/", "?", "#"] { + if let range = value.range(of: separator) { + value = String(value[.. String? { + let value = raw.trimmingCharacters(in: .whitespacesAndNewlines) + guard let range = value.range(of: "://") else { + return nil + } + let scheme = String(value[.. String { + var value = raw.trimmingCharacters(in: .whitespacesAndNewlines) + while value.hasSuffix("/") { + value.removeLast() + } + if value.isEmpty { + return "" + } + if value.contains("://") { + return value + } + return "https://\(value)" + } + + static func normalizeConnectUrl(_ raw: String) -> String { + var value = raw.trimmingCharacters(in: .whitespacesAndNewlines) + while value.hasSuffix("/") { + value.removeLast() + } + if value.isEmpty { + return "" + } + if value.contains("://") { + return value + } + return "wss://\(value)" + } + + static func hasInsecureScheme(_ raw: String, secureScheme: String) -> Bool { + guard let scheme = explicitScheme(raw) else { + return false + } + return scheme != secureScheme + } + + /// Mirrors the SDK's own `ServiceUrl` derivation (host/env -> api./connect. + /// subdomains), so the sheet can show an accurate placeholder/preview + /// before the user applies anything. + static func derivedServiceUrl( + hostName: String, + migrationHostName: String, + envName: String, + scheme: String, + service: String + ) -> String { + let serviceHost = migrationHostName.isEmpty ? hostName : migrationHostName + let serviceHostName = (envName == "main" || envName.isEmpty) + ? "\(service).\(serviceHost)" + : "\(envName)-\(service).\(serviceHost)" + return "\(scheme)://\(serviceHostName)" + } +} diff --git a/app/network/Shared/ViewModels/DeviceManager.swift b/app/network/Shared/ViewModels/DeviceManager.swift index d473ea58..02b488c9 100644 --- a/app/network/Shared/ViewModels/DeviceManager.swift +++ b/app/network/Shared/ViewModels/DeviceManager.swift @@ -28,6 +28,8 @@ class DeviceManager: ObservableObject { // updateParsedJwt() } } + + @Published private(set) var networkSpaceManager: SdkNetworkSpaceManager? var api: SdkApi? { get { @@ -521,23 +523,23 @@ extension DeviceManager { let deviceSpecs = self.getDeviceSpecs() let networkSpaceManager = URnetworkSdk.SdkNewNetworkSpaceManager(storagePath) + self.networkSpaceManager = networkSpaceManager - let hostName = "ur.network" - let envName = "main" + let hostName = NetworkConfig.officialHostName + let envName = NetworkConfig.officialEnvName let networkSpaceKey = URnetworkSdk.SdkNewNetworkSpaceKey(hostName, envName) networkSpaceManager?.updateNetworkSpace(networkSpaceKey, callback: NetworkSpaceUpdateCallback( c: { networkSpaceValues in - // TODO: this should be moved into a config - networkSpaceValues.envSecret = "" + networkSpaceValues.envSecret = NetworkConfig.envSecret networkSpaceValues.bundled = true - networkSpaceValues.netExposeServerIps = true - networkSpaceValues.netExposeServerHostNames = true - networkSpaceValues.linkHostName = "ur.io" - networkSpaceValues.migrationHostName = "bringyour.com" - networkSpaceValues.store = "" - networkSpaceValues.wallet = "circle" - networkSpaceValues.ssoGoogle = false + networkSpaceValues.netExposeServerIps = NetworkConfig.netExposeServerIps + networkSpaceValues.netExposeServerHostNames = NetworkConfig.netExposeServerHostNames + networkSpaceValues.linkHostName = NetworkConfig.officialLinkHostName + networkSpaceValues.migrationHostName = NetworkConfig.officialMigrationHostName + networkSpaceValues.store = NetworkConfig.store + networkSpaceValues.wallet = NetworkConfig.wallet + networkSpaceValues.ssoGoogle = NetworkConfig.ssoGoogle } )) @@ -573,6 +575,73 @@ extension DeviceManager { } +// MARK: Network server selection +@MainActor +extension DeviceManager { + + var activeHostName: String { + networkSpace?.getHostName() ?? NetworkConfig.officialHostName + } + + var activeApiUrl: String { + networkSpace?.getApiUrl() ?? "" + } + + var activePlatformUrl: String { + networkSpace?.getPlatformUrl() ?? "" + } + + var configuredApiUrl: String { + networkSpace?.getConfiguredApiUrl() ?? "" + } + + var configuredPlatformUrl: String { + networkSpace?.getConfiguredPlatformUrl() ?? "" + } + + /// Switches the active network space to `hostName`, deriving api/connect + /// urls unless explicit overrides are given. Mirrors Android's + /// `NetworkServerSelector`/`updateNetworkSpace` flow - both platforms + /// share the same underlying Go `NetworkSpaceManager`. + func applyNetworkSpace( + hostName: String, + apiUrl: String, + connectUrl: String + ) -> Bool { + guard let networkSpaceManager else { + return false + } + + let isOfficial = hostName == NetworkConfig.officialHostName + let hasExplicitUrls = !apiUrl.isEmpty || !connectUrl.isEmpty + let key = URnetworkSdk.SdkNewNetworkSpaceKey(hostName, NetworkConfig.officialEnvName) + + let updated = networkSpaceManager.updateNetworkSpace(key, callback: NetworkSpaceUpdateCallback( + c: { values in + values.envSecret = NetworkConfig.envSecret + values.bundled = isOfficial && !hasExplicitUrls + values.netExposeServerIps = NetworkConfig.netExposeServerIps + values.netExposeServerHostNames = NetworkConfig.netExposeServerHostNames + values.linkHostName = isOfficial ? NetworkConfig.officialLinkHostName : hostName + values.migrationHostName = isOfficial ? NetworkConfig.officialMigrationHostName : "" + values.store = NetworkConfig.store + values.wallet = NetworkConfig.wallet + values.ssoGoogle = NetworkConfig.ssoGoogle + values.apiUrl = apiUrl + values.platformUrl = connectUrl + } + )) + + guard let updated else { + return false + } + + networkSpaceManager.setActiveNetworkSpace(updated) + self.networkSpace = updated + return true + } +} + // MARK: Device handlers @MainActor extension DeviceManager { diff --git a/app/network/Shared/Views/NetworkServerSheet/NetworkServerSheet.swift b/app/network/Shared/Views/NetworkServerSheet/NetworkServerSheet.swift new file mode 100644 index 00000000..e6295a3f --- /dev/null +++ b/app/network/Shared/Views/NetworkServerSheet/NetworkServerSheet.swift @@ -0,0 +1,178 @@ +// +// NetworkServerSheet.swift +// URnetwork +// +// "Change Network API" sheet - lets the user point the app at a different +// network domain (self-hosted or alternate) instead of the official +// ur.network, or enter explicit API/connect url overrides. Available on +// both iOS and macOS (unlike the Solana wallet sign-in flow, which is +// iOS-only). Ported from Android's `NetworkServerSelector.kt`. +// + +import SwiftUI +import URnetworkSdk + +struct NetworkServerSheet: View { + + @EnvironmentObject var themeManager: ThemeManager + @StateObject private var viewModel: ViewModel + + var currentApiUrl: String + var currentConnectUrl: String + var managerAvailable: Bool + var onApply: (_ hostName: String, _ apiUrl: String, _ connectUrl: String) -> Bool + var dismiss: () -> Void + + init( + initialHostName: String, + currentApiUrl: String, + currentConnectUrl: String, + configuredApiUrl: String, + configuredConnectUrl: String, + managerAvailable: Bool, + onApply: @escaping (_ hostName: String, _ apiUrl: String, _ connectUrl: String) -> Bool, + dismiss: @escaping () -> Void + ) { + self._viewModel = .init(wrappedValue: .init( + initialHostName: initialHostName, + configuredApiUrl: configuredApiUrl, + configuredConnectUrl: configuredConnectUrl + )) + self.currentApiUrl = currentApiUrl + self.currentConnectUrl = currentConnectUrl + self.managerAvailable = managerAvailable + self.onApply = onApply + self.dismiss = dismiss + } + + var body: some View { + VStack(alignment: .leading) { + + Spacer().frame(height: 24) + + HStack { + Text("Change Network API") + .font(themeManager.currentTheme.toolbarTitleFont) + + Spacer() + + #if os(macOS) + Button(action: dismiss) { + Image(systemName: "xmark") + } + .buttonStyle(.plain) + #endif + } + + Spacer().frame(height: 8) + + Text("Choose the network domain used by the app. Leave the API and connect URLs blank to derive them from the domain.") + .font(themeManager.currentTheme.bodyFont) + .foregroundColor(themeManager.currentTheme.textMutedColor) + + Spacer().frame(height: 16) + + Text("Current API: \(currentApiUrl)") + .font(themeManager.currentTheme.secondaryBodyFont) + .foregroundColor(themeManager.currentTheme.textMutedColor) + + Text("Current connect: \(currentConnectUrl)") + .font(themeManager.currentTheme.secondaryBodyFont) + .foregroundColor(themeManager.currentTheme.textMutedColor) + + Spacer().frame(height: 20) + + UrTextField( + text: $viewModel.hostName, + label: "Network domain", + placeholder: viewModel.officialHostName, + supportingText: "Example: ur.network or your custom domain.", + isEnabled: managerAvailable + ) + + Spacer().frame(height: 16) + + UrTextField( + text: $viewModel.apiUrl, + label: "API URL (optional)", + placeholder: viewModel.derivedApiUrl, + supportingText: "Leave blank to derive from the network domain.", + isEnabled: managerAvailable + ) + + Spacer().frame(height: 16) + + UrTextField( + text: $viewModel.connectUrl, + label: "Connect URL (optional)", + placeholder: viewModel.derivedConnectUrl, + supportingText: "Use wss:// for secure custom connect servers.", + isEnabled: managerAvailable + ) + + if viewModel.showInsecureEndpointWarning { + Spacer().frame(height: 12) + Text("Warning: one or more custom endpoints are not using HTTPS/WSS. Traffic may be unencrypted.") + .font(themeManager.currentTheme.secondaryBodyFont) + .foregroundColor(themeManager.currentTheme.dangerColor) + } + + Spacer().frame(height: 20) + + UrButton( + text: "Use default network", + action: { + let (hostName, apiUrl, connectUrl) = viewModel.resetToDefault() + apply(hostName: hostName, apiUrl: apiUrl, connectUrl: connectUrl) + }, + style: .outlineSecondary, + enabled: managerAvailable + ) + + Spacer().frame(height: 12) + + UrButton( + text: "Apply Network API", + action: { + apply(hostName: viewModel.hostName, apiUrl: viewModel.apiUrl, connectUrl: viewModel.connectUrl) + }, + enabled: managerAvailable + ) + + if !managerAvailable { + Spacer().frame(height: 12) + UrInlineErrorText(message: "Network manager unavailable") + } + + if let statusMessage = viewModel.statusMessage { + Spacer().frame(height: 12) + Text(statusMessage) + .font(themeManager.currentTheme.secondaryBodyFont) + .foregroundColor(themeManager.currentTheme.textMutedColor) + } + + Spacer() + } + .padding(.horizontal, 12) + .padding(.vertical, 24) + } + + private func apply(hostName: String, apiUrl: String, connectUrl: String) { + let normalizedHost = viewModel.normalizedHostName + guard !normalizedHost.isEmpty else { + viewModel.setStatusMessage("Enter a network domain") + return + } + + let normalizedApiUrl = NetworkServerUtils.normalizeApiUrl(apiUrl) + let normalizedConnectUrl = NetworkServerUtils.normalizeConnectUrl(connectUrl) + + let applied = onApply(normalizedHost, normalizedApiUrl, normalizedConnectUrl) + if applied { + viewModel.setStatusMessage("Switched to \(normalizedHost)") + dismiss() + } else { + viewModel.setStatusMessage("Network manager unavailable") + } + } +} diff --git a/app/network/Shared/Views/NetworkServerSheet/NetworkServerSheetViewModel.swift b/app/network/Shared/Views/NetworkServerSheet/NetworkServerSheetViewModel.swift new file mode 100644 index 00000000..97affaec --- /dev/null +++ b/app/network/Shared/Views/NetworkServerSheet/NetworkServerSheetViewModel.swift @@ -0,0 +1,96 @@ +// +// NetworkServerSheetViewModel.swift +// URnetwork +// +// ViewModel for `NetworkServerSheet`, the "Change Network API" sheet that +// lets a user point the app at a different network (self-hosted or +// alternate) instead of the official ur.network. Ported from Android's +// `NetworkServerSelector.kt`. +// + +import Foundation +import SwiftUI + +extension NetworkServerSheet { + + @MainActor + class ViewModel: ObservableObject { + + @Published var hostName: String + @Published var apiUrl: String + @Published var connectUrl: String + @Published private(set) var statusMessage: String? + + let officialHostName: String + let officialMigrationHostName: String + let envName: String + + init( + initialHostName: String, + configuredApiUrl: String, + configuredConnectUrl: String, + officialHostName: String = NetworkConfig.officialHostName, + officialMigrationHostName: String = NetworkConfig.officialMigrationHostName, + envName: String = NetworkConfig.officialEnvName + ) { + let normalizedInitial = NetworkServerUtils.normalizeNetworkHost(initialHostName) + self.hostName = normalizedInitial.isEmpty ? officialHostName : normalizedInitial + self.apiUrl = configuredApiUrl + self.connectUrl = configuredConnectUrl + self.officialHostName = officialHostName + self.officialMigrationHostName = officialMigrationHostName + self.envName = envName + } + + var normalizedHostName: String { + NetworkServerUtils.normalizeNetworkHost(hostName) + } + + var isOfficialHost: Bool { + let normalizedOfficial = NetworkServerUtils.normalizeNetworkHost(officialHostName) + return normalizedHostName == normalizedOfficial + } + + var activeMigrationHostName: String { + isOfficialHost ? officialMigrationHostName : "" + } + + var derivedApiUrl: String { + let host = normalizedHostName.isEmpty ? officialHostName : normalizedHostName + return NetworkServerUtils.derivedServiceUrl( + hostName: host, + migrationHostName: activeMigrationHostName, + envName: envName, + scheme: "https", + service: "api" + ) + } + + var derivedConnectUrl: String { + let host = normalizedHostName.isEmpty ? officialHostName : normalizedHostName + return NetworkServerUtils.derivedServiceUrl( + hostName: host, + migrationHostName: activeMigrationHostName, + envName: envName, + scheme: "wss", + service: "connect" + ) + } + + var showInsecureEndpointWarning: Bool { + (!apiUrl.isEmpty && NetworkServerUtils.hasInsecureScheme(apiUrl, secureScheme: "https")) || + (!connectUrl.isEmpty && NetworkServerUtils.hasInsecureScheme(connectUrl, secureScheme: "wss")) + } + + func resetToDefault() -> (hostName: String, apiUrl: String, connectUrl: String) { + hostName = officialHostName + apiUrl = "" + connectUrl = "" + return (officialHostName, "", "") + } + + func setStatusMessage(_ message: String?) { + statusMessage = message + } + } +} From 0cd43dfc9affa0ec7e95755de0fb8a964155e4db Mon Sep 17 00:00:00 2001 From: Ryanmello07 <67509637+Ryanmello07@users.noreply.github.com> Date: Sat, 11 Jul 2026 08:23:58 -0700 Subject: [PATCH 015/109] ios: fix LocalizedStringKey conversion in NetworkServerSheet UrTextField.placeholder is LocalizedStringKey, which only auto-converts from string literals, not String variables. Wrap the three computed placeholder values (officialHostName, derivedApiUrl, derivedConnectUrl) explicitly. --- .../Views/NetworkServerSheet/NetworkServerSheet.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/network/Shared/Views/NetworkServerSheet/NetworkServerSheet.swift b/app/network/Shared/Views/NetworkServerSheet/NetworkServerSheet.swift index e6295a3f..9a10aefd 100644 --- a/app/network/Shared/Views/NetworkServerSheet/NetworkServerSheet.swift +++ b/app/network/Shared/Views/NetworkServerSheet/NetworkServerSheet.swift @@ -85,7 +85,7 @@ struct NetworkServerSheet: View { UrTextField( text: $viewModel.hostName, label: "Network domain", - placeholder: viewModel.officialHostName, + placeholder: LocalizedStringKey(viewModel.officialHostName), supportingText: "Example: ur.network or your custom domain.", isEnabled: managerAvailable ) @@ -95,7 +95,7 @@ struct NetworkServerSheet: View { UrTextField( text: $viewModel.apiUrl, label: "API URL (optional)", - placeholder: viewModel.derivedApiUrl, + placeholder: LocalizedStringKey(viewModel.derivedApiUrl), supportingText: "Leave blank to derive from the network domain.", isEnabled: managerAvailable ) @@ -105,7 +105,7 @@ struct NetworkServerSheet: View { UrTextField( text: $viewModel.connectUrl, label: "Connect URL (optional)", - placeholder: viewModel.derivedConnectUrl, + placeholder: LocalizedStringKey(viewModel.derivedConnectUrl), supportingText: "Use wss:// for secure custom connect servers.", isEnabled: managerAvailable ) From 4f7e43fbc77a008dfb75593a8ba01d7a1d65b8dc Mon Sep 17 00:00:00 2001 From: Ryanmello07 <67509637+Ryanmello07@users.noreply.github.com> Date: Sat, 11 Jul 2026 08:41:05 -0700 Subject: [PATCH 016/109] ci: build and release a macOS app archive alongside iOS The SDK build (make build_apple) already produces a macOS xcframework slice (macos/arm64, macos/amd64), and the app project already declares macosx in SUPPORTED_PLATFORMS, but the workflow never built or published a macOS app. Add an unsigned macOS archive + zip package and attach it to the prerelease alongside the iOS IPA. --- .github/workflows/beta-build.yml | 36 +++++++++++++++++++++++++++++--- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/.github/workflows/beta-build.yml b/.github/workflows/beta-build.yml index d71bb720..19db1bee 100644 --- a/.github/workflows/beta-build.yml +++ b/.github/workflows/beta-build.yml @@ -1,4 +1,4 @@ -name: Beta Build — iOS +name: Beta Build — iOS & macOS on: push: @@ -14,7 +14,7 @@ env: jobs: build-ios: - name: Build unsigned iOS app + name: Build unsigned iOS and macOS apps runs-on: macos-latest steps: - name: Check out iOS repo @@ -89,6 +89,35 @@ jobs: cp -R "$APP" Payload/ zip -r URnetwork-beta.ipa Payload + - name: Archive unsigned macOS app + working-directory: urnetwork-ios/app + run: | + set -o pipefail + xcodebuild archive \ + -project app.xcodeproj \ + -scheme URnetwork \ + -configuration Release \ + -destination 'generic/platform=macOS' \ + -archivePath build/URnetwork-macOS.xcarchive \ + -derivedDataPath DerivedData \ + CURRENT_PROJECT_VERSION=${{ github.run_number }} \ + MARKETING_VERSION=beta \ + CODE_SIGNING_ALLOWED=NO \ + CODE_SIGNING_REQUIRED=NO \ + CODE_SIGN_IDENTITY="" \ + PROVISIONING_PROFILE_SPECIFIER="" \ + 2>&1 | tee xcodebuild-archive-macos.log + + - name: Package unsigned macOS app + working-directory: urnetwork-ios/app + run: | + APP=$(find build/URnetwork-macOS.xcarchive -name 'URnetwork.app' -type d | head -n 1) + if [ -z "$APP" ]; then + echo "Could not find URnetwork.app in macOS archive" + exit 1 + fi + ditto -c -k --sequesterRsrc --keepParent "$APP" URnetwork-beta-macOS.zip + - name: Create prerelease env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -100,4 +129,5 @@ jobs: --prerelease \ --title "URnetwork iOS beta custom server ${{ github.run_number }}" \ --generate-notes \ - urnetwork-ios/app/URnetwork-beta.ipa + urnetwork-ios/app/URnetwork-beta.ipa \ + urnetwork-ios/app/URnetwork-beta-macOS.zip From 1c0b75c33a845465c2995b3a7f8e6c9fbc0788b0 Mon Sep 17 00:00:00 2001 From: Ryanmello07 <67509637+Ryanmello07@users.noreply.github.com> Date: Sat, 11 Jul 2026 10:20:10 -0700 Subject: [PATCH 017/109] ios: remove Solana sign-in button from macOS login screen The macOS SSOButtons variant showed the Solana sign-in button unconditionally, unlike the iOS variant which only shows it when a Solana wallet app is installed. Remove it from macOS entirely; wallet address entry elsewhere (post-login wallet management) is unaffected. --- .../LoginInitial/LoginInitialView.swift | 31 ------------------- 1 file changed, 31 deletions(-) diff --git a/app/network/Authenticate/LoginInitial/LoginInitialView.swift b/app/network/Authenticate/LoginInitial/LoginInitialView.swift index 1da095bf..50268d4b 100644 --- a/app/network/Authenticate/LoginInitial/LoginInitialView.swift +++ b/app/network/Authenticate/LoginInitial/LoginInitialView.swift @@ -932,37 +932,6 @@ private struct SSOButtons: View { } .frame(maxWidth: .infinity) - Spacer() - .frame(height: 8) - - HStack { - Button(action: presentSignInWithSolanaSheet) { - HStack { - Image("solana.gradient.logo") - .resizable() - .scaledToFit() - .frame(width: 16) - Spacer().frame(width: 8) - Text("Sign in with Solana") - .foregroundColor(themeManager.currentTheme.inverseTextColor) - .font( - Font.system(size: 12, weight: .medium) - ) - } - } - .frame(height: 30) - .frame(maxWidth: .infinity) - .background(.white) - .cornerRadius(6) - .buttonStyle(.plain) - .disabled(isLoginActionInFlight) - - Spacer().frame(width: 8) - - Spacer().frame(maxWidth: .infinity) - } - .frame(maxWidth: .infinity) - } } From 00525d46b001351758ac6874de4070204115b1c8 Mon Sep 17 00:00:00 2001 From: Ryanmello07 <67509637+Ryanmello07@users.noreply.github.com> Date: Sat, 11 Jul 2026 12:44:24 -0700 Subject: [PATCH 018/109] fix(settings): use renamed deviceDescription field on NetworkClientInfo Same NetworkClientInfo.Description -> DeviceDescription rename as Android's SettingsViewModel fix. This one silently compiled (Swift's NSObject.description is a valid read-only String getter), but returned the object's default description instead of the actual device description text in the Settings device list. --- app/network/Main/Account/Settings/SettingsViewModel.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/network/Main/Account/Settings/SettingsViewModel.swift b/app/network/Main/Account/Settings/SettingsViewModel.swift index c6b87e06..cf9f5151 100644 --- a/app/network/Main/Account/Settings/SettingsViewModel.swift +++ b/app/network/Main/Account/Settings/SettingsViewModel.swift @@ -113,7 +113,7 @@ extension SettingsView { } if info.clientId?.idStr == clientId.idStr { self.deviceId = info.deviceId - self.deviceName = !info.deviceName.isEmpty ? info.deviceName : info.description + self.deviceName = !info.deviceName.isEmpty ? info.deviceName : info.deviceDescription self.deviceSpec = info.deviceSpec break } From 87b74f43ffcfa437d3c87d124f2b6ca350d662be Mon Sep 17 00:00:00 2001 From: Ryanmello07 <67509637+Ryanmello07@users.noreply.github.com> Date: Sat, 11 Jul 2026 13:03:00 -0700 Subject: [PATCH 019/109] fix(ios): address Copilot review feedback on apple#327 - apply(hostName:apiUrl:connectUrl:) now normalizes its own hostName parameter instead of reading viewModel.normalizedHostName, so the method behaves correctly regardless of view-model state (previously harmless only because every call site happened to pass a value already in sync with the view model). - Fix a misleading error message shown when fetching the Solana wallet-auth challenge from the server fails, before any wallet interaction happens - it previously said 'error connecting to your wallet'. --- .../Authenticate/LoginInitial/LoginInitialViewModel.swift | 2 +- .../Shared/Views/NetworkServerSheet/NetworkServerSheet.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/network/Authenticate/LoginInitial/LoginInitialViewModel.swift b/app/network/Authenticate/LoginInitial/LoginInitialViewModel.swift index c6618224..eaa86fe5 100644 --- a/app/network/Authenticate/LoginInitial/LoginInitialViewModel.swift +++ b/app/network/Authenticate/LoginInitial/LoginInitialViewModel.swift @@ -131,7 +131,7 @@ extension LoginInitialView { return true } catch { solanaChallengeMessage = nil - setLoginErrorMessage("There was an error connecting to your wallet") + setLoginErrorMessage("There was an error connecting to the network") return false } } diff --git a/app/network/Shared/Views/NetworkServerSheet/NetworkServerSheet.swift b/app/network/Shared/Views/NetworkServerSheet/NetworkServerSheet.swift index 9a10aefd..ab66a000 100644 --- a/app/network/Shared/Views/NetworkServerSheet/NetworkServerSheet.swift +++ b/app/network/Shared/Views/NetworkServerSheet/NetworkServerSheet.swift @@ -158,7 +158,7 @@ struct NetworkServerSheet: View { } private func apply(hostName: String, apiUrl: String, connectUrl: String) { - let normalizedHost = viewModel.normalizedHostName + let normalizedHost = NetworkServerUtils.normalizeNetworkHost(hostName) guard !normalizedHost.isEmpty else { viewModel.setStatusMessage("Enter a network domain") return From 035420190f1b077c551977f4873466dba55e8bc5 Mon Sep 17 00:00:00 2001 From: Ryanmello07 <67509637+Ryanmello07@users.noreply.github.com> Date: Sat, 11 Jul 2026 13:19:35 -0700 Subject: [PATCH 020/109] fix(ios): remove unused URnetworkSdk import in NetworkServerSheet.swift NetworkServerSheet.swift doesn't reference any Sdk* types directly - it takes plain String/closure parameters and delegates the actual SDK calls to DeviceManager. --- .../Shared/Views/NetworkServerSheet/NetworkServerSheet.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/app/network/Shared/Views/NetworkServerSheet/NetworkServerSheet.swift b/app/network/Shared/Views/NetworkServerSheet/NetworkServerSheet.swift index ab66a000..88943de4 100644 --- a/app/network/Shared/Views/NetworkServerSheet/NetworkServerSheet.swift +++ b/app/network/Shared/Views/NetworkServerSheet/NetworkServerSheet.swift @@ -10,7 +10,6 @@ // import SwiftUI -import URnetworkSdk struct NetworkServerSheet: View { From de344a04b32fb29cd4d83f6663e1346b40e90ec6 Mon Sep 17 00:00:00 2001 From: Ryanmello07 <67509637+Ryanmello07@users.noreply.github.com> Date: Sat, 11 Jul 2026 13:21:29 -0700 Subject: [PATCH 021/109] fix(ios): strip port before deriving api./connect. subdomains Same bug as Android's NetworkServerSelector (this file is an explicit Swift port of that normalization logic, per its own doc comment): normalizeNetworkHost preserved a trailing :port, which derivedServiceUrl then prepended a service prefix to, producing invalid hosts like 'api.192.168.1.5:8080'. Not flagged by Copilot on this PR, but the identical root cause was flagged on the Android PR and applies here too. --- app/network/Shared/Utilities/NetworkServerUtils.swift | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/app/network/Shared/Utilities/NetworkServerUtils.swift b/app/network/Shared/Utilities/NetworkServerUtils.swift index 58ade3ff..0face536 100644 --- a/app/network/Shared/Utilities/NetworkServerUtils.swift +++ b/app/network/Shared/Utilities/NetworkServerUtils.swift @@ -25,6 +25,14 @@ enum NetworkServerUtils { if let atRange = value.range(of: "@") { value = String(value[atRange.upperBound...]) } + // Strip a trailing :port - the network domain field is a bare host + // used to derive api./connect.; a custom port belongs in + // the explicit API/connect URL overrides, not baked into the derived + // subdomain (which would otherwise produce invalid hosts like + // "api.192.168.1.5:8080"). + if let colonRange = value.range(of: ":", options: .backwards) { + value = String(value[.. Date: Sun, 12 Jul 2026 01:05:34 -0500 Subject: [PATCH 022/109] ad and tracker blocker --- NEXTSTEPS2.md | 184 + app/URnetwork-Info.plist | 5 + app/extension/PacketTunnelProvider.swift | 66 +- .../Images/URnode1.imageset/urnode_01.svg | 1462 - .../Images/URnode2.imageset/Contents.json | 24 - .../Images/URnode2.imageset/urnode_02.svg | 1389 - .../Images/URnode3.imageset/Contents.json | 24 - .../Images/URnode3.imageset/urnode_03.svg | 1540 - .../Contents.json | 5 +- .../ReferralFrog.imageset/ReferralFrog.png | Bin 0 -> 29161 bytes .../CreateNetworkVerifyView.swift | 2 +- .../LoginInitial/LoginInitialView.swift | 204 +- .../LoginInitial/LoginInitialViewModel.swift | 29 +- .../ResetPassword/ResetPasswordView.swift | 2 +- app/network/ContentView.swift | 2 +- .../Main/Account/AccountRootView.swift | 54 +- .../Main/Account/Profile/ProfileView.swift | 6 +- .../Account/Settings/SettingsForm-iOS.swift | 82 +- .../Account/Settings/SettingsForm-macOS.swift | 92 +- .../Main/Account/Settings/SettingsView.swift | 25 +- .../URNodeCarousel/URNodeCarousel.swift | 90 - .../Main/Account/Wallet/WalletView.swift | 43 +- .../ConnectWalletSheetView.swift | 36 +- .../EnterWalletAddressView.swift | 6 +- .../EnterWalletAddressViewModel.swift | 32 +- .../Main/Account/Wallets/WalletsView.swift | 4 +- .../ConnectActions/ConnectActions.swift | 7 +- .../ConnectStatusIndicator.swift | 4 +- .../Main/Connect/ConnectStatsSections.swift | 21 +- .../Main/Connect/ConnectView-iOS.swift | 8 +- .../Main/Connect/ConnectView-macOS.swift | 9 + .../ProviderListItemView.swift | 1 + .../Main/Connect/SelectedProvider.swift | 3 +- app/network/Main/Feedback/FeedbackView.swift | 4 +- app/network/Main/MainView.swift | 18 + app/network/Shared/Models/WalletChain.swift | 3 + .../Shared/Resources/Localizable.xcstrings | 40162 +++++++++++++--- .../Services/UrApiService/UrApiService.swift | 2 +- .../AccountPreferencesViewModel.swift | 2 +- .../AppStoreSubscriptionManager.swift | 73 +- .../ConnectWalletProviderViewModel.swift | 77 +- .../Shared/ViewModels/DeviceManager.swift | 107 +- .../SubscriptionBalanceViewModel.swift | 105 +- app/network/Shared/Views/AccountMenu.swift | 33 + .../Introduction/IntroductionUsageBar.swift | 3 +- .../Views/Introduction/IntroductionView.swift | 4 +- .../Introduction/ParticipateReferView.swift | 7 +- .../RedeemBalanceCodeSheet.swift | 20 +- .../ReferralShareLink/ReferralShareLink.swift | 14 +- .../Shared/Views/SolanaSignMessageSheet.swift | 7 +- .../Views/Stats/ContractDetailsView.swift | 4 +- .../Shared/Views/Stats/SplitRulesView.swift | 4 +- .../Views/UpgradeSubscriptionSheet.swift | 54 +- app/network/Shared/Views/UsageBar.swift | 73 +- app/network/Shared/Views/WalletIcon.swift | 45 +- .../Navigation/MainNavigationSplitView.swift | 1 + 56 files changed, 34378 insertions(+), 11905 deletions(-) create mode 100644 NEXTSTEPS2.md delete mode 100644 app/network/Assets.xcassets/Images/URnode1.imageset/urnode_01.svg delete mode 100644 app/network/Assets.xcassets/Images/URnode2.imageset/Contents.json delete mode 100644 app/network/Assets.xcassets/Images/URnode2.imageset/urnode_02.svg delete mode 100644 app/network/Assets.xcassets/Images/URnode3.imageset/Contents.json delete mode 100644 app/network/Assets.xcassets/Images/URnode3.imageset/urnode_03.svg rename app/network/Assets.xcassets/{Images/URnode1.imageset => ReferralFrog.imageset}/Contents.json (72%) create mode 100644 app/network/Assets.xcassets/ReferralFrog.imageset/ReferralFrog.png delete mode 100644 app/network/Main/Account/URNodeCarousel/URNodeCarousel.swift diff --git a/NEXTSTEPS2.md b/NEXTSTEPS2.md new file mode 100644 index 00000000..30eaff78 --- /dev/null +++ b/NEXTSTEPS2.md @@ -0,0 +1,184 @@ +# NEXTSTEPS2 — configuration to finish the Bittensor / wallet sign in +#### (and, at the end, the Stripe checkout config) + +Follow-up to [BITTENSOR.md](BITTENSOR.md). The code is built and compiling across +`server`, `sdk`, `android`, `apple`, and `mmm/ur.io`. What remains is **configuration**: +one WalletConnect project id in three config files, three deploys in order, and a +short QA pass over four values that could not be verified from the repo. + +Nothing below blocks a build. Every unset value degrades gracefully (see +[If the project id is left empty](#if-the-project-id-is-left-empty)). + +--- + +## 1. WalletConnect Cloud project id + +One project id is shared by all clients. + +**Create it:** a project at the WalletConnect / Reown Cloud dashboard → +copy the **Project ID**. In the project settings: + +- Name: `URnetwork`, URL: `https://ur.io`, icon: the ur.io favicon + (this metadata is what the wallet shows on the approval screen — the clients + already send the same values, so keep them consistent). +- Allowed domains / origins: `ur.io` (add `localhost` for local dev of the web ui). + +**Then set it in three places.** All three are placeholders today. + +### Android — `android/app/local.properties` + +```properties +WALLETCONNECT_PROJECT_ID= +``` + +Surfaces as `BuildConfig.WALLETCONNECT_PROJECT_ID` (wired in `app/app/build.gradle` +next to `BUNDLER_RPC_URL`), and is passed to the bridge as `wc_project_id` by +`ui/login/LoginUtils.kt`. Gradle prints a warning when it is unset. + +> `local.properties` is **not** in version control — it must also be set on the +> build machine / CI, the same way `BUNDLER_RPC_URL` is. + +### Apple — `apple/app/URnetwork-Info.plist` + +```xml +URWalletConnectProjectId + +``` + +Read by `ConnectWalletProviderViewModel.openBittensorSignIn` and passed to the +bridge as `wc_project_id`. + +### Web + bridge — `mmm/ur.io` build environment + +```sh +PUBLIC_WALLETCONNECT_PROJECT_ID= +``` + +**`PUBLIC_`, not `VITE_`** — production is built by Astro, whose `envPrefix` is +`PUBLIC_`, so a `VITE_*` variable never reaches the bundle (this is also why the +other client config there — `PUBLIC_SOLANA_MERCHANT`, `PUBLIC_SOLANA_USDC_MINT` — +uses that prefix). A WalletConnect project id is a public client identifier meant +to ship in the browser bundle, so `PUBLIC_` is correct on its own merits too. +There is no `.env` file in the repo today — set it in the build environment, or +add `mmm/ur.io/react/.env`. It is read by **both**: + +- `react/src/components/WalletConnect.jsx` — the `/wallet-connect` bridge the + native apps open. Precedence: the `wc_project_id` query param the app sends + **wins**; this env var is the fallback for direct visits. +- `react/src/auth/walletAuth.js` — the web ui's own "Continue with Bittensor" / + "Continue with Solana" buttons in the login dialog. + +The astro site imports these from `react/src` (`@ur` alias), so one value covers +both builds. + +### If the project id is left empty + +No crash and no dead buttons — wallet sign in simply falls back to **injected +wallets only**: + +| surface | with a project id | without | +|---|---|---| +| web login dialog (desktop) | extension, else QR | extension only | +| web login dialog (mobile) | wallet app deep link | *no wallet path* | +| app sign in → bridge (desktop) | extension, else QR | extension only | +| app sign in → bridge (mobile) | wallet app deep link | wallet's in-app browser only | + +Desktop users with an extension (Phantom/Solflare; Bittensor Wallet, SubWallet, +Talisman, polkadot-js) work either way. **Mobile wallet pairing is the thing the +project id buys.** + +--- + +## 2. Deploy order + +1. **`server`** — sr25519 verification + the `TAO` blockchain. Must be live + **before** any client that can produce a Bittensor login, or those logins fail + with `unsupported blockchain`. No DB migration is needed (`blockchain + varchar(32)` already fits `TAO`). +2. **`mmm/ur.io`** — the `/wallet-connect` bridge (bittensor provider + + WalletConnect) and the web login dialog. The shipped apps point at + `https://ur.io/wallet-connect`, so the site must be redeployed before app + releases reach users. +3. **`android` / `apple`** releases, built against the rebuilt SDK artifacts + (already regenerated locally: `.aar`, `.xcframework`, cgo headers). + +--- + +## 3. Verify in QA (four values written from spec, not from this repo) + +Each is isolated as a named constant with a comment. If a wallet rejects the +session proposal or the server rejects a signature, start here. + +| # | value | where | +|---|---|---| +| 1 | Bittensor CAIP chain id `polkadot:2f0555cc76fc2840a25a6ea3b9637146` (first 32 hex of the finney genesis hash) | `react/src/components/WalletConnect.jsx`, `react/src/auth/walletAuth.js` | +| 2 | Solana CAIP chain id `solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp` (mainnet-beta) | `react/src/auth/walletAuth.js` | +| 3 | WalletConnect Solana signature is base58 → decoded to base64 for the server | `react/src/auth/walletAuth.js` (`signInWithSolana`) | +| 4 | **One real-wallet signature end to end.** sr25519 is verified round-trip against `go-schnorrkel`, but not yet cross-implementation. Sign in once with a real wallet (Bittensor Wallet extension, polkadot-js, or a WalletConnect wallet) and confirm the server accepts it. | `server/model/auth_bittensor.go` | + +The injected-wallet path touches neither chain id — that is the common desktop +case, and it exercises the server verification the same way. + +--- + +## 4. Behavior worth knowing before QA + +- **Signed message**: all clients sign the static `"Welcome to URnetwork"`, the + same string the Solana flow already signs. No client sends a nonce; the server + only enforces one *when present* (`handleLoginWallet`). If nonce presence is + ever made mandatory, every wallet client must be updated together. +- **Bittensor wallets are storage only.** They are recorded for future use and + can never be the payout wallet: the apps hide "Make default", and the server + rejects it in `SetPayoutWallet` **and** skips the auto-set-first-wallet-as-default + on wallet creation. A Bittensor *signup* also creates no payout wallet + (`NetworkCreate` only auto-creates one for `SOL`/`MATIC`). +- **Connect wallet** for Bittensor is paste-an-address (ss58, checksum-validated + server side) — no signature, per the decision in BITTENSOR.md. +- **Sign-up parity**: an unlinked wallet routes to create-network with the wallet + on every client, including the web dialog (which swaps the email/password + fields for the connected wallet's address). + +--- + +## 5. Stripe checkout (the non-Apple upgrade path) + +`POST /stripe/create-checkout-session` now takes a `ui_mode`: + +- **`hosted`** (default, back-compatible) → returns `checkout_url` for the system browser. This is what the website already uses. +- **`embedded`** → returns `client_secret` + `publishable_key`, for **inline** checkout: the desktop apps load `https://ur.io/checkout?client_secret=&redirect_link=urnetwork://…` in an embedded webview (WebView2 / WebKitGTK). On completion the page returns `urnetwork://…?status=complete&session_id=…`, and the app polls the subscription balance. + +A single Stripe Checkout Session **cannot** return both a `client_secret` and a `url` — the two are mutually exclusive by `ui_mode` — so the caller picks the mode. Note embedded checkout has **no cancel URL** (the customer never leaves the page), so the app must provide its own close affordance. + +The subscription is granted by the **`invoice.paid` webhook**, never by the client. The client only polls afterwards. + +### Config to fill in + +```yaml +# config//stripe.yml (the config repo — outside android/apple/server) +checkout: + return_url: "https://ur.io/checkout?complete=1&session_id={CHECKOUT_SESSION_ID}" # NEW, required for embedded +``` + +```sh +# mmm/ur.io build environment +PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_live_… # pk_test_… on canary +``` + +Both are optional-but-degrading: without `return_url`, embedded mode refuses cleanly ("Checkout is not configured") instead of stranding someone mid-payment in a webview; without the publishable key the build warns and `/checkout` says the same. A **malformed** key fails the build on purpose. + +--- + +## Appendix — unrelated build config still outstanding + +Not part of Bittensor, but needed to ship the desktop SDK artifacts (the cgo +header already exports `TAO`): + +- **windows/arm64** SDK dll — needs `llvm-mingw` on the build host + (`x86_64` builds today with the installed mingw-w64). +- **linux** SDK `.so` — needs `zig` (`sdk/cgo/Makefile` pins the glibc 2.35 floor + via `zig cc`). + +Until both are installed, `make -C sdk/cgo build_windows build_linux` cannot +produce `URnetworkSdkWindows.zip` / `URnetworkSdkLinux.zip`, and the +`windows` / `linux` apps cannot link the connect-drawer work against the current +SDK. diff --git a/app/URnetwork-Info.plist b/app/URnetwork-Info.plist index 68c336e3..4e01a5be 100644 --- a/app/URnetwork-Info.plist +++ b/app/URnetwork-Info.plist @@ -39,6 +39,11 @@ solflare phantom + + URWalletConnectProjectId + NEVPNConfiguration network.ur.extension diff --git a/app/extension/PacketTunnelProvider.swift b/app/extension/PacketTunnelProvider.swift index e3fc78ed..7104226a 100644 --- a/app/extension/PacketTunnelProvider.swift +++ b/app/extension/PacketTunnelProvider.swift @@ -32,6 +32,9 @@ class PacketTunnelProvider: NEPacketTunnelProvider { private var close: (() -> Void)? private var memoryPressureSource: DispatchSourceMemoryPressure? private var connected: Bool = false + // the dns servers currently applied to the tunnel, used to detect when the + // network settings need a re-apply after a dns settings change + private var appliedTunnelDnsServers: [String] = [] private var stopped: Bool = false private var shouldSaveKeyMaterial: Bool = true private let packetReadLock = NSLock() @@ -271,6 +274,11 @@ class PacketTunnelProvider: NEPacketTunnelProvider { device.setProvideControlMode(localState.getProvideControlMode()) device.setProvideNetworkMode(localState.getProvideNetworkMode()) device.setRouteLocal(localState.getRouteLocal()) + // the device restores the blocker from its own local state at creation, + // but seed it here too: when the app and the extension do not share + // storage, a toggle made before the tunnel ever ran would otherwise be + // lost on the first start + device.setBlockerEnabled(localState.getBlockerEnabled()) device.setCanShowRatingDialog(localState.getCanShowRatingDialog()) device.setCanPromptIntroFunnel(localState.getCanPromptIntroFunnel()) device.setCanRefer(localState.getCanRefer()) @@ -377,6 +385,19 @@ class PacketTunnelProvider: NEPacketTunnelProvider { let defaultLocationChangeSub = device.add(DefaultLocationChangeListener { location in try? localState.setDefaultLocation(location) }) + // re-apply the network settings when the dns settings change the tunnel + // dns servers (e.g. unencrypted local servers set or cleared) + let dnsResolverSettingsChangeSub = device.add(DnsResolverSettingsChangeListener { _ in + DispatchQueue.main.async { + if self.tunnelDnsServers() != self.appliedTunnelDnsServers { + self.setTunnelNetworkSettings(self.networkSettings()) { error in + if let error = error { + self.logger.error("[PacketTunnelProvider]failed to set tunnel network settings: \(error.localizedDescription)") + } + } + } + } + }) let updateWindowStatus = { (windowStatus: SdkWindowStatus?) in var connected = false if let windowStatus = windowStatus { @@ -480,6 +501,7 @@ class PacketTunnelProvider: NEPacketTunnelProvider { provideSecretKeysSub?.close() locationChangeSub?.close() defaultLocationChangeSub?.close() + dnsResolverSettingsChangeSub?.close() windowStatusChangeSub?.close() provideNetworkModeChangeSub?.close() // packetContext.wrappingIncrement(ordering: .relaxed) @@ -536,18 +558,21 @@ class PacketTunnelProvider: NEPacketTunnelProvider { let ipv6Settings = NEIPv6Settings() networkSettings.ipv6Settings = ipv6Settings - // DNS from the SDK device's tunnel DNS setting (default plain 1.1.1.1). Plain - // :53 lets the UpgradeMux intercept and upgrade it; a DoH setting is applied at - // the OS level and bypasses the mux. + // DNS from the SDK device, like the tunnel address: the dns settings' + // unencrypted local servers when set, otherwise the default plain 1.1.1.1 + // (see `tunnelDnsServers`). Plain :53 lets the UpgradeMux intercept and + // upgrade it; a DoH setting is applied at the OS level and bypasses the mux. + let dnsServers = self.tunnelDnsServers() + self.appliedTunnelDnsServers = dnsServers let tunnelDns = self.device?.tunnelDnsSetting() - let dnsServer = (tunnelDns?.server.isEmpty == false) ? tunnelDns!.server : "1.1.1.1" let dnsSettings: NEDNSSettings if let tunnelDns = tunnelDns, tunnelDns.doh, !tunnelDns.dohUrl.isEmpty { + let dnsServer = tunnelDns.server.isEmpty ? "1.1.1.1" : tunnelDns.server let dohSettings = NEDNSOverHTTPSSettings(servers: [dnsServer]) dohSettings.serverURL = URL(string: tunnelDns.dohUrl) dnsSettings = dohSettings } else { - dnsSettings = NEDNSSettings(servers: [dnsServer]) + dnsSettings = NEDNSSettings(servers: dnsServers) } // route every DNS query to the tunnel resolver (empty string matches all // domains). without this the OS may not send :53 queries into the tunnel, so @@ -561,6 +586,24 @@ class PacketTunnelProvider: NEPacketTunnelProvider { return networkSettings } + /// The plain-dns servers for the tunnel, from the sdk device like the tunnel + /// address: the dns settings' unencrypted local servers when set, otherwise + /// the default plain 1.1.1.1 (which the UpgradeMux can intercept and upgrade). + /// The tunnel is ipv4-only (no ipv6 addresses or routes), so only the ipv4 + /// resolvers apply. + func tunnelDnsServers() -> [String] { + var servers: [String] = [] + if let addresses = self.device?.tunnelDnsAddressesIpv4() { + for i in 0.. Bool { let storedByJwt = localState.getByJwt() let storedInstanceId = localState.getInstanceId()?.string() @@ -865,6 +908,19 @@ private class WindowStatusChangeListener: NSObject, SdkWindowStatusChangeListene } } +private class DnsResolverSettingsChangeListener: NSObject, SdkDnsResolverSettingsChangeListenerProtocol { + + private let c: (_ dnsResolverSettings: SdkDnsResolverSettings?) -> Void + + init(c: @escaping (_ dnsResolverSettings: SdkDnsResolverSettings?) -> Void) { + self.c = c + } + + func dnsResolverSettingsChanged(_ dnsResolverSettings: SdkDnsResolverSettings?) { + c(dnsResolverSettings) + } +} + private class ProvideNetworkModeChangeListener: NSObject, SdkProvideNetworkModeChangeListenerProtocol { private let c: (_ mode: String?) -> Void diff --git a/app/network/Assets.xcassets/Images/URnode1.imageset/urnode_01.svg b/app/network/Assets.xcassets/Images/URnode1.imageset/urnode_01.svg deleted file mode 100644 index cc6701ff..00000000 --- a/app/network/Assets.xcassets/Images/URnode1.imageset/urnode_01.svg +++ /dev/null @@ -1,1462 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/network/Assets.xcassets/Images/URnode2.imageset/Contents.json b/app/network/Assets.xcassets/Images/URnode2.imageset/Contents.json deleted file mode 100644 index cc284ee2..00000000 --- a/app/network/Assets.xcassets/Images/URnode2.imageset/Contents.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "images" : [ - { - "filename" : "urnode_02.svg", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - }, - "properties" : { - "preserves-vector-representation" : true - } -} diff --git a/app/network/Assets.xcassets/Images/URnode2.imageset/urnode_02.svg b/app/network/Assets.xcassets/Images/URnode2.imageset/urnode_02.svg deleted file mode 100644 index e5c65264..00000000 --- a/app/network/Assets.xcassets/Images/URnode2.imageset/urnode_02.svg +++ /dev/null @@ -1,1389 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/network/Assets.xcassets/Images/URnode3.imageset/Contents.json b/app/network/Assets.xcassets/Images/URnode3.imageset/Contents.json deleted file mode 100644 index cf0388b3..00000000 --- a/app/network/Assets.xcassets/Images/URnode3.imageset/Contents.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "images" : [ - { - "filename" : "urnode_03.svg", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - }, - "properties" : { - "preserves-vector-representation" : true - } -} diff --git a/app/network/Assets.xcassets/Images/URnode3.imageset/urnode_03.svg b/app/network/Assets.xcassets/Images/URnode3.imageset/urnode_03.svg deleted file mode 100644 index 086eaff3..00000000 --- a/app/network/Assets.xcassets/Images/URnode3.imageset/urnode_03.svg +++ /dev/null @@ -1,1540 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/network/Assets.xcassets/Images/URnode1.imageset/Contents.json b/app/network/Assets.xcassets/ReferralFrog.imageset/Contents.json similarity index 72% rename from app/network/Assets.xcassets/Images/URnode1.imageset/Contents.json rename to app/network/Assets.xcassets/ReferralFrog.imageset/Contents.json index 4e8d881b..cd43443f 100644 --- a/app/network/Assets.xcassets/Images/URnode1.imageset/Contents.json +++ b/app/network/Assets.xcassets/ReferralFrog.imageset/Contents.json @@ -1,7 +1,7 @@ { "images" : [ { - "filename" : "urnode_01.svg", + "filename" : "ReferralFrog.png", "idiom" : "universal", "scale" : "1x" }, @@ -17,8 +17,5 @@ "info" : { "author" : "xcode", "version" : 1 - }, - "properties" : { - "preserves-vector-representation" : true } } diff --git a/app/network/Assets.xcassets/ReferralFrog.imageset/ReferralFrog.png b/app/network/Assets.xcassets/ReferralFrog.imageset/ReferralFrog.png new file mode 100644 index 0000000000000000000000000000000000000000..404b16a8e549f9930fe412a6d09d74351eed1bb4 GIT binary patch literal 29161 zcmV(|K+(U6P)0zI^+wG=B63AHjr<{^e12nmpBXV~+a z*FXQ?aPIqgp6^?Ww%1i*r);jPKkTgOAHMH>pYeX~=XryCn?3elap`qgA+Hn3Utff1 zIm*T&!)^Q}$f(HV?VVIkjZcIy`8IjXEicd2WOP$L9^5G0sJM>34T3$87@LvfUyPp*>}ku?B$qli>#Jvbg8h>e z8##xUOwZt%MJ}Nc$pCwpU?T%wH#J?8NgC^GvOJSG*oO0S_;X$a>2=4FK}h0+?@>rf z>B8TqYC#TSL$h%#{d^+pd>#Hj$?-Y(KEity`2Ny;O~`C6EqwN$KRUtt;Bz5-=R#A8 zq;@nd@}nWE{luQjkKEcB<+mI^etbi|4IbYX0`Mo-&CUJf1M45ff*vifA)z%b(0<+Y zfS>V20yZPU2EF8I({gH%L2F~V_hc7Z%l(Mtils#2xrwx!b@}p%SjPBmJ;L^5v-Zx` zu^|v9Y>-Gzg(k-4B(+#F3R69nyY3rGg5PBjrkqwGf%xI+b*xaQIg(l<$YvVJHm?PL zujB7Iq!Z#J`8bpBzH(MJ`a{{<68UHM7t*DW!D7{#4T%~M4l%2O`@6BLy*Q}xD^b(n9eXH-nE8(2yFT%@^eir6~xUyk3w@WQ0W(yT>NhbC{05?Sdd zvOSr`#$l5nI59S-#lJ_~ZVF0nAF3uk*S(__2S`}J59Ts7Oi*{jPwP&CobER8zD-$A zYr2VJJR!pK7xDKQ{9Ax^Inl-Q;AS4_H6+9P0@IV(+x%wNfP?|pn)GZHi7U^Am|Ems4X{01p?nR z@Z2%1NE0!`XHVwx&p&ZmB5d*k+|BRojb#IGF&d5KZBs!tI++Rby&qhI_2L_B%fh~W z5WYk#MlJN3D9kDWj2kG+zI3CtAxBIyj8HH4ps$Hij0aq>W} zz{VBaAralg2!HQSChGr(HALLaNM;f2WomW{>BAiNbA2WJc>*(^sgEx;ev+p8mG4Qn zpz#2#LIR1x7Y*(vxRz$ECiUq==J59!VE~zH43OaSR~ZA~vtnF#2hVA{kkVb{Zp|3G zsPNLF6c#vYoTK+vAls1}zZOEXK4J;gwe0s%iB z#k2-Y;jp3f=jy9Py4w@!uE77p|MnIUctHbG#0m*qK+a$SmLSb76^!w_2yw=Q<^jT# z#JQyS%o;v#l9{8SfJIp(y&>X>wV`a*5?lk^&U_8eg<#NP;5iAdIY#6!+Yrz=R8~Yc zIEu_|al>J8C=(jn;JVcU6veV!;75=P{Ae%D6b!~b@;yt^mZAFdlV``WwLMVtdH1L$ zzjHR0X{;GF^5N?j0b_a&tz@ZU>sRn9%JaY6Usf?9w*%X7$$gABPb z)H?i|q|jt&?%I&%R~1)L0A>ae7MH%b25n@XPBWX0Qc3EOY;^}B>zmk&iA2ch6UGb7 zC@3Vc=j&qDf{uZ|)5uaIzF0g!hsB9yLJfx?j&U6u-LBM5pOpY>&|Mv=8%g<|crsJL zX$|r30xo7IoKA36xa++jr9wYh@7Pf)Jyf&c^MmjjLcpw{PvVWm1$({}tHVH03oVTVe!$7(huM>na9s|8uFD;sEO7PF^Hyt%rcf{nsUjZIT>yZ zWxCdpL1x-J$xUDl48rq+DYB+|YWivGa|MYga>0Cqey=Ve@;oDt7zo}f7wB<8&ClK| zpY}+er_1#8&Hh=9_y^d~ADm6)+07K@RpjnrUH))2mMLsdjzzp}Y9gyJ3%&kCe&B@% z0pv_%9#-M+erX_grG#=*dE=oe`Pl=R^fHV3BVq~o8WN4a)haA@>T=D8SEbfUqzA$J z-GhlV(^NGyR)nwsj4blmVJyGjC8&h_KAXsojR!Jjtbl91b&|*w1&l#oXOCeO5mUTK ztxNF0uW&3NP{=4Y5O}p_{Z$BJxbxiO>(|Q#dR#CZV9&)1H^AR6(;V_DM)=x;cJ-i{ zOy9Wq+=NWEhL52Bc{?vGcwe5saZprs-EKz{P%z zZWv~MB~x#$L8AI@2b~a?N`%z(EQDO;#Yg{dG5iD|3Y4RB`Mimev zn#R5B;F87}MXfGZed=MUV?$_hnB$Mf6X`S>vf7B{a|?B8GKA~`B)Si-3ULVWf`2it z!v)aQ16iC}+PTRx@-H>NFyGH?A)1UdEu{tdp>|Jtg+bVVwH-G4S#iskAKUtt#slAy zAn=1Pm|LEV<@L~**8!Hj4*0H|#iG0uni;&fBGdadjiSar0hI6YNmK<`u}033F6^-^ z)~^WkQF%)M4ZuA2Ch~%75x*}dnqTMl*Co3ff|Z$1ru87QOlv_QqlR};Q2LuMX&_KU z8o1b$dp_+!oLG!d2Y58X^)0~UeBYIDJN;aS-B><)4?rJ)6>TDm!@>=KA$?}a^6Gtd z&_DADENq3eulIs0l$R`x0WYI!2qAm-YGbYwhqvuLJau~e0UrqDnw;ezlG)i?L=+09j}#ri+F8dpwe+?XcD=@;2<%{VMH=0=FRpKTdT&V%|I{YgD!fZe@ z;lAZK@^YqQH3Vm;zSNbo=TMQu!d8loT<+`Al5q2}gj(T)bpceoeOFgn z5RCkRv7AD*pXAObKsp+zC)g|6?m3-7{53Js*XYseOjw!LfiDb<% z3z)}`uX-X?%`qhk3fY?PwA`9zyoQx~?0fde%H~Ms<~s6EZ+{5YzF0cgg!c>+IW{)H z=M9JI@>^Fm5x8aY7wbWO{tKH@C#n?u{d^;0SdgMzY@~zgf=X?+XKJkhfrY}nbBI5F ze=*1zmVRMc_QQ1W>ms1UA32vvg93(k88_!4%QgMD!a#o|i;yuC2Afq;XxoYoidS^z zmZ<4^|Kfq9zHEY`*mzq>E0#too^Y&P3)e#i&RYnc91AeYZi0Fp^+KmD9*G=So*fZ{gQaLDnlAAsqu2lwGn?j*X)KxyL`9BtG7RD%nkDaXt-OTbmbr0$dzhaNji?CyD3j0|Fv1WofA6XGe z;bJ^xZDOvWk(vJ&{aDLPWEY1)JVh+Lj7?>T@VDvMgpXRU90l?hEYvYe!oW3()n!ou ziZZ{%S|eq?2|v79&hv5JY2YJ&^NQt}dhN#gcnn`z=s#`O0NYs*CbL~WPdqTD?MCGG0m}JV=s7s!pE?>h2#|`*) z3&xlpPMimr7KLgrzh`{GLR3it$xAW_{x`;kEAt1Nf&G?@pi*38(10w1)yGIyib&s2 z1u2w8;7HRz_3fIHx;1gjs2k3E(>OtMM&p)*s1!&240~>gTIb4zC^9W#-FVTHz$Z9w z3vlU&Pu}#*sYC`pe2HxI#!_!L9}3gFCOL2u1llJ-QIIREfa zCjUn*MGZg5pKWBa{O1IJM4pV~2kWgM5OMK&15leDHU)ZV>}o1jp?qd`_xD9q2lJ zO^Z<%>Eb{G>lR&un-VoYx+ zr+cYPV-YqX`AuB>+03K-+)QQR3@Wt*!OFdq{RX~nO$@z5RwrX>fv0}vuiYEd^kVbs zG@tld+eA8szx5Ev4WX%ngcW0IHR=kTMCuAA6>=KYMsoR(1{iRJG&9vqAZQjrdKrPF z2I1zS)MB$*Eci^unQn3t*HL(?$!y{oezDtuh+2*)INIm;U}ayij35eLn{fY5-Bx7M z03vZWV6S)UhkM?jMZ$G6;61%LkRh%&j8&e_n@@25F+i3cW#}_AT~}x{hCiQWfdxU( z3^z;+TW3*+$X$ojE1G>WYgvnrLgk03buqykBkhP9zY;CPuO|3@34s`DMY4xT9(e=M z9&TWhjGe>~y6IX_zChu8z5NZmcR$n5ibJLlQ!TcQrdj3pC@})=&GmN_k~(2zGJhaY zGb8{KVot@k+D!EtekN;)8?#Ug9zbS}0Tkwv=+t*n(TvQ(Dy6~BTCOfE7&f#pC=@;s zvsN?s-9B932A-4Qxd*1|G8~o+Cs~&Be4KX{u;*z<5F@3sH}7j@m21w_CZY!C#P(zT zy?z*eH&qxfe+s{Q+um3Xb0P&0@$jX6RvK{@wwY z;~JSnB6lF#e{^C2WY%uW0In!@;c64ojja$A1*>30DdWny_7Ru%0Or z*ume1MH{d&G60R7VTphe28DnjgtdkW2CV|MuOO`}vV1%M@uOc(H3*Araq)d-wOW|o zIvu?(RS~SFP?%wo1!OSmxc9k4D#AyZy^zVE=G;Lg!vc2jjJ(1*g4GO)%wHQx3-`1y zfm8!ncpS**X}Gj`RS-OHd%TAj=*}w*|UXPy-?P=kUv2F^$KWs*9 zTAo;nSgKmK-ZiEU7W=!VN0bwo>q0(MThK-C7w425fO3R)5kr$5*Wu!Ho5a%k94n> zZ}Ra?TY!Db`)-iQVA%|tr9d>L#LeG$AXc1Sv48wbjy|P#qnpda-HEb&Rsp;I__;!6 zX)*A;=g+|34TmzqCNAT57e_{?P7umBn-qpZHSYaeZOfezcbr9zuV$2#uco;j*lcwx zx#8;OScVAu$PC8M*S7lzCV}c<;Xe!k;GPblp)c!-Y>$2TISld)#p=rnn=xc)@0M*OlErEm=>UZFgFIyK9dJAVAnR#$j8?|(sNt4cFB;q{im~jA^6Rd-v~}apKREcN ztiU$~1kv|H;cgz%cOu9e>(`)~MGa&s)=z0xgEmnPA>reKT92bnq$k~*~4d$W3GP-Wd$xbkRMw1g=PfID)Pe6IR=l0PkVerE5prndMAyPTpX*RKu26zm zy6BN1dU*vN#kihA*Jgs(CRW5$?bO;uVFjfkFVoY)@eu=-Adffw;Z@%ppWqv|0DF#H zbi*hc-u$dqG`DBEsde$p1E3l^Wz&W{bhfXG5jir(O{~ERDEY!QlYakM%}A!n^}~DI zpT+7Ls2NJ5Agw#Mbgh|E`*L|`8y7s?Qbdgl(ws-6Ptqj?fxwJiuu8JY{wuCnPDA?o z&|QCB6!Pmf#;N??Gv?)o_pt$k9kn$r_%JH110{Rk=g%Q8x6T{w?v1TzLT%U0fvF_r znt3}POMr5}vp?v4xzE%Uw8m{?M@3}|QJPlG^+jK*LRxVz=Aj2LCPyoDc z+IUdbP_kyP!q1HNE6~L2O&T>7L^gx2KelR3rg)FeTpPcuS)mI7XtC9ooem+h0<;KS zMX?K2m_}#pJT6S1~8f3G0NaSOLJYqet=$Ityw`2@*- zzuA{whjn;e@c#UuxcS6@iYW5b29gc+JU77KsHxCwcHc~p3f2+L*Ag%4L`KHsg|X~v z2F%#DT=;Hb)}z7BnrO#As$@o1NGZHHq(z*>AmlOX4hs9HnMT*F|7)oyGCw=fRj6ox74`dT*yO{-}q@Hy` z31=4yds}i8mhqc+@%j5MS-=;hWSzxhA*vmV{s(-5N&uFZm*={THkTVW0-PJip3CEAgTt(<(YOXh$};o&+FI@fsR8bRq}>U> z=Jwf;U8HWgrhiAG?TjbG z7ozI*xH7~C#ZlbBe>a~Xl$+nHM<2yYk1A#_o;x#r>#gb{ZJ@UPN8hnuUeW?c$>vNf zNS}U|)jep~u?N>R+Mj{1{L0!$)1GO()35duIfE4^rTa4Y@f+GH&_Bago-*C2qGJYO zds;R1v%dvj{~1NU$hs-y;IofdM-Mh<@5Qs)#znPD$0J=B2|L(41yF-T|JiUC(`X7@ zvuPz@-qQq-Dt|v{x8#jqxCfcRSem1;-2T){<>BU(SOW<*yJ1zm93aeR-+Q;zrkV=i z%*0mTNFkJmo_|DPa1GF42@(iOO6^O%6L~jFi0SS6NX~xgQB7Hm7;7%81kkh`U78lL z`!JXyRM2=nwO9p$JLL<1t=ls3H7JS-QRBa3Q{()43V~XHUMsl?WyYhSqBEOWRFh9% zr4b$d{YbWfY)rg)6D>(Ev4Y(g7PxJSINXa_}=qRcqV zDrj9YIGW18m)TMXM|-gL`1Z}o{wtQ3aXmL0f>v0!Y~c-uS2;aslll9xf8?_HPjAj2=Z!V z81rc)g|_M%wDLL|$O0MxmIFRw9TlNk-OTes!?c%MtHEkceaciZc-_?eT4K>VU1goT z1y~(gupz2?QEmvLl3z8rvyEo4YEt+%R+?=tLOYTo>xi1BE20wY>N+ktbcGZZ{9ckw zEETov438U^uDJAeAkMX$ieL+_eD&N?DRKWB9FgK<>@{=}Do>sp0o5BTGo}v-`pO10 zG@fXS^zRO9q9yY|1Vk%TCW*Q*@kS3S%)^@_y%cu#{N|vhDH*S$KR+W=^EHchVM>?w zP5C0Ck~gr%&_=T^kKB8vvTD};Wu`ImrV&Q|X~{f}X_8ZU%9TqHltk+n++W?SL0|5; z@^Zu-a1r?V-Y=b!&3o6h(Ns&=9;bBhe!uq2Htwm=&f(imX0UFeLFU4~X>BZ_5DrLL z)&f@|8>>AlC)4VAk3x|fJE{GY{Z+q1|A`fKy9BA83lQlD;+hewt8QudUjk@{+liv6 zZS2+m+gq2lp}nt$!Oa=p_+1u?zsW-t)2Qo^!V})&^$8^IsJ$kxBu(7(I!qy@lQqVb z1sKYn$!XFvEnSk2&z5UC?o~);M|{SQOEA+a4Y<5pH}^nMJbzwMY$w)Wim)duZVK!K zFC4|9m;-&hx&wkrBl2+!e=i&`(VHLVZgC7$QWY4bRu8D7oH`h(Z|5KB&bVnS^`>q_ z6Nug@JuO*xs9XldD4E9-8Q|cjObull835DVlp62r#s*czF``geO_Dvd1d!YBvcj5R zv}DXRsU_y4S*eVTGR6&TyWF5?f)?Kz37upF30lER%masO^^T4{N_fje7xm;YOlbr7)}jj_~|Zn(tHtbA!&Q1ZA5T@28wKlv@U=GmjW-E4ewCo6~s-uwKK| z8dwG-pt5%9oUvM2I^EfN%}UsZX#9h)Q%th<`sKs~Bvg5)F4}mY7t|dG`SFW8y5U1X zK?3>yi8NPjC(CB6sZEB`Y$BgSphT@t6drF{@1B=K?LtJh@w<=ULRflbyz`xfkuwNo zT#g`Yz`KUje!E1I#QU) zCm!2^CCk-^&cU^)W-_*b>8HRBaeY^`IQE4?nJKI3tKCAm3FHafbJ1ZLAX5m4KlWk0 zS|p6pqGda&=wxV>&@MjX*(f*6MdpW1Vl3#TF!O#W*z;3`yca-N>H^DHh(q!StvB%f zERjSv+PrtV-4yS6E$->5^pxByJoj^xAz~G~XGIKyxtgkPu_&B;;>;sjE-BlY-E65Q zOnT5M$M%`opFs307-hCQo zJdx8Yefi+Oe6B>}O{}R|lragR@RN-GnVN~u@Qad;{vG8vS^oR?$)3fA4EqDwck#So z4C&vCn17$GZ8>Zw7Ti65&AU5N}%gfq<%ozYrJ!BXKafghqL}KlQ z*xDQ`RJ?{#=8ezJ<;8o(?Eh0i>!8Fq0tP|+fG&oLzptIiUwlqvbz~MZ(di^+2}qaQ z-?a94#!Vbh#dMqXm+M-&S_hWePQd;C*ygGrfDUwj>Q!YWN5eqYu!95;Z#>d&ex&Y7t8JW zSrZ@F6jTTz&EF}_DFh)3=0-GXpQI)9p_YX{Ge(rYy=9wJL$H-0eN%{!X(%qXLtoB{ zBm=X;#=5jxSnyV2bqi>G?$t}350shp;KtIe4b`&%?;uT#-rZcOB9;Ti519WOPhr#_ zDJaGGq-+Eg9ZCWGWP(6=!tr*Ze!9P9l(7Z@rQTQvQ_f5oFG3mAp7JTeSr`>O5{v%F zKE5X8_B%46HRZ=*b!-8!6r6(_p28^g7?DvSk}lxlSpMYmtL)oDxaCbu~zn=feQXa z))B)(3#E9%NSxFocmzqcOgfVyNRuW4!9pY8@fn!*eG8JQxopY$2AZ$ z

^Udp&7&=+^N2u`zY>)OrEwvZ!e+Gq;*eYx-1bcCJ2`n#XI2bDL2V8U|v~A`u3s zFwz3mwYx|(wQVShk!Ue1SK)5SHYB2kETWDgR7^tcC@2qsuOaxQu0V@iP%da;AKz=8 z0J2-hx{Qc+dLLrRIGSW;Ra3EkwK%fuO?wr~?yZlYcOn2Iy1!KL40V4)XlsJMjWg(@}XiLh@ zpH-%4!PqX5gGCwY@s~gE5;=6$UOc@(zC4uA{rk^I8h?&jz>$OVY8e)nre*1;J_8J(se$F?&pV`3 zMh9>W6Y}mN6IudXad)KCzyeaxd*6Sr)L_OL_`TpKUnC<~8*1_DsV&`z4cNH1KW!iv zH?3s7ip{3K20$twIiAa}{@H1XJ2lVit$ds#uKD;sd73OWQ<;Og|Ah~3$sgYj7Z@8$ zd0cWB@$`2r!5UMu#VnyND;r|*n^jsAyffkIHv@s?_*`9{)|~Y+_?}pnpc<~>x}$@+ z0zBd{V{jOfCYU}cp@}X`a>%KhhMAnr$}uTc^iW({U4TvFD!XCIwA5(QxfO}aN_yT{Q)M&Ac(M&`zh9Cu^fOYkbGC|dFG&JlXFunl{TyNJ^Xvzi> z@s4A<0oJg4@E@mu97aBdjzW}&OatWhYy%q<`8698_>72_%CpfB@m8#J4%(bhS~k_h zrsWJm6Yq8=xkd4)DyS3X6!5dwNo9*;Pr)-o4H;1th7shu0{*0whzL=mrd}-A01{!p4BKOh30rJ@xOjR1APQs#bY}xn%$vmjp`hwtH65Gd9M#nWM}vNG#xslBf(Cbi?Dpic6iDdj5I@v-QF~b$RD27i5JHDgO4Ccb}66 z;M4Cn_gON&mnM2dHjEAWf5O&{8j zyB-~=pfiS;>zK|ppqcmm;t@I5?Ms6cx$Z>U>XLRWQB(qO9GFp;qBCoSDO}GFo;<78 zxQBbWd4F9lVh|h~CUFp-r?ZS1JDoK^p^W+s>euh8W?Nd}q)Z8dx3V&L7()mMfUIx# z6i26_XTBSd4${Olatl~WoY_CgIPPMggIbjkyjvzH_CB02Djv(`AX8>8J6xzqJwTth zbnPY7%#~>l!e(CcSl`Pv!LPGGED_gc^m8J%oe^Tr$mg@!!o6Wp@$ZywHq%lcG?;j~ zmcWQ}8<>_9nf2xIW;hvy^@fS^2@<^)e$EJ+U&m*U;U;=yqN{7byd0v^N{vF%ni$+4J~k|Zu(t! zwKN-PxwmX<${owpn*VeSW-ykm)M^1-lO#4Y4e)~SSO}6k7V))?2%dETu3X_&3~kTC zH8rQElrj}N6NsO~ZlF1Y0JW)_%+$^LRDp#U|6U~?(SA|`hTn+wx+AZ;>3w36SxF>@ zFz!!`cBa)w15}9hwr)WI$_wSj_74N-4Z7nyYBn=Y{n;9S3} zmS>uY9VZ-{r)c3917TDZ3Bej|kml#7V&kL;9iltoQzfZzYd z-dM4M4mSTcyW2n|EeAjO}v!NmAGWZ<}Yae9)~us1`8mFQ>(_C zu|?!QHuecNeL199pjiFXxQ@L2;a*m9KXJ552D+Udxu zc}&HkqM~YP@Ph>(ZuMjk8&)3$TdBu4@J~A?#Trn8xu(Lql8O=*9$K(C_R%e65X+P? zunR=2zmZEp!bA1|akCL(|!VDobWJG-#ftpM+8vrTB=YvsEB$79+UHQRE zou$U{o(4edIFo?2e}Y|{Gsr36Wx7 z#U4|)Y`6>Z;?8gQ%@#o1nqFjqCy91uqkGoTXT$Fe_^F* ziw`V@(GoVvI1J!NMzzwW9P#YTnMjozEzF_q-~gI@7nKC_W;an;IMo{zd+y#eUcsa*ue%D436^690QiR=9Lt%mPwUQYoIG!9 z5e55~UOg>a{jt^rR9NUE^QT8OkWzJ}xC zv|1Tk5y-k@BVDE3q3&xhZpxuKl*f^RK6q{-$4-uPh5_-*H(WKPn>w1b9XSIN+4E_wpz5kIlSxc<~c4|blE>jRf@h1-`s_J1LDj&oLPw9H^ zdhl^CkaA+*nZck})^qvC2NNKSxncx`J88IZdEY5$3)4!Z0#7*{WI>?>Se?1HTy)K0 zU0@32Ti^LA)<$<-2C((Q_j?vacyGd;1e8 zmIdv6e&e20eq@>hEo$llIG>m)U{q_!$GSo5E+nUXq-$%)9?HuR0Ka8lOU{g}Tz=wg zPx2)4GXE}WS%|8VfV!&^`*#bW7f?sQ6LWl8V)?frFFfwse=yth9Za|ZRU!p`MJ$6^5zCWFK5ErR{w42OxQ7=y;+33?983>XV zJNIRlws2aTXqVDfziUlnDA}tV%ye2>7F?7^w_B(KZH8~JYJr~C{a!+Fz z0Op8yC=ey+(uh;#QgHHle}wu5K6o0!q9=`&-8}638yoejhpH>FmT6quAh(o`rTAvu z&OOq?6eIlH3sYLM0U)OthLwk_J`9z7Q)*sf>SbSXR#4^YUFQ>JwD4@cvQXo>JHdn~ z1fS_wQ~`*-R*9B{&gV0$0V#u&TV>I)e=WxcJN3(7Hz};xl99247oRT7O!xWOQw^FU znB784%(RW#;>DN)shy5sCl-N)jDX~!3?vV1uC2^^6LQvsv}(_=dWS7h5}ZJv58rvSzDOXvM$--tEl@E_=*+U7MX=&3`e;N^dPBO zm4n%8%><)~8$V4lu)@k6nL=>PAyN|v?HsC+7taMvg}IntJV&4hnbfEzX^F?+>j0MN zl=3O<=RRu^3jfxv7eUj)AReGuJA7W^0GZyO1<_LWp&Nc8R;06^qup6RRE3y~T900_ zAJRG2lI7Pvd0M5k8uH-&--0(&RRuqq^9rt1Atg7Sm9+k48-M)nC*(YKYCbx`Ea4vzztFhgGJ(DVW&69XHag5{H+hd1h*Jp4CLjnx>Vm&dmT9a3j*6f zmhiFt2PMDgur_mW096b10QQMDkmLX4ou89YCsxbwPoH)*T*Fwc+ND?T(mlXL zwUfANIM8WYW{WFwfeWn{lQL`U6xpFB7kPb*p zok;d#14lq3nGK8wM&OJ{DPk~7Rx=lv$IURE8;pz!rpg5qi`Vub4766P_cJ)boRS zX4b*IviSW7xiZc0V z9p<>ESOBTwWj_J&b=E5w)hpLgPbJBm2T|tP5FBrEDcn4V(!6r>=|-GQ?UVmx3RnVh zkXFNjurZIbg&?fcvCG8PEBLWCL8{ifqT)1)WmH9Q4=&Ch^w~nLU}cF{p?ERtSBrh^ z{WHpj62)!&t&(DAYpX~xE8*b8rgPNw6wR#qkkahyn1jezFyi@T)wYxy44S(TaPYy` zMnQh$ft?FsT4*3oq2%W@@QE#Y1MonkHW&yHg zFJ(KK4uvG+2=_CcXmeVnV z4__=Q;bWA|)3?e49frMu!o~CWTZ|NE$Ya|go(C`l7AMNfo0@KDt)AuEg*JQa8)~(5 z4p0#}9$-xxdTE7rFa; z3SqnhHsz%NlnfxZ1f_XNtjE&|A{w9(h(JW%V3TC01~}KKT%42^d$Gu0?i@lNei~xG z&}gUVq3$cQSU{vQaTZFHIjP~-UF7x?Y;e-)px}av9O7cOkWIG7>JQs>YFnfoI}s;- zgh-jZ4O}C61!o>#)3z^7<$OSdQoQ24BD`cik*th{r{<^Z6jBC`EIoT|fh}asX45!| zFwM&Oqp$b6MDRwM=2Xi7Ef}G}_Q*~m=iDIF5E$p^7Dqc9tJjmn&XTgYiJKXFT4~HE z>+o?U*SMu>&e31m+KMZexN^~e^8OfLb5F^GV0zf_1G7t@u5vNC6`l+UOmMSpo2peb z3wgm`oWf=#I_~Xv_O+GXyhKcyX-i(#3_`^$(Nr)&eBJ-DvAjNwY2`MPT~>d*67W*6GFq4>U?|jG`()ED^Sn zjk4p^tkWk{Y#+;T7WxC^vM{BHmgnKWA5-e69RiK|kz)63x|}NvPtc=Z88uI=QI+Qd zanOV2@^Nq%6x2KiuRny?gi?DRd0C@WftkFYS_4Hex527q)-}pk3u#(8ouiUtf?sf1 z6J#Eohma2~G8b3WS~K>;suDHZiEkR&(3R!PT}cXip2kXIe1}~7C2M9yW6#}vE zqR6XiCpVnT=G+TB6tF>Rp-(gTXE|tBo?|3#%@U0+6?QELDntsz!v@tS zYOa}co7QAHxl?0pM)Y~fk263<`vf(Gg@s@{nZws?T-C#&C>*0qN7>}gVPy2L(>w~} zCFGvr|(|`wse35QspT4csaMs>Kese zxuS^WlnGDU{K9nX3T|-UUKCK^3IM6gk4{Fi<&B9S0$}pcC{zEfESGv)WQQH-tXzEM zK@ge|>@+PK3gr_r@f*(jd*^1NUyIO|OAx62%`?a0cZ00KjIBNIDcHDN$spWZ9yY;q zfq!~wgdirv&F1pi#*EI(q0dZ9u0N^SxaMb^JfZ!IjDSCX@kP=`RLNlt`>(l3XYGu% z_1q$8@&V>Ao|l<@bH+g=g)1|{r)qVuLDCagiHonjM4L%6ZPm(l9Gx`ryj!muS+)e*P!3m4e=t4)U33+IkGnjHP}%#i+^8w zqAOhzqX8`a`=TLx8N>tc7&N9l%?vteT?NS3KB*FpBA2h9moF;$2j?2HiJ*(T`WLSm zwR!<5)G-824;n}fUsqSKL^s;aeC{T$z|9151>Aw=9&wkWW3-rpKYg}q!DAOn@Rpe^ zd6gI{bwAL+4?O*HnWrV7l4 z^0;h})CqC_^pPWy4F@W!OHW<$eA~=xO{{0IwWp(G*fNmJ)2xbV_+TJM3flZ73z~9j zVaxw!z)-He0x_&tHrLS(EA{h*``W^>4`e#ZcZ4e`djnb2rmKs@0V=YK04bNfmZ8^G zW#t62>*iLpg>=wZG#U-*T3W@`G zN)$0>4uO)1(~!nKhJ^4~QNhSk1u7LN5k(G%Sf_CA)hL=rokQR_ye>ZcBbh=Zp0M&5 z8U&y`mC4LZD+5TZnoU#8)R6r~y+X7-OO03NX98!|^T&Du-!JkbL#hiUuc(st=D0?8z+#p%&@+18gWwO@a6x`rf&rgtRD`YqL_wbV7)6 z0d(1X9dZZLGATF7&lHdy(=EF<5U=U-g!Dxv=b1r7oeplix(Of)N2!&Qrv@7acKK!tkX^y zTj~MRYF#)BDYNLBzzYS3>>RkuZO0Z^$4}~3Zqd{%kfH@^Ft#?X70&|b`481i!^xIj zfphOl@JvB0HpufT-ZFz-Xza5!iPZwAXW_=joOdKviH)siLWMZ?o{u8zwC!6Es9wvW z_SlbIYcJ>%7H1N9?Z+P1H(;9a_NR8_l2C=hC51pGw;J!JO>5gK92_V2hZncxKrp6` zhl0A8L;<@oM9vUj^e~DWHMb`IULB;H|KFn{dFOGuk&#>uANTpoAD74A+}M`YXsB2# z*Z$xA#W|Vc{5}+}hR-~J>o&%c4v5HXPJg%EmiIj4DqS#^M&UNK*HL>0$^FOAXMsAr zBYi+|`K*!Y1P{g#iS>EF1shd8^UQ|tPur-~%3vMRWI1zT=kUHSIWY&T6uUKa-;2$8 zb{NP{jZR4)06qI3-&izd0k2|5&!=B{ksQd4#PJXAAIiTuG1Sw}w1(YVt1>2uN`hyQ zv*)_<(X**qWbw|b5?ePefo*hI`4f-7Lb-9F@H9}&6cu?np3I)i6l%Uz(-*D5wCNEr znGG>6F=#`Taw`(~!0__WM7j{px#wZeT2RW3YL;qxX%||?8;&vQ`|)oa(L_SUbVuYA ztig7(rdEkZ$1=D)g=!j$Jvt_cE&+>}Ph0eFX9Z`L)=fCI!~-~5CHLpzfRvpsp38cH z1=9m1VC5Qyrx*FIW8HjClKB}o$FXHK3D<}%To8mtBT`|Ab8(C6X9;@ry@k#1Y7TD{ zHj8Q#S?RH_CzI*SI)E)W_f3})VU(TR-`#PWDJ}|2lOprR7`x1*bj=O}i;tiyBh7NX ztrk$FaOPVypeZ3u$=zHO6^f+Txp1^v8Q2cc>Ug)y39ey*!833?#N<|vCjc)uwpkc7ggB&SIJmO^hu@U z{&N=MY|G*gj*`=oP-cokADscs?5JWSR*<5Vic3PS2_vtQ3Nk_lk^13Nx%MA;96&|& z61EXCf>O*{2o|?EH;JFg_V|8kir{I!RGD6Gt$Ag=N1H>7a$@Dqd^!U^V8*u3==ZDe z!MCBFeGwVEA=1N|>Tce?0$==pJ>Hi^4ujM*KXzX)vYF%KY!vRl5mJm(NB2T$=)E%K- zLcXa(vyt)VRlxAO!>A)X>jOhMla|)YPxJ6ru`IXAC_Bg4mF~Ex?zN1b$H}es?9-Q9FG8Y~8HlS=`^}uiq=P*+^6F#i_cUOv=yW82!8MUqg@^ zlrc=DV_k-C`OxN2dby3RKMNtgbpmLdf=vuy?x2s$EB$`H3!5pm-TMMeILg6{L5nap zUS|h~4xW>0d6+aSXyaI|MGBK<(Xm>M$lzhSNAG20uoNE-CW1~SyW1H;7N6yfZ_g{6=q@s_?}ugP%c#AjT)H_wIbCmX^{8t!mX!_^0cvu zG;n=&ROaR;7TAWc)8}6vTle(^8(+lI=X#BUk9EZ;QZ>P;o;Geh2#in3`QRwMa>8eM zGPj3#`d{UIXUR%gLLtL(7CT8==T_ zNg_-9TTPs+2<^q^P1CrtP7&Mv1W(5Z`fxnnV%_d{3xd4vlTaChSh!?_oJJCPzXse1 zVzo|WR%FB3!`k8(tkS5H6FeTX60=*d!jzlk^04>H%WOT}jD@cQ3ixY>Uh(qTY)dts z^LbtYpZL6JqTf?qX6D;!=2$l%xzl$QAd_%9A>Iy|zRic!joR4!|$ zELs5-eg_mNrghfCU=uS^M>5N!TBGuX8VI>CdmyE#fwPCV0WST)FFp?yK)|ac!9u0!tmrCGDcHOzbKSi5KQWw6sfw|TMyZc@()sM#Ak z{R6P$L??%{Ng+_PbHlPp%+SN1m=@xLH%PEd>^M0CKguk?hf4XpTMJlrrM@MO;LoL0 zGXJ&t6K<=54Vfw`NLqI44ZAg3JlLF=DxXDxLW8 z1Pp9QwB3MijFSi`y~@0WrnF+~T_rRZUU&S52Lr*;6&W#!h0*id_-0SS8Li zfq|_KlMJzEHlh_6+EK z(wdY+8ow50LKoH_nczLzz5)@}n1#1`k?BU0z`^+eNp8p2Y6;(i<8sPtUT%1)R8g7b zm4{blj;LCUWiBm*im(xRYEo3KOm04FR`B76+=E}8%M+!F(FM$Q4Fh+NBOc!&ZdeV`U#W~wQ7eC$h# zo%a!cy!^RKvb@yrCeWffgP7NL;`oO43$k~f0u+k{f)rM!XIlek@QHIWj#8`kvDD09 z3n^XiRL=^QjxA`Oo!H3Nx8H+}s@vdbh>eu*UCeY*wMD zHm69iwhTxSME=)xMEG4}0ZmXMU!Slt8mkpvWBVvUCIUz-8N;Pi6W*L>QDt!wJiIFz z8O08w=y8s9LFV4w>WGrYHhM-)T$yK$itBALuvg@9MAMI^obs1giF4b=u4vgbG8S=Mw@RuB<7h z&TB+0h+q{}wzbq_0Po+|Izgn2pH^pD{?(s8CQ~zYC5h^k0ydDOeLc`xLkuc_9t+Pp zaDB%uNx7z(K1(-{lS+XM()8c8-j&Z}qcU#yI)W2J3M*GL!>eIiH&NJeCI5I|5mg0 z;KZU#$!xZMRWJ_aQE@?Czz{xl$RUuK1)Qx`UDh|dS{63R=5#D<+{BKK)%v~^I!wCT z8zMMNbV?|XHf%=LcgTew^)u~qE(s)2TmmvSTaPhdOS_q@TbDDabf$z>`tV$WKMhk7 z<02T?RN)0xV7+5Wb4NpMtxU+vN@?K%t@a4|c-LaL8wru8*EY5{b9Aue5K8IFUBIe= zkR=6d*B+FZKw@=!4@$~$qOy~c>0)U2Q0@htT#FFuY0K7fPu+I=a2p1d7#X3I_)-O2 zo3t!J9)C8jux?(@5an8J4cCSZn#Bg-)rJOLMx3CJuPGa-<}KZ65uCGV5j;))aFnX% zGe}IF*{N;U6139Cjbu+C7m^4e2^D!{7K}W9n)dO)*h6s*+6hQ8%AMKklSs>qG0i3h zt&%!EwjRYo=LA@5U8#|=Z)!Q;IF6!<|8LssTtDr|@}M?Y8f_=27rwkjg`{fCq^U_P z#xwLhzl;U+Dbdc~!j6z8Wq9i>cMrx)Yc(!lz>=CNtT^a=W^)=BFlVFwdwm-N- z8Z$Lrw8gm@A88M5kF2uCuAeNGYRD7^=|1Pxml*^Nq50B>zbfr%_|4q>XtP<<*o8P& z=40mcw6fe<*go;-L~g!g3)(Ivjm}fbT=vg56t>;m?#XYw`G<8_M1Rni<->bbfEQ*u z^453UA@@FbLbHn^Zb{ylDpQv#^Dzw(1K&sjZA?o^#ctQ2g)+CA3MB`lglW*`PcjqZ6ecO0yB1eymjxGvYkE=Tm^xq~wgVcB%;@7+ zJZ0lWlmj9;+SQAahJqErO_b;bcCm<0oLbS>Zfx|GRorP$si3sx8VCT}ZWYVI2(LL- z9sspZ*C?`-YXZF9MzbbJ8;2U)xlS=ByUhyybjWanQtQLo^AS^ z9N#SU$o~0EyEspYLm_w#E32D|pOBn7sb=&zNp@MWTf;(gdu{&2Sfew+X$sXkSZ#$IB zFf)YnX92^_)C}t<4?nFL%l`D)4QaI-vbMe@Z~DHMs5Kye;p2BdB>&-#&ue>}&H-*O zsOyVL#&7WT3b6;{VJEonKH zouZ_!>5fvhQ*$x7OR!DLoap%jdQ?5|JC>t~^%u@((1WIMvpQFh{;qD#qUmIyJb`hh zLx}PHP-6u(pKuSHP!G4#LsBAB@n=%VmzQ5Qw)ttR~vEufe{-Gz65GX>dprjC@fDtEt4VC)E&l(Jo z`ju%ptkx7W2GqMRS$;mwd``U zg%y3izUUWKq|mVEil;t;@0Y97I=%Q&PUqY`rnLkQs#LKm;Dnvw5oJ&)MfD+44U)vo z4!bT`Vn0ivf!no5myP9u&wlCiFPCRrdH}h<1#i3~E+(gf{M0YKLzWh1q&v*yh8OQs zlh}jM@SFicqpUNW8vvv#IyQ23k|`Qlo`qP(4~!ks)Mfy3l+M;h4;!3Fi4~iBF60oh zi^cIk$Kzn1<%>F_$8!0wZ$Fe{teQ)j7b~L!Z2pU$eVLAdVZWfJesSa$1VQa~Uj>sx zpWgjvcgYkioi0t%&=D}zG4mC0Bb6x+rIHsWKqc~8o_S!G|r6*g4NB5i~@ z257ms9TIYbc#T>mhWWdnUK#mOGCLBs<5O(@j31C}p||^=o#&}=lz|RSTRt)-i(d>E zxdF|*>x+--W(=VD&wbVrnMZwo;uAb6Ak@7MQND>?3%Lak_c&k)t|PSNM=$Bik@g5s zY$6|gU?Tte6K8emRv9_(1TTeDVNwPKBVwF6m!& zgUs>JDQw~oT}~=iqA|+9|M){1tZo6IdI7FKG?S_`dK+LA@39Tv|D2~ERw1X6ct`(E zprOG#df3^MI2KS|Zs7u1l4|VxuP^ZI3KBzEGux$@?i$LW9x60sD;eYK7=9PMuSEE? z^y`3u`PryyVJcITuS1tSQz&}{mA$$PXEwiGDFdk&lpCsm!S+NT?;*JZ$nZDE%_3e?hfjjX@%2T~! zcXdHb_~?e12-KYsRHny~FFa!t9nf6H0EyQdBE#kxgx0ik*iJI(K^Sx?ot`be&W8Qi zJw>JO&fNm+jvLAhKz0Im*%ip|QhwXVs8oQ=P)FZW3d=;|TauNpJb35gBN)~B;}$SumK=~b@QE1! zN;)VC1+qSVPl70cp&4WtF}sxzN2Fu?9kI>^QcyO0l)`Vyf;CFVhMe+mw%e5gmM*nQ zA9n|kE(pc~HmR}6?@0v#2eV0~AY!klsgZFL9N6x0IanCr|mfwC} zM|wKlJdzJ^Dme)rxa2#IosuT3#uh%~{eO2q0>?4Z&$@i*KRsbnx%^OQJ(vma_35l7 zzw)T@3>T7Ie*NGGb@-r1-2C1b9+jyYn@z^@`j=lL-Tpupf&RVY1D}y?*mzRym96P=-x4e9P+=y>R743$iM3p>@P!H~N0Cr4_ zoAtcx`!PX~DrfbSF*1Lu(J>a6T({Glp(kdJhX7_Snr#{n(M{iAG?Ma|vj&1Brf02Y z8-Z9|@&B@r!Q<0X-Q-m3@YGBmG)WQ)=LIvMUh9seH$>`|#d^$2F#D@)pi|qK(%iSO zfvcR0m^*iX_yE&5KX|g;XlQRDJ8d~Xv6lz!{hRL806~lPIS|6;J*^?gYj;|jarEPn zRv@)EpfEs-okOMa$ZoEo+H-Oe3IpC^-%$Nxu}e`|-pq@2c~w0vd?1I8@pbncRkIs* zF|ML;+te_DesSaeE0%8s%yBbba5Y|e)GtelDv&C6-Y~a1xs>Bz*lba= z`FNG}a3N9>2`6%9sonx}eIClpf(wgFbrL_cJSmFiO|{#`C?Z3qYp0lzzh94PcJL!R zg-Nf&b9Lx+zO81Cr-O&tDWzwzu*2)oS$S;T;=s!yw>d&E>2FMA7#44cWP-yXXGCnuXja@@w(Q}1z!wGUPh)uUU2xP}<@|D~YIV?QHJ;!5Z zch=i>jEu@=2qiRRFmY5KFd2*qS{Bp7PIXbYBi}(@^*OGT`1v^Y@OCKn`_A3}z@6u0 z=Mm(u@z`_KWk>PBNAXTK;Z2uI2)IlQ15u^HhDMs3y^%crnq|p{eLZTfj2mc%$PhY- z;!dk!hd$@p#T)57+*v$r8gMLm{crl@X?f3M1FhR%cFA6O@9+MM9DnSr-t_)GbGq1j z7pCRPSNwB5WNNfElwbLYYviK2y3QN^@)KKf|Ea!i>P$P9mtD0_t92|Rzvok@k>A(U z4ZQn94{CD-IrN|YvHd{)2FlZKc7x94kCg^DkrQV|c4#G*^o#v%)hvQfvpP%Au#it5 zd}?*XY`tc~q$vy^{Hx<~dSj^O_^L|}qR91pxPh*+nSSK8&y^m8`^Y8x_{C~E$iTpJl=f3>FF?`{_ zf3tM?r8fd6xCv4`S7HTzIrbF`cy4>BXWqx-odyfTaE!*qQf#Ab$F|W&&{mw|VUbwS zDLwwJDN{2x_nuQryIZ}g>GRy$wsy}l_2M7~qH7zu9pyG22OZH)Eu{sB0WFi9(PwWT zN4`x8Jpe0lYS+w6UA1UMPd}X~D#lYv@?oNlC5$_4>>N1>sRn`EBaQ**jG zUD;60Zh{35x2$~i!CU1Uc>J{lVE2O`0E=)ZR_3EX+~zDZjbyDi)=8W>PxjVANc1m|0^*q#S;>bg_rO&Qn8_J_R$}ve|pw@U3(p>nI4(Z!W>iAKW*qC>?KUq3*CQ@NN))UaVZCB#SS>AXIhfNCY*O+@247kV2}{PdzE-3badbv?+k zu2?%KmjcG^j?Ai z!4FzGvhS8C&ELjF*^3DKYo&4;Gs7cOZ7S&KA1eBO`#F3PbG7HIDef6sGtGv;Q_V)I1G z^8#|Q^NYB^PuV{$7w>6Tox9YJOlt{=apCd5b3p#y_NM1*rm-~Lk{|e^du2j03jF=e z`w}@6`l@oxMHGQvGHpCbU|6G1!p~3Fv{`mF8z?@&&ZOS`UsJ14z!JUi<!(%D-iAdOj=OsN>>YQ0MdN{X>!kRZkTSu0eMg+iRk;PqkD%D~q3)`*92P)5 z-0Z2EnsN+pbjvrh`T9!vCLb#gpSd*@lRKkeEY?FImAPUpOYuakA1&xORk{gIH*dLyZ(GNMIJr zmIp3C4VGd7)?^7m?;cJgVbO{>LB9Ja90%$OIlglLH)i&q6anC46r$Vd2SimfM#0pBmQ8=ZD)oV8E z56>OC%Ng|s3J=@iOzc2PtW!8PJ|KTC(wT#E$3SFYN$}Dh19XGKFhZV zURAY0k3<#CDp+=+XC!z%psc-Ed>!8+=kfTaTtNBw?uGjM=4;_?4J_Q(PIVRF;hBH? z7g|_w1Yo$i53Xmj#;gP@F@uG;H1NEfpoKzhL1X|2zrGjMJ7Y3Ovbs4$q#P+r=M1cc z9!nQuZRL8(p?SChV*V}p-j`i%n~e=T+1-#+U)zQWpU9!PraWzb%Y$h<>XyU|uG$p` zTMp#ra$3>oL;E#@-KK|$r90Jse3wwCW+Lv(S zFTQ#Y79o{A@Pq&KuX^&S^|AV|X8}q1_4!yM>%u2>X&#M*yJC4-7lYQ}{ztoVc5`3= z3%T@(4Xz-y{#lprSH=&Q=cnFqR1c0`oNLR^{raEEr@nMT1FY|Q#$LGsafFVISNG#s z2HyKh{kJ6`b?j9jaypEOs?LU7+z6`--+Q-d&W*^rfk+xGj{~2@2 zOs2~^eXg&>5DLJdrVgc5H_`NgSOcuVrHDuN&e^G<$Io`~(1ij6`Shr(fRtK>xGpPSdFa9k03RCzqg$I#MLBdb z*6+uPofjIsaMFdIJQ|saqshx7BQF}49;SSl`#d14Cej*cZ6Npz+{8=KEFbe&H%86C z*>$=oPdJVJtfF8{9-33SBDw^f)l&|7DpITY4@~=MZYR+MXd!|wGLPz!j)7~6(cX#V zrNu}CI9>;@lk~=V)0E8c8Co3e$))SyU?#DdL8+cM5bNKVzUu5{_A_w!7Sc^x zHf1drc8HV`d~=)7%0RI1eqSe@7Kyrq;JE(zKF(VJ2IKrVbZoiqM^G}@W4(pv{1#14 z&mKChIoE@bbe8g{uvYPcL6MDp)tifU8a?d*s7Qm!CE^FH%)#`oZK2wydsowq6hBbW z4)CyQ8X@8+si^~IS(}@$J{Swm+5l6*$;fB>^KW|~dPu1IS zE%;kf)6sPUvHnKIVS=S=X(~RQn>{Z0ygE1C*iXtk$eq0t#NlY8kPmS`(^H0KDyx}T z1m1HkYFhPF+w81Qf&ZpSgi$CNuCpl4OZ)jaZvnXPiQerH>Kk79oPFA&#bE1>Pd*NC zqalyuX8MEJ2T4)ea(UrZ3v!xG9?-J?`l%C$-bu{~-YZ{dx_IR!)AF=SI{KQ;txPV@ zH<2#~IdJiuT>ay3gQjviM<(z2gP)UbkAdUB21r_oKpuk+zvJ#RutvG|FaC|^9n{T~ zq#++UcGk+-JYrQNd7rDV{@2pOpLx|&6bjZhtuL?2PyP9W5;yCz581+<-;2uN02l%+ z-M&N9iqgheP4<7Ft0$|pXIt{YkKHdYj5=N9z_Xr$3SdW@PC`|=vjJ2Oom>O1F_teq zcv9Z|n{QIC#L~i)y!p4^FE{<=eQmP` z4QxQ&H(6oi8NV>ot)*#cVF9)_ds-+O42OEM2zw2^8A;(%B98hQ9PN@CfI|a8f6c)W zntFP@e&YRrtV!@-s+EnQel|P2`1dyc#^F^R9TUXyw4C^rC>B6vOw}|;cIF@6iz%tX zvOIV)jvdV_%8H6t03&4%ZvzURLh;QdMHCz4TgUDY$f|Kv zeQu|Lwi07d*_k~-=Nc*BAdjrc!?}92Li`*Zlb2eTQoG&K;+h_0Ef&Y*Ugb+7t4-LX z(HvFP{I)xfZycBNe4Muh;Nzmb$;}_R>-00eCghG8=MJig;JO_A?)XHox@<)&r)A$3}9#kMp(wd|ZF$#>P~mc0C05Hj+9g&YPwIIWp{e!-Ax4TH|#?~vZ zoUT~SIyIHW7@~xX()$38Xs-3}N@_emS7}{|P9dkOC?oCI>F?C!8CV9os@Nlc9tjuI z*F|RPSq9dCHP}h3ERBoy87HPywx#; zhS4@Bpf?*GbrrM#YzpPZvA)JZQ!GO>{Zlj%*VXF|;fL#L@;NknsfP3x)yxePi5hSl zlh{&D1%={5*-zZ)5w5!hfoV7@X}mw9deIS3cxn2%`F&U%vWSUvvTA9=B`S{0@kz#d ze)PT{I<|4AJc*AdO#sTrt3RWD=_h*rGmq&zv^;lIviRs%x6_-x|E~TrvG_eOhi|>I zEo(f34;ymX#WNNkK%+LhsoY0?ikHPNh6&Y*A2#LjGuv8YAWUC4nUe9v21>?Xdt_CM zHV602$*=r_*J^Nh^7J|R<3Im`0zYgbS(-r<55GJ>9Pqp&GunVcUwrLl3p!as#7y`pDvbq`6tz6NVXv)jh z^Cn)Gf#70!O%)yo?1AVs}%@8Z<$utM+KII(to1^4-Pd9`=)(KG1r0gufYUT>j@oG>Zg(VtZj4NkYx*VsJxLs(n3XX z4)y!TPIiq;=oIvF4$lT&!#Mz3Sk%3X({lOpq6T&k;q`BS_a|h2(H4HApG$XWmc2lM=)2%?;bp}0*z0U>`5s1@`Fv;>hjq6hP>mfxZ@`?8H!V0P5Z&plF4+$8j7Su?!-Soo2j|g!Gp0{#(EQO&4~)na7h7 z1AHTowZ}GY1>kdiJB~Io=e$K>r!WZ6ON@qjjx)|?T;DVwBy1q(2lZjFb1 z#UIEi)N_C)x%uE=8HHBwicAAjgtZe2vHDy>)im$42qclz;F}q<#7YXu zG>*|jLL!TIG9J;cLTSYv&I8o*8&aDaU<03b25DqPc}lC!Al|@pumA2h{^7TD^ZEFe zSb+cbu|bP)WZ{;rUj8;Bd(CE0^OyP2bW<%3Q8E~qmO+P4CBd@+EH1z!F6z;`1zTBM zlkN2_dGK-Ty<1wGlEeGw6{05n`s%9=D?OWh!4vBv_3NWaR&5v&w|>uPUy&c!$c>_F z7^XE8ftZSplsr=PB*2AD&H)7-4DL`$WCyZF4 zUXN~h<&XW|d82yY%;SOx03UZhymsrOdy3bc9Aww^hud=PRZG@f3XNEtN>D&abS4n< z^t>#=riIV!DS(Wnax)4`47~Q>c|Z4s`{h4vPIRX3i=TU`EOZ*Ojf`P-sxI&Rl^>Ko z$I8RJAAQYl$lRV8>7jn`V=q2rN57Ls)-VQ-QtZT9y_uP;Atn3x{Uak_hDcqJIMx01 z7rL^_c?9ewASM7It$XV{zbF12m$W?^*fSFFJ6)pXE%=jpa1aP-*U|_9~Vpj z_y7cZJJ#a5R>P*&r_N(9g0W-S5|-(iI94fRdSuFEo3o;|03TwFhwHihge2Pe{k$H_^m5-Ef_`%S$D48C|YM35- z4+}k_#fT6*%&JIsI(FEHK;%&T5Jx?O(0c*In`VH0oHv?xK96sC46yqFC4V1NAswHh zty;0>!R+xTw$F}Qntn#}k+*qq7$rfLiMxx`{VC1w0GrQ35D6NDS=mWMt+$2uqg}8o z!!gR!_(v?F+3F~9Qzwf>)+*NRkAM;m6?0(ak=J4XmLqxIm&f_PA*;Iftzwv3XAx6Z zW}nJIB>5z@lSK-TWt1;tQ)(gi6ea4={cLpb)mW{)(05pc*<@6J9_KZ`5$O~l&e*vLZRiwds`i1~k7 zn=gI!gxvg_fBe6%0Pryf?O1hyguqL#IC{_#aAXu`3Xke&N0~yAg)Cy`&Gn+Lwl>Xd zDk*XJF=ys-ng@;tJ*C zWsXnSD7uP&A92jgSbt9!l;&2VRqGI2-;|Dw^B#(^atQ1*3Uz0(gy^r;mhr#zaY0=G zA26xMwcR_|D3poM^ykvF5y-U~^<6UqZQw(a*f46Qd7Q6L9-*G9&d57som_O550AJb zs=jw|4#;0k_U>IkrLPMGO=W{~2K`JOZMkO6f=C>4_I9hO#7-R$#m*w#JW-+uU)yIf z3Ngk0PEGc}QXQC1s^X8%56-L;s_%#%3sY0DIFTLFXaB>!cDsWMs{MRia0?)8S(5yQ zyqdUG$eEP^5VEm`&n)5)$Jfy@VlyR;Yahuoj?CKzV&PAo?I}BHh|Pa#6Q&>O6+3#L z=x@o7|MI($mlG!#$#1{q2LT%OWdG8fyz4)I5-~vC37J;8BM0D%53R`d$To`BXlAOR z`8>O~Ui0jWG-hEG|K0!LtOk!Hnf#;HSbG@=J15@ZJdM=$@fFl3(z=pOA6v6?1bkSI zyz0eQ9DD2AKOh(Kalr)uIrRGwt=&*gv0%!mq8T*Y~bPTeLxPb(lik}Xv;#Myx? zbGIAz{KXkdRok#Acbpvo!DAx}u6NleWIZyUG9z#Bl#&e|_^<^a3GYqo5qYyLYXyt5 z(-$-Y;Nyb3fYZk}?u7Q;R>R7Wip3498>*^{r8E28Z;}X-`xI&$brx0ghLqcxgzUf9*u^GC2A@Jgb|JAo$J(7JO16W3RWX^OawOH#vj`73?r9VRWtFF zE_Po&^tNn1pV!So7suig6=Y?!oFSz2vn;*snpfX^TrTkA+rR~2F&lF8-J|TK^&&n> zjlUTGmXKd@(}>V=SQCSAg0-x%=Uyf)+q{Ew2Fi1Ns*yEZd~C`!ZO#Y=YKKzqhwnNr zV^rRzfeb$VY5UchaO3WOV%yM00#3+ErkgrPSG9OJE)+gav#cr&kDbD%BXoR9h(fzA zuzCg|2Uh`uSV0w%@bTs#vniygi)@SrH_Nxd}XZxBsbeD!)2!+7Aybum7z- z)jq=^(zieOwV%>MC^@L`#)`rN<(599+Fkad(=9CPWz19J>}Zj zl#)jfm!sXxP4JAQb`c#Q5}>Hq)$ literal 0 HcmV?d00001 diff --git a/app/network/Authenticate/CreateNetworkVerify/CreateNetworkVerifyView.swift b/app/network/Authenticate/CreateNetworkVerify/CreateNetworkVerifyView.swift index d3b589b3..acfc21a8 100644 --- a/app/network/Authenticate/CreateNetworkVerify/CreateNetworkVerifyView.swift +++ b/app/network/Authenticate/CreateNetworkVerify/CreateNetworkVerifyView.swift @@ -161,7 +161,7 @@ struct CreateNetworkVerifyView: View { switch result { case .success: - snackbarManager.showSnackbar(message: "Verification code sent.") + snackbarManager.showSnackbar(message: String(localized: "Verification code sent.")) break case .failure(let error): print("error resending OTP \(error.localizedDescription)") diff --git a/app/network/Authenticate/LoginInitial/LoginInitialView.swift b/app/network/Authenticate/LoginInitial/LoginInitialView.swift index 50268d4b..4bdd422b 100644 --- a/app/network/Authenticate/LoginInitial/LoginInitialView.swift +++ b/app/network/Authenticate/LoginInitial/LoginInitialView.swift @@ -81,6 +81,9 @@ struct LoginInitialView: View { } } }, + signInWithBittensor: { + handleBittensorSignIn() + }, presentAuthCodeLoginSheet: { viewModel.setPresentAuthCodeLoginSheet(true) }, @@ -117,6 +120,9 @@ struct LoginInitialView: View { } } }, + signInWithBittensor: { + handleBittensorSignIn() + }, presentAuthCodeLoginSheet: { viewModel.setPresentAuthCodeLoginSheet(true) }, @@ -235,25 +241,33 @@ struct LoginInitialView: View { .handleDeepLink( url, onSignature: { signature in - + guard let pk = connectWalletProviderViewModel.connectedPublicKey else { viewModel.setIsSigningMessage(false) - viewModel.setLoginErrorMessage("There was an error logging in") + viewModel.setLoginErrorMessage(String(localized: "There was an error logging in")) return } - + Task { - await handleSolanaWalletResult( - message: viewModel.solanaChallengeMessage ?? "", - signature: signature, - publicKey: pk - ) + if connectWalletProviderViewModel.connectedWalletProvider == .bittensor { + await handleBittensorWalletResult( + message: connectWalletProviderViewModel.welcomeMessage, + signature: signature, + publicKey: pk + ) + } else { + await handleSolanaWalletResult( + message: connectWalletProviderViewModel.welcomeMessage, + signature: signature, + publicKey: pk + ) + } } - + }, onError: { _ in viewModel.setIsSigningMessage(false) - viewModel.setLoginErrorMessage("There was an error logging in") + viewModel.setLoginErrorMessage(String(localized: "There was an error logging in")) } ) } @@ -312,7 +326,7 @@ struct LoginInitialView: View { case .failure(let error): print("error create args result: \(error.localizedDescription)") viewModel.setIsSigningMessage(false) - viewModel.setLoginErrorMessage("There was an error logging in") + viewModel.setLoginErrorMessage(String(localized: "There was an error logging in")) } } @@ -370,7 +384,7 @@ struct LoginInitialView: View { case .failure(let error): print("error create args result: \(error.localizedDescription)") - viewModel.setLoginErrorMessage("There was an error logging in") + viewModel.setLoginErrorMessage(String(localized: "There was an error logging in")) } } @@ -395,7 +409,7 @@ struct LoginInitialView: View { case .failure(let error): print("error create args result: \(error.localizedDescription)") - viewModel.setLoginErrorMessage("There was an error logging in") + viewModel.setLoginErrorMessage(String(localized: "There was an error logging in")) } } @@ -452,9 +466,9 @@ struct LoginInitialView: View { case .failure(let error): print("auth login error: \(error.localizedDescription)") viewModel.setIsCheckingUserAuth(false) - viewModel.setLoginErrorMessage("There was an error logging in") + viewModel.setLoginErrorMessage(String(localized: "There was an error logging in")) if deviceManager.device != nil { - snackbarManager.showSnackbar(message: "There was an error logging in") + snackbarManager.showSnackbar(message: String(localized: "There was an error logging in")) } break @@ -466,6 +480,52 @@ struct LoginInitialView: View { updateArgs.walletAuth = args.walletAuth return updateArgs } + + /** + * Bittensor sign in: opens the ur.io/wallet-connect bridge; the wallet + * returns through onOpenURL with the ss58 address and sr25519 signature + */ + private func handleBittensorSignIn() { + connectWalletProviderViewModel.openBittensorSignIn( + message: connectWalletProviderViewModel.welcomeMessage + ) + } + + private func handleBittensorWalletResult(message: String, signature: String, publicKey: String) async { + + guard viewModel.beginLoginAction(.bittensor) else { + return + } + + defer { + viewModel.endLoginAction(.bittensor) + } + + let createArgsResult = viewModel.createBittensorAuthLoginArgs(message: message, signature: signature, publicKey: publicKey) + switch createArgsResult { + case .success(let args): + + if deviceManager.device != nil { + + let upgradeArgs = self.createUpgradeSolanaWalletArgs(args) + + let result = await guestUpgradeViewModel.linkGuestToExistingLogin(args: upgradeArgs) + + await self.handleAuthLoginResult(result) + viewModel.setIsSigningMessage(false) + + } else { + let result = await viewModel.authLogin(args: args) + await self.handleAuthLoginResult(result) + viewModel.setIsSigningMessage(false) + } + + case .failure(let error): + print("error create args result: \(error.localizedDescription)") + viewModel.setIsSigningMessage(false) + viewModel.setLoginErrorMessage(String(localized: "There was an error logging in")) + } + } private func createUpgradeExistingSocialArgs(_ args: SdkAuthLoginArgs) -> SdkUpgradeGuestExistingArgs { let updateArgs = SdkUpgradeGuestExistingArgs() @@ -489,7 +549,7 @@ struct LoginInitialView: View { guard let rootViewController = getRootViewController() else { print("no root view controller found") - viewModel.setLoginErrorMessage("There was an error logging in") + viewModel.setLoginErrorMessage(String(localized: "There was an error logging in")) return } @@ -498,7 +558,7 @@ struct LoginInitialView: View { guard let presentingWindow = NSApplication.shared.windows.first else { print("There is no presenting window!") - viewModel.setLoginErrorMessage("There was an error logging in") + viewModel.setLoginErrorMessage(String(localized: "There was an error logging in")) return } @@ -531,12 +591,12 @@ struct LoginInitialView: View { case .failure(let error): print("error create args result: \(error.localizedDescription)") - viewModel.setLoginErrorMessage("There was an error logging in") + viewModel.setLoginErrorMessage(String(localized: "There was an error logging in")) } } catch { print("Error signing in: \(error.localizedDescription)") - viewModel.setLoginErrorMessage("There was an error logging in") + viewModel.setLoginErrorMessage(String(localized: "There was an error logging in")) } @@ -560,8 +620,9 @@ private struct LoginInitialFormView: View { let loginErrorMessage: String? let deviceExists: Bool let presentSignInWithSolanaSheet: () -> Void + let signInWithBittensor: () -> Void let presentAuthCodeLoginSheet: () -> Void - + @Binding var presentGuestNetworkSheet: Bool @State private var presentNetworkServerSheet = false @@ -643,6 +704,7 @@ private struct LoginInitialFormView: View { handleAppleLoginResult: handleAppleLoginResult, handleGoogleSignInButton: handleGoogleSignInButton, presentSignInWithSolanaSheet: presentSignInWithSolanaSheet, + signInWithBittensor: signInWithBittensor, presentAuthCodeLoginSheet: presentAuthCodeLoginSheet, activeLoginAction: activeLoginAction, isLoginActionInFlight: isLoginActionInFlight @@ -730,6 +792,7 @@ private struct SSOButtons: View { let handleAppleLoginResult: (Result) async -> Void let handleGoogleSignInButton: () async -> Void let presentSignInWithSolanaSheet: () -> Void + let signInWithBittensor: () -> Void let presentAuthCodeLoginSheet: () -> Void let activeLoginAction: LoginInitialView.LoginAction? let isLoginActionInFlight: Bool @@ -771,14 +834,56 @@ private struct SSOButtons: View { ) .buttonStyle(.plain) + Spacer() + .frame(height: 24) + + /** + * Bittensor sign in runs through the ur.io/wallet-connect bridge, + * so it does not depend on an installed wallet app + */ + Button(action: signInWithBittensor) { + ZStack { + HStack { + Text("τ") + .foregroundColor(themeManager.currentTheme.inverseTextColor) + .font( + Font.system(size: 19, weight: .bold) + ) + Spacer().frame(width: 8) + Text("Sign in with Bittensor") + .foregroundColor(themeManager.currentTheme.inverseTextColor) + .font( + Font.system(size: 19, weight: .medium) + ) + } + + if activeLoginAction == .bittensor { + HStack { + Spacer() + ProgressView() + .tint(.urBlack) + .controlSize(.small) + .padding(.trailing, 16) + } + } + } + .frame(maxWidth: .infinity) + } + .frame(height: 48) + .background(.white) + .clipShape(Capsule()) + .buttonStyle(.plain) + .opacity(isLoginActionInFlight && activeLoginAction != .bittensor ? 0.3 : 1) + .disabled(isLoginActionInFlight) + // check if either .phantom or solflare are installed if (connectWalletProviderViewModel.isWalletAppInstalled(.phantom) || connectWalletProviderViewModel.isWalletAppInstalled(.solflare) ) { - + Spacer() .frame(height: 24) - + Button(action: presentSignInWithSolanaSheet) { ZStack { HStack { @@ -854,6 +959,7 @@ private struct SSOButtons: View { var handleAppleLoginResult: (Result) async -> Void var handleGoogleSignInButton: () async -> Void var presentSignInWithSolanaSheet: () -> Void + var signInWithBittensor: () -> Void let presentAuthCodeLoginSheet: () -> Void let activeLoginAction: LoginInitialView.LoginAction? let isLoginActionInFlight: Bool @@ -932,6 +1038,60 @@ private struct SSOButtons: View { } .frame(maxWidth: .infinity) + Spacer() + .frame(height: 8) + + HStack { + /** + * Bittensor sign in, placed before Solana. Runs through the + * ur.io/wallet-connect bridge (browser extension wallets) + */ + Button(action: signInWithBittensor) { + HStack { + Text("τ") + .foregroundColor(themeManager.currentTheme.inverseTextColor) + .font( + Font.system(size: 12, weight: .bold) + ) + Spacer().frame(width: 8) + Text("Sign in with Bittensor") + .foregroundColor(themeManager.currentTheme.inverseTextColor) + .font( + Font.system(size: 12, weight: .medium) + ) + } + } + .frame(height: 30) + .frame(maxWidth: .infinity) + .background(.white) + .cornerRadius(6) + .buttonStyle(.plain) + .disabled(isLoginActionInFlight) + + Spacer().frame(width: 8) + + Button(action: presentSignInWithSolanaSheet) { + HStack { + Image("solana.gradient.logo") + .resizable() + .scaledToFit() + .frame(width: 16) + Spacer().frame(width: 8) + Text("Sign in with Solana") + .foregroundColor(themeManager.currentTheme.inverseTextColor) + .font( + Font.system(size: 12, weight: .medium) + ) + } + } + .frame(height: 30) + .frame(maxWidth: .infinity) + .background(.white) + .cornerRadius(6) + .buttonStyle(.plain) + .disabled(isLoginActionInFlight) + } + .frame(maxWidth: .infinity) } } diff --git a/app/network/Authenticate/LoginInitial/LoginInitialViewModel.swift b/app/network/Authenticate/LoginInitial/LoginInitialViewModel.swift index eaa86fe5..29dd2469 100644 --- a/app/network/Authenticate/LoginInitial/LoginInitialViewModel.swift +++ b/app/network/Authenticate/LoginInitial/LoginInitialViewModel.swift @@ -18,6 +18,7 @@ extension LoginInitialView { case apple case google case solana + case bittensor } @MainActor @@ -284,17 +285,37 @@ extension LoginInitialView.ViewModel { // MARK: Solana Sign in extension LoginInitialView.ViewModel { func createSolanaAuthLoginArgs(message: String, signature: String, publicKey: String) -> Result { - + let args = SdkAuthLoginArgs() let walletAuth = SdkWalletAuthArgs() walletAuth.blockchain = SdkSOL walletAuth.message = message walletAuth.signature = signature walletAuth.publicKey = publicKey - + args.walletAuth = walletAuth - + return .success(args) - + + } +} + +// MARK: Bittensor Sign in +extension LoginInitialView.ViewModel { + func createBittensorAuthLoginArgs(message: String, signature: String, publicKey: String) -> Result { + + let args = SdkAuthLoginArgs() + let walletAuth = SdkWalletAuthArgs() + // publicKey is the ss58 address; the signature is sr25519 hex from + // the ur.io/wallet-connect bridge + walletAuth.blockchain = SdkTAO + walletAuth.message = message + walletAuth.signature = signature + walletAuth.publicKey = publicKey + + args.walletAuth = walletAuth + + return .success(args) + } } diff --git a/app/network/Authenticate/ResetPassword/ResetPasswordView.swift b/app/network/Authenticate/ResetPassword/ResetPasswordView.swift index fa5b5af6..e31e8f1d 100644 --- a/app/network/Authenticate/ResetPassword/ResetPasswordView.swift +++ b/app/network/Authenticate/ResetPassword/ResetPasswordView.swift @@ -88,7 +88,7 @@ struct ResetPasswordView: View { case .success: - snackbarManager.showSnackbar(message: "Password reset link sent to \(userAuth).") + snackbarManager.showSnackbar(message: String(localized: "Password reset link sent to \(userAuth).")) self.popNavigationStack() break diff --git a/app/network/ContentView.swift b/app/network/ContentView.swift index 4a14f24e..01ebe3fd 100644 --- a/app/network/ContentView.swift +++ b/app/network/ContentView.swift @@ -120,7 +120,7 @@ struct ContentView: View { if case .failure(let error) = result { print("[ContentView] handleSuccessWithJwt: \(error.localizedDescription)") - snackbarManager.showSnackbar(message: "There was an error creating your network. Please try again later.") + snackbarManager.showSnackbar(message: String(localized: "There was an error creating your network. Please try again later.")) return } diff --git a/app/network/Main/Account/AccountRootView.swift b/app/network/Main/Account/AccountRootView.swift index f28fca23..f230f3e6 100644 --- a/app/network/Main/Account/AccountRootView.swift +++ b/app/network/Main/Account/AccountRootView.swift @@ -96,7 +96,7 @@ struct AccountRootView: View { .foregroundColor(themeManager.currentTheme.textColor) } else { - Text(isPro ? "Supporter" : "Free") + Text(isPro ? "Pro" : "Free") .font(themeManager.currentTheme.titleCondensedFont) .foregroundColor(themeManager.currentTheme.textColor) @@ -129,21 +129,37 @@ struct AccountRootView: View { usedByteCount: subscriptionBalanceViewModel.usedBalanceByteCount, meanReliabilityWeight: meanReliabilityWeight, totalReferrals: referralLinkViewModel.totalReferrals, - dailyBalanceByteCount: subscriptionBalanceViewModel.startBalanceByteCount + dailyBalanceByteCount: subscriptionBalanceViewModel.startBalanceByteCount, + referralCode: referralLinkViewModel.referralCode ) - HStack { - - Spacer() - - Button(action: { - viewModel.isPresentedRedeemBalanceCodeSheet = true - }) { + /** + * "Redeem Balance Code" gets its OWN row, as a full-width tap + * target. + * + * It used to be a small right-aligned link crammed under the + * usage bar beside the referral affordance — a fiddly thing to + * hit on a phone, and easy to miss altogether. The whole row is + * now tappable, with a chevron so it reads as somewhere to go. + */ + Button(action: { + viewModel.isPresentedRedeemBalanceCodeSheet = true + }) { + HStack { Text("Redeem Balance Code") - .font(themeManager.currentTheme.secondaryBodyFont) + .font(themeManager.currentTheme.bodyFont) + .foregroundColor(themeManager.currentTheme.textColor) + + Spacer() + + Image(systemName: "chevron.right") + .font(.system(size: 12, weight: .semibold)) + .foregroundColor(themeManager.currentTheme.textMutedColor) } - + .contentShape(Rectangle()) + .padding(.vertical, 12) } + .buttonStyle(.plain) Divider() .background(themeManager.currentTheme.borderBaseColor) @@ -327,12 +343,7 @@ struct AccountRootView: View { } .buttonStyle(.plain) - - /** - * URnode Carousel - */ - URNodeCarousel() - + Spacer().frame(height: 16) } @@ -443,8 +454,13 @@ struct AccountRootView: View { }, isPurchasing: subscriptionManager.isPurchasing, purchaseSuccess: subscriptionManager.purchaseSuccess, + purchasePending: subscriptionManager.purchasePending, dismiss: { viewModel.isPresentedUpgradeSheet = false + // the purchase flags describe ONE attempt; letting them + // survive is what showed "You're premium." to a user who + // had not actually completed a purchase + subscriptionManager.resetPurchaseState() } ) .environmentObject(themeManager) @@ -469,6 +485,7 @@ struct AccountRootView: View { ToolbarItem { AccountMenu( isGuest: isGuest, + isPro: isPro, logout: logout, networkName: networkName, isPresentedCreateAccount: $viewModel.isPresentedCreateAccount, @@ -501,6 +518,7 @@ struct AccountRootView: View { ToolbarItem(placement: .automatic) { AccountMenu( isGuest: isGuest, + isPro: isPro, logout: logout, networkName: networkName, isPresentedCreateAccount: $viewModel.isPresentedCreateAccount, @@ -539,7 +557,7 @@ struct AccountRootView: View { if case .failure(let error) = result { print("[AccountRootView] handleSuccessWithJwt: \(error.localizedDescription)") - snackbarManager.showSnackbar(message: "There was an error creating your network. Please try again later.") + snackbarManager.showSnackbar(message: String(localized: "There was an error creating your network. Please try again later.")) return } diff --git a/app/network/Main/Account/Profile/ProfileView.swift b/app/network/Main/Account/Profile/ProfileView.swift index 2a5e82ce..77fdeae4 100644 --- a/app/network/Main/Account/Profile/ProfileView.swift +++ b/app/network/Main/Account/Profile/ProfileView.swift @@ -89,12 +89,12 @@ struct ProfileView: View { switch result { case .success: if let userAuth = userAuth { - snackbarManager.showSnackbar(message: "Password reset link sent to \(userAuth)") + snackbarManager.showSnackbar(message: String(localized: "Password reset link sent to \(userAuth).")) } else { - snackbarManager.showSnackbar(message: "Something went wrong finding your account") + snackbarManager.showSnackbar(message: String(localized: "Something went wrong finding your account")) } case .failure: - snackbarManager.showSnackbar(message: "Error sending password reset link") + snackbarManager.showSnackbar(message: String(localized: "Error sending password reset link")) } } } diff --git a/app/network/Main/Account/Settings/SettingsForm-iOS.swift b/app/network/Main/Account/Settings/SettingsForm-iOS.swift index 97b6b35e..9568c62d 100644 --- a/app/network/Main/Account/Settings/SettingsForm-iOS.swift +++ b/app/network/Main/Account/Settings/SettingsForm-iOS.swift @@ -17,8 +17,8 @@ struct SettingsForm_iOS: View { let urApiService: UrApiServiceProtocol let clientId: SdkId?; - let clientUrl: String; let referralCode: String?; + let totalReferrals: Int let referralNetworkName: String? let version: String let isUpdatingAccountPreferences: Bool @@ -38,56 +38,48 @@ struct SettingsForm_iOS: View { @Binding var canReceiveProductUpdates: Bool var body: some View { - + Form { - - Section("URid") { - - /** - * Copy URid - */ - // TODO: copy URid - Button(action: { - if let clientId = clientId?.idStr { - - copyToPasteboard(clientId) - - snackbarManager.showSnackbar(message: "URid copied to clipboard") - } - }) { + + /** + * referral royalty: networks with at least one referral get the + * crowned frog mascot (same as the ur.io site) + */ + if 0 < totalReferrals { + Section { HStack { - Text(clientId?.idStr ?? "") - .font(themeManager.currentTheme.secondaryBodyFont) Spacer() - Image(systemName: "document.on.document") + VStack { + Image("ReferralFrog") + .resizable() + .scaledToFit() + .frame(height: 80) + Text("You're referral royalty!") + .font(themeManager.currentTheme.secondaryBodyFont) + .foregroundColor(themeManager.currentTheme.textMutedColor) + } + Spacer() } - .foregroundColor(themeManager.currentTheme.textMutedColor) } - .buttonStyle(.plain) - + .listRowBackground(Color.clear) } - - Section("Share URnetwork") { - + + Section("Client ID") { + /** - * Copy URnetwork link + * Copy client id */ - Button(action: { if let clientId = clientId?.idStr { - - copyToPasteboard("https://ur.io/c?\(clientId)") - - snackbarManager.showSnackbar(message: "URnetwork link copied to clipboard") - + + copyToPasteboard(clientId) + + snackbarManager.showSnackbar(message: String(localized: "Client ID copied to clipboard")) } }) { HStack { - Text(clientUrl) + Text(clientId?.idStr ?? "") .font(themeManager.currentTheme.secondaryBodyFont) - .foregroundColor(themeManager.currentTheme.textMutedColor) - .lineLimit(1) - .truncationMode(.tail) Spacer() Image(systemName: "document.on.document") } @@ -96,7 +88,7 @@ struct SettingsForm_iOS: View { .buttonStyle(.plain) } - + Section("Bonus referral code") { /** @@ -108,7 +100,7 @@ struct SettingsForm_iOS: View { copyToPasteboard(referralCode) - snackbarManager.showSnackbar(message: "Bonus referral code copied to clipboard") + snackbarManager.showSnackbar(message: String(localized: "Bonus referral code copied to clipboard")) } }) { @@ -328,15 +320,21 @@ struct SettingsForm_iOS: View { Section("General") { HStack { - + Text("Version and Build info") .font(themeManager.currentTheme.bodyFont) - + Spacer() - + Text(version.isEmpty ? "0.0.0" : version) .font(themeManager.currentTheme.bodyFont) } + + Link(destination: URL(string: "https://ur.xyz")!) { + Text("Uses the UR Protocol") + .font(themeManager.currentTheme.secondaryBodyFont) + .foregroundColor(themeManager.currentTheme.textMutedColor) + } } Section("Earning multipliers") { diff --git a/app/network/Main/Account/Settings/SettingsForm-macOS.swift b/app/network/Main/Account/Settings/SettingsForm-macOS.swift index 612a9b21..ee78bee2 100644 --- a/app/network/Main/Account/Settings/SettingsForm-macOS.swift +++ b/app/network/Main/Account/Settings/SettingsForm-macOS.swift @@ -17,8 +17,8 @@ struct SettingsForm_macOS: View { let urApiService: UrApiServiceProtocol let clientId: SdkId?; - let clientUrl: String; let referralCode: String?; + let totalReferrals: Int let referralNetworkName: String? let version: String let isUpdatingAccountPreferences: Bool @@ -53,67 +53,47 @@ struct SettingsForm_macOS: View { GeometryReader { geometry in ScrollView(.vertical) { - + VStack { - - HStack { - UrLabel(text: "URid") - - Spacer() - } - + /** - * Copy URid + * referral royalty: networks with at least one referral get + * the crowned frog mascot (same as the ur.io site) */ - // TODO: copy URid - Button(action: { - if let clientId = clientId?.idStr { - - copyToPasteboard(clientId) - - snackbarManager.showSnackbar(message: "URid copied to clipboard") - } - }) { - HStack { - Text(clientId?.idStr ?? "") + if 0 < totalReferrals { + VStack { + Image("ReferralFrog") + .resizable() + .scaledToFit() + .frame(height: 80) + Text("You're referral royalty!") .font(themeManager.currentTheme.secondaryBodyFont) - Spacer() - Image(systemName: "document.on.document") + .foregroundColor(themeManager.currentTheme.textMutedColor) } - .foregroundColor(themeManager.currentTheme.textMutedColor) - .padding() - .contentShape(Rectangle()) + .frame(maxWidth: .infinity) + .padding(.bottom, 16) } - .buttonStyle(.plain) - .background(themeManager.currentTheme.tintedBackgroundBase) - .clipShape(RoundedRectangle(cornerRadius: 16, style: .continuous)) - - Spacer().frame(height: 32) - - /** - * Copy URnetwork link - */ + HStack { - UrLabel(text: "Share URnetwork") - + UrLabel(text: "Client ID") + Spacer() } + /** + * Copy client id + */ Button(action: { if let clientId = clientId?.idStr { - - copyToPasteboard("https://ur.io/c?\(clientId)") - - snackbarManager.showSnackbar(message: "URnetwork link copied to clipboard") - + + copyToPasteboard(clientId) + + snackbarManager.showSnackbar(message: String(localized: "Client ID copied to clipboard")) } }) { HStack { - Text(clientUrl) + Text(clientId?.idStr ?? "") .font(themeManager.currentTheme.secondaryBodyFont) - .foregroundColor(themeManager.currentTheme.textMutedColor) - .lineLimit(1) - .truncationMode(.tail) Spacer() Image(systemName: "document.on.document") } @@ -124,7 +104,7 @@ struct SettingsForm_macOS: View { .buttonStyle(.plain) .background(themeManager.currentTheme.tintedBackgroundBase) .clipShape(RoundedRectangle(cornerRadius: 16, style: .continuous)) - + Spacer().frame(height: 32) /** @@ -141,7 +121,7 @@ struct SettingsForm_macOS: View { copyToPasteboard(referralCode) - snackbarManager.showSnackbar(message: "Bonus referral code copied to clipboard") + snackbarManager.showSnackbar(message: String(localized: "Bonus referral code copied to clipboard")) } }) { @@ -498,13 +478,25 @@ struct SettingsForm_macOS: View { HStack { Text(version.isEmpty ? "0.0.0" : version) .font(themeManager.currentTheme.bodyFont) - + Spacer() } .padding() .background(themeManager.currentTheme.tintedBackgroundBase) .clipShape(RoundedRectangle(cornerRadius: 16, style: .continuous)) - + + Spacer().frame(height: 16) + + HStack { + Link(destination: URL(string: "https://ur.xyz")!) { + Text("Uses the UR Protocol") + .font(themeManager.currentTheme.secondaryBodyFont) + .foregroundColor(themeManager.currentTheme.textMutedColor) + } + + Spacer() + } + Spacer().frame(height: 64) Button(role: .destructive, action: { diff --git a/app/network/Main/Account/Settings/SettingsView.swift b/app/network/Main/Account/Settings/SettingsView.swift index 08ff4f03..acf9fb3f 100644 --- a/app/network/Main/Account/Settings/SettingsView.swift +++ b/app/network/Main/Account/Settings/SettingsView.swift @@ -47,19 +47,14 @@ struct SettingsView: View { self.api = api } - var clientUrl: String { - guard let clientId = clientId?.idStr else { return "" } - return "https://ur.io/c?\(clientId)" - } - var body: some View { #if os(iOS) SettingsForm_iOS( urApiService: api, clientId: clientId, - clientUrl: clientUrl, referralCode: referralLinkViewModel.referralCode, + totalReferrals: referralLinkViewModel.totalReferrals, referralNetworkName: viewModel.referralNetwork?.name, version: viewModel.version, isUpdatingAccountPreferences: accountPreferencesViewModel.isUpdatingAccountPreferences, @@ -140,7 +135,7 @@ struct SettingsView: View { guard let pk = connectWalletProviderViewModel.connectedPublicKey else { viewModel.setIsSigningMessage(false) - snackbarManager.showSnackbar(message: "Couldn't parse public key, please try again later.") + snackbarManager.showSnackbar(message: String(localized: "Couldn't parse public key, please try again later.")) return } @@ -155,7 +150,7 @@ struct SettingsView: View { }, onError: { _ in viewModel.setIsSigningMessage(false) - snackbarManager.showSnackbar(message: "Sorry, there was an error claiming multiplier.") + snackbarManager.showSnackbar(message: String(localized: "Sorry, there was an error claiming multiplier.")) } ) } @@ -182,8 +177,8 @@ struct SettingsView: View { SettingsForm_macOS( urApiService: api, clientId: clientId, - clientUrl: clientUrl, referralCode: referralLinkViewModel.referralCode, + totalReferrals: referralLinkViewModel.totalReferrals, referralNetworkName: viewModel.referralNetwork?.name, version: viewModel.version, isUpdatingAccountPreferences: accountPreferencesViewModel.isUpdatingAccountPreferences, @@ -274,12 +269,12 @@ struct SettingsView: View { switch result { case .success(true): - snackbarManager.showSnackbar(message: "Successfully claimed multiplier!") + snackbarManager.showSnackbar(message: String(localized: "Successfully claimed multiplier!")) viewModel.presentSigninWithSolanaSheet = false case .success(false): - snackbarManager.showSnackbar(message: "Sorry, there was an error claiming multiplier.") + snackbarManager.showSnackbar(message: String(localized: "Sorry, there was an error claiming multiplier.")) case .failure(let error): - snackbarManager.showSnackbar(message: "Sorry, there was an error claiming multiplier: \(error)") + snackbarManager.showSnackbar(message: String(localized: "Sorry, there was an error claiming multiplier: \(error.localizedDescription)")) } } @@ -288,10 +283,10 @@ struct SettingsView: View { let result = await viewModel.updateDeviceName() switch result { case .success: - snackbarManager.showSnackbar(message: "Device name updated") + snackbarManager.showSnackbar(message: String(localized: "Device name updated")) case .failure(let error): print("Error updating device name: \(error)") - snackbarManager.showSnackbar(message: "There was an error updating the device name.") + snackbarManager.showSnackbar(message: String(localized: "There was an error updating the device name.")) } } @@ -302,7 +297,7 @@ struct SettingsView: View { break case .failure(let error): print("Error deleting account: \(error)") - snackbarManager.showSnackbar(message: "Sorry, there was an error deleting your account.") + snackbarManager.showSnackbar(message: String(localized: "Sorry, there was an error deleting your account.")) } } diff --git a/app/network/Main/Account/URNodeCarousel/URNodeCarousel.swift b/app/network/Main/Account/URNodeCarousel/URNodeCarousel.swift deleted file mode 100644 index d5da2fff..00000000 --- a/app/network/Main/Account/URNodeCarousel/URNodeCarousel.swift +++ /dev/null @@ -1,90 +0,0 @@ -// -// URNodeCarousel.swift -// URnetwork -// -// Created by Stuart Kuentzel on 9/10/25. -// - -import SwiftUI - -struct URNodeCarousel: View { - - @EnvironmentObject var themeManager: ThemeManager - - private let imgs = ["URnode1", "URnode2", "URnode3"] - private let cornerRadius: CGFloat = 24 - - var body: some View { - VStack { - Spacer().frame(height: 24) - - HStack { - Text("URnode") - .font(themeManager.currentTheme.titleCondensedFont) - .foregroundColor(themeManager.currentTheme.textColor) - Spacer() - } - .padding(.horizontal) - - Spacer().frame(height: 12) - - Group { - if #available(iOS 17, macOS 14, *) { - ScrollView(.horizontal) { - LazyHStack(spacing: 0) { - ForEach(imgs, id: \.self) { img in - - Image(img) - .resizable() - .scaledToFill() - .clipShape(RoundedRectangle(cornerRadius: cornerRadius)) - .padding(.horizontal) - .containerRelativeFrame(.horizontal) - - } - } - .scrollTargetLayout() - } - .scrollIndicators(.hidden) - .scrollTargetBehavior(.paging) - } else { - ScrollView(.horizontal, showsIndicators: false) { - HStack(spacing: 0) { - ForEach(imgs, id: \.self) { img in - Image(img) - .resizable() - .scaledToFill() - .frame(height: 200) - .clipShape(RoundedRectangle(cornerRadius: cornerRadius)) - // .frame(width: UIScreen.main.bounds.width) // approximate full-width fallback - } - } - } - } - } - - Spacer().frame(height: 24) - - HStack { - UrButton( - text: "Preorder Now", - action: { - if let url = URL(string: "https://ur.io/urnode") { - #if canImport(UIKit) - UIApplication.shared.open(url, options: [:], completionHandler: nil) - #endif - #if canImport(AppKit) - NSWorkspace.shared.open(url) - #endif - } - } - ) - } - .padding(.horizontal) - } - } -} - -#Preview { - URNodeCarousel() -} diff --git a/app/network/Main/Account/Wallet/WalletView.swift b/app/network/Main/Account/Wallet/WalletView.swift index 1fbfd6b7..25ce6c5e 100644 --- a/app/network/Main/Account/Wallet/WalletView.swift +++ b/app/network/Main/Account/Wallet/WalletView.swift @@ -22,14 +22,18 @@ struct WalletView: View { let fetchPayments: () async -> Void var walletName: String { - + if wallet.blockchain == "SOL" { return "Solana" } - + + if wallet.blockchain == "TAO" { + return "Bittensor" + } + // otherwise, POLY return "Polygon" - + } init( @@ -93,15 +97,28 @@ struct WalletView: View { * Actions */ VStack { - - if !isPayoutWallet { - + + /** + * bittensor wallets are recorded for future use only and + * cannot be the payout wallet + */ + if wallet.blockchain == "TAO" { + + Text("Bittensor wallets are stored for future use and can't receive payouts yet.") + .font(themeManager.currentTheme.secondaryBodyFont) + .foregroundColor(themeManager.currentTheme.textMutedColor) + .multilineTextAlignment(.center) + + Spacer().frame(height: 8) + + } else if !isPayoutWallet { + UrButton(text: "Make default", action: { makeDefaultWallet() }) - + Spacer().frame(height: 8) - + } UrButton( @@ -161,17 +178,17 @@ struct WalletView: View { private func makeDefaultWallet() { guard let walletId = wallet.walletId else { - snackbarManager.showSnackbar(message: "Error setting default wallet") - + snackbarManager.showSnackbar(message: String(localized: "Error setting default wallet")) + return } - + Task { switch await payoutWalletViewModel.updatePayoutWallet(walletId) { case .success: - snackbarManager.showSnackbar(message: "Payout wallet updated") + snackbarManager.showSnackbar(message: String(localized: "Payout wallet updated")) case .failure(let error): - snackbarManager.showSnackbar(message: "Error setting default wallet: \(error.localizedDescription)") + snackbarManager.showSnackbar(message: String(localized: "Error setting default wallet: \(error.localizedDescription)")) } } } diff --git a/app/network/Main/Account/Wallets/ConnectWalletSheet/ConnectWalletSheetView.swift b/app/network/Main/Account/Wallets/ConnectWalletSheet/ConnectWalletSheetView.swift index 63888740..79cb7cee 100644 --- a/app/network/Main/Account/Wallets/ConnectWalletSheet/ConnectWalletSheetView.swift +++ b/app/network/Main/Account/Wallets/ConnectWalletSheet/ConnectWalletSheetView.swift @@ -76,10 +76,36 @@ struct ConnectWalletSheetView: View { .buttonStyle(.plain) .disabled(!connectWalletProviderViewModel.isWalletAppInstalled(.solflare)) + /** + * Bittensor wallets connect by address (recorded for future + * use; they can't receive payouts yet) + */ Button(action: { navigate(ConnectWalletNavigationPath.external) }) { - + + VStack { + Text("τ") + .font(.system(size: 28, weight: .bold)) + .foregroundColor(.white) + .frame(width: 36, height: 36) + .padding() + .background(Color(hex: "#1C1C1C")) + .cornerRadius(12) + + + Text("Bittensor") + .font(themeManager.currentTheme.secondaryBodyFont) + .foregroundColor(themeManager.currentTheme.textColor) + } + + } + .buttonStyle(.plain) + + Button(action: { + navigate(ConnectWalletNavigationPath.external) + }) { + VStack { Image("ur.symbols.wallet") .resizable() @@ -88,15 +114,15 @@ struct ConnectWalletSheetView: View { .padding() .background(.urLightBlue) .cornerRadius(12) - + Text("Other") .font(themeManager.currentTheme.secondaryBodyFont) .foregroundColor(themeManager.currentTheme.textColor) } - + } - + } Spacer().frame(height: 16) @@ -124,7 +150,7 @@ struct ConnectWalletSheetView: View { } private func showWalletOpenFailed() { - snackbarManager.showSnackbar(message: "Couldn't open wallet. Please install it and try again.") + snackbarManager.showSnackbar(message: String(localized: "Couldn't open wallet. Please install it and try again.")) } } diff --git a/app/network/Main/Account/Wallets/ConnectWalletSheet/EnterWalletAddress/EnterWalletAddressView.swift b/app/network/Main/Account/Wallets/ConnectWalletSheet/EnterWalletAddress/EnterWalletAddressView.swift index 54d4171d..4e874203 100644 --- a/app/network/Main/Account/Wallets/ConnectWalletSheet/EnterWalletAddress/EnterWalletAddressView.swift +++ b/app/network/Main/Account/Wallets/ConnectWalletSheet/EnterWalletAddress/EnterWalletAddressView.swift @@ -34,9 +34,9 @@ struct EnterWalletAddressView: View { UrTextField( text: $viewModel.walletAddress, - label: "USDC wallet address", - placeholder: "Enter a Solana USDC wallet address", - supportingText: "USDC addresses on Solana are currently supported", + label: "Wallet address", + placeholder: "Enter a Solana or Bittensor wallet address", + supportingText: "Solana USDC addresses and Bittensor (TAO) addresses are supported. Bittensor wallets are stored for future use and can't receive payouts yet.", isEnabled: !accountWalletsViewModel.isCreatingWallet, submitLabel: .continue, onSubmit: { diff --git a/app/network/Main/Account/Wallets/ConnectWalletSheet/EnterWalletAddress/EnterWalletAddressViewModel.swift b/app/network/Main/Account/Wallets/ConnectWalletSheet/EnterWalletAddress/EnterWalletAddressViewModel.swift index 837a2778..85dcc92a 100644 --- a/app/network/Main/Account/Wallets/ConnectWalletSheet/EnterWalletAddress/EnterWalletAddressViewModel.swift +++ b/app/network/Main/Account/Wallets/ConnectWalletSheet/EnterWalletAddress/EnterWalletAddressViewModel.swift @@ -54,23 +54,25 @@ extension EnterWalletAddressView { let solanaResult = await self.validateAddress(addressToValidate, chain: "SOL") guard !Task.isCancelled, self.walletAddress == addressToValidate else { return } - - switch (solanaResult) { - case (.success(let isSolanaValid)): - - if isSolanaValid { - self.chain = WalletChain.sol - self.isValidWalletAddress = true - } else { - self.chain = WalletChain.invalid - self.isValidWalletAddress = false - } - - default: - print("\(domain) validation failed") + + if case .success(true) = solanaResult { + self.chain = WalletChain.sol + self.isValidWalletAddress = true + print("is valid wallet address: \(self.isValidWalletAddress)") + print("chain is \(self.chain)") + return + } + + // bittensor (ss58) wallets are recorded for future use only + let taoResult = await self.validateAddress(addressToValidate, chain: "TAO") + guard !Task.isCancelled, self.walletAddress == addressToValidate else { return } + + if case .success(true) = taoResult { + self.chain = WalletChain.tao + self.isValidWalletAddress = true + } else { self.chain = WalletChain.invalid self.isValidWalletAddress = false - } print("is valid wallet address: \(self.isValidWalletAddress)") print("chain is \(self.chain)") diff --git a/app/network/Main/Account/Wallets/WalletsView.swift b/app/network/Main/Account/Wallets/WalletsView.swift index f63dc64d..509f7609 100644 --- a/app/network/Main/Account/Wallets/WalletsView.swift +++ b/app/network/Main/Account/Wallets/WalletsView.swift @@ -169,12 +169,12 @@ struct WalletsView: View { await payoutWalletViewModel.fetchPayoutWallet() viewModel.presentConnectWalletSheet = false case .failure(let error): - snackbarManager.showSnackbar(message: "There was an error connecting your wallet: \(error.localizedDescription)") + snackbarManager.showSnackbar(message: String(localized: "There was an error connecting your wallet: \(error.localizedDescription)")) } } }, onError: { _ in - snackbarManager.showSnackbar(message: "There was an error connecting your wallet.") + snackbarManager.showSnackbar(message: String(localized: "There was an error connecting your wallet.")) } ) } diff --git a/app/network/Main/Connect/ConnectActions/ConnectActions.swift b/app/network/Main/Connect/ConnectActions/ConnectActions.swift index 11f67b57..66f94265 100644 --- a/app/network/Main/Connect/ConnectActions/ConnectActions.swift +++ b/app/network/Main/Connect/ConnectActions/ConnectActions.swift @@ -26,6 +26,8 @@ struct ConnectActions: View { let promptMoreDataFlow: () -> Void let meanReliabilityWeight: Double let totalReferrals: Int + // when set, the usage bar referral row shares the referral link + let referralCode: String? let isPro: Bool @Binding var selectedWindowType: WindowType @Binding var fixedIpSize: Bool @@ -172,7 +174,7 @@ struct ConnectActions: View { HStack(alignment: .firstTextBaseline) { - Text(isPro ? "Supporter" : "Free") + Text(isPro ? "Pro" : "Free") .font(themeManager.currentTheme.titleCondensedFont) .foregroundColor(themeManager.currentTheme.textColor) @@ -195,7 +197,8 @@ struct ConnectActions: View { usedByteCount: usedByteCount, meanReliabilityWeight: meanReliabilityWeight, totalReferrals: totalReferrals, - dailyBalanceByteCount: dailyBalanceByteCount + dailyBalanceByteCount: dailyBalanceByteCount, + referralCode: referralCode ) } diff --git a/app/network/Main/Connect/ConnectButton/StatusIndicator/ConnectStatusIndicator.swift b/app/network/Main/Connect/ConnectButton/StatusIndicator/ConnectStatusIndicator.swift index 9d37bb3e..5a0b9bba 100644 --- a/app/network/Main/Connect/ConnectButton/StatusIndicator/ConnectStatusIndicator.swift +++ b/app/network/Main/Connect/ConnectButton/StatusIndicator/ConnectStatusIndicator.swift @@ -48,7 +48,9 @@ struct ConnectStatusIndicator: View { if displayReconnectTunnel { return String(localized: "VPN tunnel disconnected 😓") } else { - return String(localized: "Connected to \(windowCurrentSize) \(windowCurrentSize == 1 ? "provider" : "providers")") + // real plural rules live in Localizable.xcstrings + // ("Connected to %d providers") + return String(localized: "Connected to \(windowCurrentSize) providers") } } case .none: return "" diff --git a/app/network/Main/Connect/ConnectStatsSections.swift b/app/network/Main/Connect/ConnectStatsSections.swift index a21b5ba2..04fbbed6 100644 --- a/app/network/Main/Connect/ConnectStatsSections.swift +++ b/app/network/Main/Connect/ConnectStatsSections.swift @@ -28,6 +28,7 @@ struct ConnectStatsSections: View { @EnvironmentObject var themeManager: ThemeManager @EnvironmentObject var throughputStore: ThroughputStore @EnvironmentObject var dnsSettingsStore: DnsSettingsStore + @EnvironmentObject var deviceManager: DeviceManager let openSheet: (ConnectStatsSheet) -> Void @@ -106,6 +107,22 @@ struct ConnectStatsSections: View { } + /** + * Ad and tracker blocker toggle. The device applies it immediately + * and persists it to local settings. + */ + VStack(alignment: .leading, spacing: 0) { + UrSwitchToggle(isOn: $deviceManager.blockerEnabled) { + Text("Block ads and trackers") + .font(themeManager.currentTheme.bodyFont) + .foregroundColor(themeManager.currentTheme.textColor) + } + } + .padding() + .frame(maxWidth: .infinity, alignment: .leading) + .background(themeManager.currentTheme.tintedBackgroundBase) + .cornerRadius(12) + } } @@ -170,7 +187,9 @@ private struct SplitRuleCountLabel: View { @EnvironmentObject var blockActionsStore: BlockActionsStore var body: some View { - Text("\(blockActionsStore.splitRules.count) split rule\(blockActionsStore.splitRules.count == 1 ? "" : "s")") + // real plural rules live in Localizable.xcstrings ("%lld split rules"); + // never inflect in the interpolation, it is untranslatable + Text("\(blockActionsStore.splitRules.count) split rules") .font(themeManager.currentTheme.secondaryBodyFont) .foregroundColor(themeManager.currentTheme.textColor) } diff --git a/app/network/Main/Connect/ConnectView-iOS.swift b/app/network/Main/Connect/ConnectView-iOS.swift index bcb51a35..084f82e5 100644 --- a/app/network/Main/Connect/ConnectView-iOS.swift +++ b/app/network/Main/Connect/ConnectView-iOS.swift @@ -195,6 +195,7 @@ struct ConnectView_iOS: View { }, meanReliabilityWeight: meanReliabilityWeight, totalReferrals: referralLinkViewModel.totalReferrals, + referralCode: referralLinkViewModel.referralCode, isPro: isPro, selectedWindowType: $deviceManager.selectedWindowType, fixedIpSize: $deviceManager.fixedIpSize, @@ -366,8 +367,13 @@ struct ConnectView_iOS: View { }, isPurchasing: subscriptionManager.isPurchasing, purchaseSuccess: subscriptionManager.purchaseSuccess, + purchasePending: subscriptionManager.purchasePending, dismiss: { connectViewModel.isPresentedUpgradeSheet = false + // the purchase flags describe ONE attempt; letting them + // survive is what showed "You're premium." to a user who + // had not actually completed a purchase + subscriptionManager.resetPurchaseState() } ) .environmentObject(themeManager) @@ -410,7 +416,7 @@ struct ConnectView_iOS: View { if case .failure(let error) = result { print("[ContentView] handleSuccessWithJwt: \(error.localizedDescription)") - snackbarManager.showSnackbar(message: "There was an error creating your network. Please try again later.") + snackbarManager.showSnackbar(message: String(localized: "There was an error creating your network. Please try again later.")) return } diff --git a/app/network/Main/Connect/ConnectView-macOS.swift b/app/network/Main/Connect/ConnectView-macOS.swift index 3cf5b399..27d5386d 100644 --- a/app/network/Main/Connect/ConnectView-macOS.swift +++ b/app/network/Main/Connect/ConnectView-macOS.swift @@ -32,6 +32,7 @@ import URnetworkSdk let promptMoreDataFlow: () -> Void let meanReliabilityWeight: Double let totalReferrals: Int + let referralCode: String? let isPro: Bool init( @@ -40,12 +41,14 @@ import URnetworkSdk promptMoreDataFlow: @escaping () -> Void, meanReliabilityWeight: Double, totalReferrals: Int, + referralCode: String?, isPro: Bool ) { self.providerListStore = providerStore self.promptMoreDataFlow = promptMoreDataFlow self.meanReliabilityWeight = meanReliabilityWeight self.totalReferrals = totalReferrals + self.referralCode = referralCode self.isPro = isPro } @@ -101,6 +104,7 @@ import URnetworkSdk promptMoreDataFlow: promptMoreDataFlow, meanReliabilityWeight: meanReliabilityWeight, totalReferrals: totalReferrals, + referralCode: referralCode, isPro: isPro, selectedWindowType: $deviceManager.selectedWindowType, fixedIpSize: $deviceManager.fixedIpSize, @@ -238,8 +242,13 @@ import URnetworkSdk }, isPurchasing: subscriptionManager.isPurchasing, purchaseSuccess: subscriptionManager.purchaseSuccess, + purchasePending: subscriptionManager.purchasePending, dismiss: { connectViewModel.isPresentedUpgradeSheet = false + // the purchase flags describe ONE attempt; letting them + // survive is what showed "You're premium." to a user who + // had not actually completed a purchase + subscriptionManager.resetPurchaseState() } ) .environmentObject(themeManager) diff --git a/app/network/Main/Connect/ProviderListItem/ProviderListItemView.swift b/app/network/Main/Connect/ProviderListItem/ProviderListItemView.swift index 2c7662b8..88feac37 100644 --- a/app/network/Main/Connect/ProviderListItem/ProviderListItemView.swift +++ b/app/network/Main/Connect/ProviderListItem/ProviderListItemView.swift @@ -46,6 +46,7 @@ struct ProviderListItemView: View { HStack(spacing: 0) { if let providerCount = providerCount, providerCount > 0 { + // real plural rules live in Localizable.xcstrings ("%d providers") Text("\(providerCount) providers") .font(themeManager.currentTheme.secondaryBodyFont) .foregroundColor(themeManager.currentTheme.textMutedColor) diff --git a/app/network/Main/Connect/SelectedProvider.swift b/app/network/Main/Connect/SelectedProvider.swift index a006e207..3554356d 100644 --- a/app/network/Main/Connect/SelectedProvider.swift +++ b/app/network/Main/Connect/SelectedProvider.swift @@ -38,7 +38,8 @@ struct SelectedProvider: View { if selectedProvider.providerCount > 0 { HStack(spacing: 0) { - + + // real plural rules live in Localizable.xcstrings ("%d providers") Text("\(selectedProvider.providerCount) providers") .font(themeManager.currentTheme.secondaryBodyFont) .foregroundColor(themeManager.currentTheme.textMutedColor) diff --git a/app/network/Main/Feedback/FeedbackView.swift b/app/network/Main/Feedback/FeedbackView.swift index 3873a78b..d87b0c0a 100644 --- a/app/network/Main/Feedback/FeedbackView.swift +++ b/app/network/Main/Feedback/FeedbackView.swift @@ -124,7 +124,7 @@ struct FeedbackView: View { // TODO: message sent overlay - snackbarManager.showSnackbar(message: "Sent! Thanks for your feedback.") + snackbarManager.showSnackbar(message: String(localized: "Sent! Thanks for your feedback.")) requestReview() @@ -139,7 +139,7 @@ struct FeedbackView: View { } case .failure: - snackbarManager.showSnackbar(message: "There was an error sending your feedback. Please try again later.") + snackbarManager.showSnackbar(message: String(localized: "There was an error sending your feedback. Please try again later.")) } } } diff --git a/app/network/Main/MainView.swift b/app/network/Main/MainView.swift index dcd874c9..397f8dd1 100644 --- a/app/network/Main/MainView.swift +++ b/app/network/Main/MainView.swift @@ -105,6 +105,24 @@ struct MainView: View { .background(themeManager.currentTheme.backgroundColor) .environmentObject(subscriptionBalanceViewModel) .environmentObject(subscriptionManager) + .onChange(of: subscriptionManager.transactionUpdateSequence) { _ in + /** + * StoreKit delivered a transaction outside of an in-app purchase flow: an + * Ask to Buy approval that came through later, a purchase made on another + * device, a renewal — anything that completed while the app was closed. + * + * This used to do NOTHING. The transaction listener starts at init, but the + * callback it invoked (`onPurchaseSuccess`) is only assigned inside + * `purchase()`. So on a fresh launch it was nil: StoreKit handed us the + * transaction, we finished it, and the app never noticed. The user's purchase + * had completed and they were still on the free plan, with nothing to explain + * why. + * + * Poll: the server learns of the purchase by webhook, so give it a moment and + * pick up the entitlement. + */ + subscriptionBalanceViewModel.startPolling() + } .onChange(of: deviceManager.isPro) { newValue in /** * plan updates change subscription balance polling behavior diff --git a/app/network/Shared/Models/WalletChain.swift b/app/network/Shared/Models/WalletChain.swift index 90157ecf..cefa867c 100644 --- a/app/network/Shared/Models/WalletChain.swift +++ b/app/network/Shared/Models/WalletChain.swift @@ -9,5 +9,8 @@ import Foundation enum WalletChain: String { case sol = "SOL" + // TAO (bittensor) wallets are recorded for future use only: they cannot + // be the payout wallet (payouts are USDC on Solana/Polygon) + case tao = "TAO" case invalid = "INVALID" } diff --git a/app/network/Shared/Resources/Localizable.xcstrings b/app/network/Shared/Resources/Localizable.xcstrings index 2f0bf5bd..58772574 100644 --- a/app/network/Shared/Resources/Localizable.xcstrings +++ b/app/network/Shared/Resources/Localizable.xcstrings @@ -12,47 +12,79 @@ }, "..." : { "comment" : "An ellipsis character used to indicate ongoing processing.", - "isCommentAutoGenerated" : true + "isCommentAutoGenerated" : true, + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "..." + } + } + } }, "************" : { + "comment" : "Used in Authenticate/CreateNetwork/CreateNetworkView.swift.", "shouldTranslate" : false }, "***%@" : { + "comment" : "Used in Authenticate/CreateNetwork/CreateNetworkView.swift.", "shouldTranslate" : false }, "#%@" : { - + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "#%@" + } + } + } }, "#%lld" : { - + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "#%lld" + } + } + } }, "%@" : { - + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@" + } + } + } }, "%@ Payout" : { + "comment" : "Call to action", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "%@ دفع" + "value" : "دفعة %@" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Výplata z %@" + "value" : "Výplata ze dne %@" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "%@ Auszahlung" + "value" : "Auszahlung am %@" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "%@ Πληρωμή" + "value" : "Πληρωμή %@" } }, "en" : { @@ -64,7 +96,7 @@ "es" : { "stringUnit" : { "state" : "translated", - "value" : "Pago de %@" + "value" : "Pago del %@" } }, "es-419" : { @@ -73,10 +105,16 @@ "value" : "Pago de %@" } }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pago del %@" + } + }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Paiement %@" + "value" : "Paiement du %@" } }, "he" : { @@ -106,7 +144,7 @@ "ja" : { "stringUnit" : { "state" : "translated", - "value" : "%@ 支払い" + "value" : "%@ の支払い" } }, "ko" : { @@ -118,7 +156,7 @@ "nl" : { "stringUnit" : { "state" : "translated", - "value" : "%@ uitbetaling" + "value" : "%@ Uitbetaling" } }, "pl" : { @@ -127,6 +165,12 @@ "value" : "Wypłata %@" } }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pagamento de %@" + } + }, "pt-BR" : { "stringUnit" : { "state" : "translated", @@ -142,7 +186,7 @@ "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Выплата %@" + "value" : "Выплата за %@" } }, "sv" : { @@ -154,7 +198,7 @@ "th" : { "stringUnit" : { "state" : "translated", - "value" : "การจ่ายเงิน %@" + "value" : "ยอดจ่าย %@" } }, "uk" : { @@ -193,6 +237,12 @@ "state" : "translated", "value" : "%@ peněženky" } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@ wallet" + } } } }, @@ -204,6 +254,12 @@ "value" : "%@ Peněženka" } }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@ Wallet" + } + }, "nl" : { "stringUnit" : { "state" : "translated", @@ -367,17 +423,534 @@ } }, "%d providers" : { + "comment" : "A label that shows the number of providers for a location. The argument is the number of providers.", "localizations" : { + "ar" : { + "variations" : { + "plural" : { + "zero" : { + "stringUnit" : { + "state" : "translated", + "value" : "%d مزود" + } + }, + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "مزود واحد" + } + }, + "two" : { + "stringUnit" : { + "state" : "translated", + "value" : "مزودان" + } + }, + "few" : { + "stringUnit" : { + "state" : "translated", + "value" : "%d مزودين" + } + }, + "many" : { + "stringUnit" : { + "state" : "translated", + "value" : "%d مزودًا" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%d مزود" + } + } + } + } + }, "cs" : { - "stringUnit" : { - "state" : "translated", - "value" : "%d poskytovatelů" + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%d poskytovatel" + } + }, + "few" : { + "stringUnit" : { + "state" : "translated", + "value" : "%d poskytovatelé" + } + }, + "many" : { + "stringUnit" : { + "state" : "translated", + "value" : "%d poskytovatele" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%d poskytovatelů" + } + } + } + } + }, + "de" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%d Anbieter" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%d Anbieter" + } + } + } + } + }, + "el" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%d πάροχος" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%d πάροχοι" + } + } + } + } + }, + "en" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%d provider" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%d providers" + } + } + } + } + }, + "es" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%d proveedor" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%d proveedores" + } + } + } + } + }, + "es-419" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%d proveedor" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%d proveedores" + } + } + } + } + }, + "es-MX" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%d proveedor" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%d proveedores" + } + } + } + } + }, + "fr" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%d fournisseur" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%d fournisseurs" + } + } + } + } + }, + "he" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%d ספק" + } + }, + "two" : { + "stringUnit" : { + "state" : "translated", + "value" : "%d ספקים" + } + }, + "many" : { + "stringUnit" : { + "state" : "translated", + "value" : "%d ספקים" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%d ספקים" + } + } + } + } + }, + "hi" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%d प्रदाता" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%d प्रदाता" + } + } + } + } + }, + "id" : { + "variations" : { + "plural" : { + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%d penyedia" + } + } + } + } + }, + "it" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%d provider" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%d provider" + } + } + } + } + }, + "ja" : { + "variations" : { + "plural" : { + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%d プロバイダー" + } + } + } + } + }, + "ko" : { + "variations" : { + "plural" : { + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%d개 제공자" + } + } + } } }, "nl" : { - "stringUnit" : { - "state" : "translated", - "value" : "%d providers" + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%d aanbieder" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%d aanbieders" + } + } + } + } + }, + "pl" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%d dostawca" + } + }, + "few" : { + "stringUnit" : { + "state" : "translated", + "value" : "%d dostawców" + } + }, + "many" : { + "stringUnit" : { + "state" : "translated", + "value" : "%d dostawców" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%d dostawców" + } + } + } + } + }, + "pt" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%d fornecedor" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%d fornecedores" + } + } + } + } + }, + "pt-BR" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%d provedor" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%d provedores" + } + } + } + } + }, + "pt-PT" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%d fornecedor" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%d fornecedores" + } + } + } + } + }, + "ru" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%d провайдер" + } + }, + "few" : { + "stringUnit" : { + "state" : "translated", + "value" : "%d провайдера" + } + }, + "many" : { + "stringUnit" : { + "state" : "translated", + "value" : "%d провайдеров" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%d провайдеров" + } + } + } + } + }, + "sv" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%d leverantör" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%d leverantörer" + } + } + } + } + }, + "th" : { + "variations" : { + "plural" : { + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%d ผู้ให้บริการ" + } + } + } + } + }, + "uk" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%d провайдер" + } + }, + "few" : { + "stringUnit" : { + "state" : "translated", + "value" : "%d провайдери" + } + }, + "many" : { + "stringUnit" : { + "state" : "translated", + "value" : "%d провайдерів" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%d провайдера" + } + } + } + } + }, + "vi" : { + "variations" : { + "plural" : { + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%d nhà cung cấp" + } + } + } + } + }, + "zh-Hans" : { + "variations" : { + "plural" : { + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%d 个提供商" + } + } + } + } + }, + "zh-HK" : { + "variations" : { + "plural" : { + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%d 個供應商" + } + } + } } } } @@ -387,15 +960,9 @@ }, "%lf %@" : { "localizations" : { - "cs" : { - "stringUnit" : { - "state" : "translated", - "value" : "" - } - }, "en" : { "stringUnit" : { - "state" : "new", + "state" : "translated", "value" : "%1$lf %2$@" } }, @@ -414,36982 +981,61170 @@ "comment" : "A label displaying the number of allowed and blocked actions.", "isCommentAutoGenerated" : true, "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$lld مسموح · %2$lld محظور" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Povoleno: %1$lld · Blokováno: %2$lld" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$lld erlaubt · %2$lld blockiert" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$lld επιτρεπόμενα · %2$lld αποκλεισμένα" + } + }, "en" : { "stringUnit" : { - "state" : "new", + "state" : "translated", "value" : "%1$lld allowed · %2$lld blocked" } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$lld permitidas · %2$lld bloqueadas" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$lld permitidas · %2$lld bloqueadas" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$lld permitidas · %2$lld bloqueadas" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$lld autorisés · %2$lld bloqués" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$lld מותרים · %2$lld חסומים" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$lld अनुमत · %2$lld अवरुद्ध" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$lld diizinkan · %2$lld diblokir" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$lld consentiti · %2$lld bloccati" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "許可 %1$lld · ブロック %2$lld" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "허용 %1$lld · 차단 %2$lld" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$lld toegestaan · %2$lld geblokkeerd" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dozwolone: %1$lld · Zablokowane: %2$lld" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$lld permitidos · %2$lld bloqueados" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$lld permitidos · %2$lld bloqueados" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$lld permitidos · %2$lld bloqueados" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$lld разрешено · %2$lld заблокировано" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$lld tillåtna · %2$lld blockerade" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "อนุญาต %1$lld · บล็อก %2$lld" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$lld дозволено · %2$lld заблоковано" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$lld được phép · %2$lld bị chặn" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$lld 已允许 · %2$lld 已拦截" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$lld 已允許 · %2$lld 已攔截" + } } } }, "%lld contracts" : { "comment" : "A label that shows the number of contracts for a given client. The argument is the number of contracts.", - "isCommentAutoGenerated" : true + "isCommentAutoGenerated" : true, + "localizations" : { + "ar" : { + "variations" : { + "plural" : { + "zero" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld عقد" + } + }, + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "عقد واحد" + } + }, + "two" : { + "stringUnit" : { + "state" : "translated", + "value" : "عقدان" + } + }, + "few" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld عقود" + } + }, + "many" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld عقدًا" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld عقد" + } + } + } + } + }, + "cs" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld kontrakt" + } + }, + "few" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld kontrakty" + } + }, + "many" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld kontraktu" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kontrakty: %lld" + } + } + } + } + }, + "de" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld Vertrag" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld Verträge" + } + } + } + } + }, + "el" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld συμβόλαιο" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld συμβόλαια" + } + } + } + } + }, + "en" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld contract" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld contracts" + } + } + } + } + }, + "es" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld contrato" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld contratos" + } + } + } + } + }, + "es-419" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld contrato" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld contratos" + } + } + } + } + }, + "es-MX" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld contrato" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld contratos" + } + } + } + } + }, + "fr" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld contrat" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld contrats" + } + } + } + } + }, + "he" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld חוזה" + } + }, + "two" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld חוזים" + } + }, + "many" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld חוזים" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld חוזים" + } + } + } + } + }, + "hi" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld कॉन्ट्रैक्ट" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld कॉन्ट्रैक्ट" + } + } + } + } + }, + "id" : { + "variations" : { + "plural" : { + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld kontrak" + } + } + } + } + }, + "it" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld contratto" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld contratti" + } + } + } + } + }, + "ja" : { + "variations" : { + "plural" : { + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld件の契約" + } + } + } + } + }, + "ko" : { + "variations" : { + "plural" : { + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "계약 %lld개" + } + } + } + } + }, + "nl" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld contract" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld contracten" + } + } + } + } + }, + "pl" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld kontrakt" + } + }, + "few" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld kontrakty" + } + }, + "many" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld kontraktów" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kontrakty: %lld" + } + } + } + } + }, + "pt" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld contrato" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld contratos" + } + } + } + } + }, + "pt-BR" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld contrato" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld contratos" + } + } + } + } + }, + "pt-PT" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld contrato" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld contratos" + } + } + } + } + }, + "ru" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld контракт" + } + }, + "few" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld контракта" + } + }, + "many" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld контрактов" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "Контрактов: %lld" + } + } + } + } + }, + "sv" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld kontrakt" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld kontrakt" + } + } + } + } + }, + "th" : { + "variations" : { + "plural" : { + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "สัญญา %lld รายการ" + } + } + } + } + }, + "uk" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld контракт" + } + }, + "few" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld контракти" + } + }, + "many" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld контрактів" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "Контрактів: %lld" + } + } + } + } + }, + "vi" : { + "variations" : { + "plural" : { + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld hợp đồng" + } + } + } + } + }, + "zh-Hans" : { + "variations" : { + "plural" : { + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld 个合约" + } + } + } + } + }, + "zh-HK" : { + "variations" : { + "plural" : { + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld 個合約" + } + } + } + } + } + } }, "%lld host%@" : { "comment" : "A label that shows the number of hosts for a split rule. The argument is the number of hosts.", + "extractionState" : "stale", "isCommentAutoGenerated" : true, "localizations" : { "en" : { "stringUnit" : { - "state" : "new", + "state" : "translated", "value" : "%1$lld host%2$@" } } } }, - "%lld new" : { - "comment" : "A count of new routing events that have not yet been scrolled into view.", - "isCommentAutoGenerated" : true - }, - "%lld split rule%@" : { - "comment" : "A label displaying the number of split rules. The argument is the count of split rules.", + "%lld hosts" : { + "comment" : "A label that shows the number of hosts for a split rule. The argument is the number of hosts.", "isCommentAutoGenerated" : true, "localizations" : { + "ar" : { + "variations" : { + "plural" : { + "zero" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld مضيف" + } + }, + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "مضيف واحد" + } + }, + "two" : { + "stringUnit" : { + "state" : "translated", + "value" : "مضيفان" + } + }, + "few" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld مضيفين" + } + }, + "many" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld مضيفًا" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld مضيف" + } + } + } + } + }, + "cs" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld hostitel" + } + }, + "few" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld hostitelé" + } + }, + "many" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld hostitele" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld hostitelů" + } + } + } + } + }, + "de" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld Host" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld Hosts" + } + } + } + } + }, + "el" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld host" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld hosts" + } + } + } + } + }, "en" : { - "stringUnit" : { - "state" : "new", - "value" : "%1$lld split rule%2$@" + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld host" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld hosts" + } + } + } + } + }, + "es" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld host" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld hosts" + } + } + } + } + }, + "es-419" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld host" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld hosts" + } + } + } + } + }, + "es-MX" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld host" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld hosts" + } + } + } + } + }, + "fr" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld hôte" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld hôtes" + } + } + } + } + }, + "he" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld מארח" + } + }, + "two" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld מארחים" + } + }, + "many" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld מארחים" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld מארחים" + } + } + } + } + }, + "hi" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld होस्ट" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld होस्ट" + } + } + } + } + }, + "id" : { + "variations" : { + "plural" : { + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld host" + } + } + } + } + }, + "it" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld host" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld host" + } + } + } + } + }, + "ja" : { + "variations" : { + "plural" : { + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld件のホスト" + } + } + } + } + }, + "ko" : { + "variations" : { + "plural" : { + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "호스트 %lld개" + } + } + } + } + }, + "nl" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld host" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld hosts" + } + } + } + } + }, + "pl" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld host" + } + }, + "few" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld hosty" + } + }, + "many" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld hostów" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld hosta" + } + } + } + } + }, + "pt" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld host" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld hosts" + } + } + } + } + }, + "pt-BR" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld host" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld hosts" + } + } + } + } + }, + "pt-PT" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld host" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld hosts" + } + } + } + } + }, + "ru" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld хост" + } + }, + "few" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld хоста" + } + }, + "many" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld хостов" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld хоста" + } + } + } + } + }, + "sv" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld värd" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld värdar" + } + } + } + } + }, + "th" : { + "variations" : { + "plural" : { + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "โฮสต์ %lld รายการ" + } + } + } + } + }, + "uk" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld хост" + } + }, + "few" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld хости" + } + }, + "many" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld хостів" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld хоста" + } + } + } + } + }, + "vi" : { + "variations" : { + "plural" : { + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld máy chủ" + } + } + } + } + }, + "zh-Hans" : { + "variations" : { + "plural" : { + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld 个主机" + } + } + } + } + }, + "zh-HK" : { + "variations" : { + "plural" : { + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld 個主機" + } + } + } } } } }, - "%lld/5" : { - "comment" : "A label displaying the number of referrals a user has made. The argument is the number of referrals.", - "isCommentAutoGenerated" : true - }, - "+%@" : { - "shouldTranslate" : false - }, - "+%@ GiB/Day" : { - "extractionState" : "stale", + "%lld new" : { + "comment" : "A count of new routing events that have not yet been scrolled into view.", + "isCommentAutoGenerated" : true, + "localizations" : { + "ar" : { + "variations" : { + "plural" : { + "zero" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld جديد" + } + }, + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld جديد" + } + }, + "two" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld جديدان" + } + }, + "few" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld جديدة" + } + }, + "many" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld جديدًا" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld جديد" + } + } + } + } + }, + "cs" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld nový" + } + }, + "few" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld nové" + } + }, + "many" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld nové" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nové: %lld" + } + } + } + } + }, + "de" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld neu" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld neu" + } + } + } + } + }, + "el" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld νέο" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld νέα" + } + } + } + } + }, + "en" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld new" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld new" + } + } + } + } + }, + "es" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld nuevo" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld nuevos" + } + } + } + } + }, + "es-419" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld nuevo" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld nuevos" + } + } + } + } + }, + "es-MX" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld nuevo" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld nuevos" + } + } + } + } + }, + "fr" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld nouveau" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld nouveaux" + } + } + } + } + }, + "he" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld חדש" + } + }, + "two" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld חדשים" + } + }, + "many" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld חדשים" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld חדשים" + } + } + } + } + }, + "hi" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld नया" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld नए" + } + } + } + } + }, + "id" : { + "variations" : { + "plural" : { + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld baru" + } + } + } + } + }, + "it" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld nuovo" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld nuovi" + } + } + } + } + }, + "ja" : { + "variations" : { + "plural" : { + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "新着%lld件" + } + } + } + } + }, + "ko" : { + "variations" : { + "plural" : { + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "신규 %lld개" + } + } + } + } + }, + "nl" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld nieuw" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld nieuw" + } + } + } + } + }, + "pl" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld nowy" + } + }, + "few" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld nowe" + } + }, + "many" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld nowych" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nowe: %lld" + } + } + } + } + }, + "pt" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld novo" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld novos" + } + } + } + } + }, + "pt-BR" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld novo" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld novos" + } + } + } + } + }, + "pt-PT" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld novo" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld novos" + } + } + } + } + }, + "ru" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld новый" + } + }, + "few" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld новых" + } + }, + "many" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld новых" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "Новых: %lld" + } + } + } + } + }, + "sv" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld ny" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld nya" + } + } + } + } + }, + "th" : { + "variations" : { + "plural" : { + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "ใหม่ %lld รายการ" + } + } + } + } + }, + "uk" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld новий" + } + }, + "few" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld нові" + } + }, + "many" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld нових" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "Нових: %lld" + } + } + } + } + }, + "vi" : { + "variations" : { + "plural" : { + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld mới" + } + } + } + } + }, + "zh-Hans" : { + "variations" : { + "plural" : { + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld 个新增" + } + } + } + } + }, + "zh-HK" : { + "variations" : { + "plural" : { + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld 個新增" + } + } + } + } + } + } + }, + "%lld split rule%@" : { + "comment" : "A label displaying the number of split rules. The argument is the count of split rules.", + "extractionState" : "stale", + "isCommentAutoGenerated" : true, + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$lld split rule%2$@" + } + } + } + }, + "%lld split rules" : { + "comment" : "A label displaying the number of split rules. The argument is the count of split rules.", + "isCommentAutoGenerated" : true, + "localizations" : { + "ar" : { + "variations" : { + "plural" : { + "zero" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld قاعدة تقسيم" + } + }, + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "قاعدة تقسيم واحدة" + } + }, + "two" : { + "stringUnit" : { + "state" : "translated", + "value" : "قاعدتا تقسيم" + } + }, + "few" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld قواعد تقسيم" + } + }, + "many" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld قاعدة تقسيم" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld قاعدة تقسيم" + } + } + } + } + }, + "cs" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld pravidlo rozdělení" + } + }, + "few" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld pravidla rozdělení" + } + }, + "many" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld pravidla rozdělení" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld pravidel rozdělení" + } + } + } + } + }, + "de" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld Split-Regel" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld Split-Regeln" + } + } + } + } + }, + "el" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld κανόνας διαχωρισμού" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld κανόνες διαχωρισμού" + } + } + } + } + }, + "en" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld split rule" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld split rules" + } + } + } + } + }, + "es" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld regla de división" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld reglas de división" + } + } + } + } + }, + "es-419" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld regla de división" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld reglas de división" + } + } + } + } + }, + "es-MX" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld regla de división" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld reglas de división" + } + } + } + } + }, + "fr" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld règle de séparation" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld règles de séparation" + } + } + } + } + }, + "he" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld כלל פיצול" + } + }, + "two" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld כללי פיצול" + } + }, + "many" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld כללי פיצול" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld כללי פיצול" + } + } + } + } + }, + "hi" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld स्प्लिट नियम" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld स्प्लिट नियम" + } + } + } + } + }, + "id" : { + "variations" : { + "plural" : { + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld aturan pemisahan" + } + } + } + } + }, + "it" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld regola di suddivisione" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld regole di suddivisione" + } + } + } + } + }, + "ja" : { + "variations" : { + "plural" : { + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld件の分割ルール" + } + } + } + } + }, + "ko" : { + "variations" : { + "plural" : { + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "분할 규칙 %lld개" + } + } + } + } + }, + "nl" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld splitsregel" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld splitsregels" + } + } + } + } + }, + "pl" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld reguła podziału" + } + }, + "few" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld reguły podziału" + } + }, + "many" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld reguł podziału" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld reguły podziału" + } + } + } + } + }, + "pt" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld regra de divisão" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld regras de divisão" + } + } + } + } + }, + "pt-BR" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld regra de divisão" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld regras de divisão" + } + } + } + } + }, + "pt-PT" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld regra de divisão" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld regras de divisão" + } + } + } + } + }, + "ru" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld правило разделения" + } + }, + "few" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld правила разделения" + } + }, + "many" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld правил разделения" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld правила разделения" + } + } + } + } + }, + "sv" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld delningsregel" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld delningsregler" + } + } + } + } + }, + "th" : { + "variations" : { + "plural" : { + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "กฎแยกเส้นทาง %lld ข้อ" + } + } + } + } + }, + "uk" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld правило розділення" + } + }, + "few" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld правила розділення" + } + }, + "many" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld правил розділення" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld правила розділення" + } + } + } + } + }, + "vi" : { + "variations" : { + "plural" : { + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld quy tắc phân tách" + } + } + } + } + }, + "zh-Hans" : { + "variations" : { + "plural" : { + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld 条分流规则" + } + } + } + } + }, + "zh-HK" : { + "variations" : { + "plural" : { + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld 條分流規則" + } + } + } + } + } + } + }, + "%lld/5" : { + "comment" : "A label displaying the number of referrals a user has made. The argument is the number of referrals.", + "isCommentAutoGenerated" : true, + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld/5" + } + } + } + }, + "+%@" : { + "shouldTranslate" : false + }, + "+%@ GiB/Day" : { + "extractionState" : "stale", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "+%@ غيغابايت/يوم" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "+%@ GiB/den" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "+%@ GiB/Tag" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "+%@ GiB/ημέρα" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "+%@ GiB/Day" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "+%@ GiB/día" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "+%@ GiB/día" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "+%@ Go/jour" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "+%@ GiB/יום" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "+%@ GiB/दिन" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "+%@ GiB/hari" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "+%@ GiB/giorno" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "+%@ GiB/日" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "+%@ GiB/일" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "+%@ GiB/dag" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "+%@ GiB/dzień" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "+%@ GiB/dia" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "+%@ GiB/dia" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "+%@ ГиБ/день" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "+%@ GiB/dag" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "+%@ GiB/วัน" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "+%@ ГіБ/день" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "+%@ GiB/ngày" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "+%@ GiB/天" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "+%@ GiB/日" + } + } + } + }, + "+%@ USDC" : { + "shouldTranslate" : false + }, + "+%lf GiB/Day" : { + "extractionState" : "stale", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "+%lf غيغابايت/يوم" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "+%lf GiB/den" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "+%lf GiB/Tag" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "+%lf GiB/ημέρα" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "+%lf GiB/Day" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "+%lf GiB/día" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "+%lf GiB/día" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "+%lf Go/jour" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "+%lf GiB/יום" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "+%lf GiB/दिन" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "+%lf GiB/hari" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "+%lf GiB/giorno" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "+%lf GiB/日" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "+%lf GiB/일" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "+%lf GiB/dag" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "+%lf GiB/dzień" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "+%lf GiB/dia" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "+%lf GiB/dia" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "+%lf ГиБ/день" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "+%lf GiB/dag" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "+%lf GiB/วัน" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "+%lf ГіБ/день" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "+%lf GiB/ngày" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "+%lf GiB/天" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "+%lf GiB/日" + } + } + } + }, + "+%lld GiB/Month" : { + "comment" : "Call to action", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "+%lld GiB/شهر" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "+%lld GiB/měsíc" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "+%lld GiB/Monat" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "+%lld GiB/μήνα" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "+%lld GiB/Month" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "+%lld GiB/mes" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "+%lld GiB/mes" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "+%lld GiB/mes" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "+%lld GiB/mois" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "+%lld GiB/חודש" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "+%lld GiB/महीना" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "+%lld GiB/bulan" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "+%lld GiB/mese" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "+%lld GiB/月" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "+%lld GiB/월" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "+%lld GiB/maand" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "+%lld GiB/miesiąc" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "+%lld GiB/mês" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "+%lld GiB/mês" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "+%lld GiB/mês" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "+%lld GiB/месяц" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "+%lld GiB/månad" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "+%lld GiB/เดือน" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "+%lld ГіБ/місяць" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "+%lld GiB/tháng" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "+%lld GiB/月" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "+%lld GiB/月" + } + } + } + }, + "0" : { + "shouldTranslate" : false + }, + "100% open source and transparent" : { + "comment" : "Call to action", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "مفتوح المصدر وشفاف بنسبة100%" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "100% open source a transparentní" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "100 %% Open Source und transparent" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "100% ανοιχτού κώδικα και διαφανές" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "100% open source and transparent" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "100% de código abierto y transparente" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "100% código abierto y transparente" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "100% de código abierto y transparente" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "100 %% open source et transparent" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "100% קוד פתוח ושקוף" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "100% ओपन सोर्स और पारदर्शी" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "100% open source dan transparan" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "100% open source e trasparente" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "100%オープンソースで透明性があります" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "100% 오픈 소스 및 투명성" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "100% open source en transparant" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "W 100% otwarty kod źródłowy i przejrzystość" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "100% open source e transparente" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "100% código aberto e transparente" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "100% open source e transparente" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "100% с открытым исходным кодом и прозрачностью" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "100 % öppen källkod och transparent" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "โอเพ่นซอร์สและโปร่งใส 100%" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "100% відкритий код і прозорість" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "100% mã nguồn mở và minh bạch" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "100% 开源且透明" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "100%開源及透明" + } + } + } + }, + "Account" : { + "comment" : "Account Screen", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "الحساب" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Účet" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Konto" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Λογαριασμός" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Account" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cuenta" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cuenta" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cuenta" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Compte" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "חשבון" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "खाता" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Akun" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Account" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "アカウント" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "계정" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Account" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Konto" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Conta" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Conta" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Conta" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Аккаунт" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Konto" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "บัญชี" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Обліковий запис" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tài khoản" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "帐户" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "帳戶" + } + } + } + }, + "Account points" : { + "comment" : "Call to action", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "نقاط الحساب" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Body na účtu" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kontopunkte" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Πόντοι λογαριασμού" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Account points" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Puntos de la cuenta" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "Puntos de cuenta" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Puntos de la cuenta" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Points du compte" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "נקודות חשבון" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "खाता अंक" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Poin akun" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Punti account" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "アカウントポイント" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "계정 포인트" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Accountpunten" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Punkty konta" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pontos da conta" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pontos da conta" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pontos da conta" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Очки аккаунта" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kontopoäng" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "คะแนนบัญชี" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Бали акаунта" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Điểm tài khoản" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "账户积分" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "帳戶積分" + } + } + } + }, + "Activity" : { + "comment" : "A heading for the list of current block actions.", + "isCommentAutoGenerated" : true, + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "النشاط" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktivita" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktivität" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Δραστηριότητα" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Activity" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Actividad" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "Actividad" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Actividad" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Activité" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "פעילות" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "गतिविधि" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktivitas" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Attività" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "アクティビティ" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "활동" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Activiteit" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktywność" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Atividade" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Atividade" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Atividade" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Активность" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktivitet" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "กิจกรรม" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Активність" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hoạt động" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "活动" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "活動" + } + } + } + }, + "Add referral code" : { + "comment" : "Call to action", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "إضافة رمز الإحالة" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Přidání doporučujícího kódu" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Empfehlungscode hinzufügen" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Προσθήκη κωδικού παραπομπής" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Add referral code" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Añadir código de referencia" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "Agregar código de referencia" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Agregar código de referencia" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ajouter un code de parrainage" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "הוסף קוד הפניה" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "रेफरल कोड जोड़ें" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tambahkan kode referensi" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aggiungi codice di riferimento" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "紹介コードを追加" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "추천 코드 추가" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Referentiecode toevoegen" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dodaj kod polecający" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Adicionar código de referência" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Adicionar código de indicação" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Adicionar código de referência" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Добавить реферальный код" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lägg till referenskod" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "เพิ่มรหัสแนะนำ" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Додати реферальний код" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Thêm mã giới thiệu" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "添加推荐码" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "添加推薦碼" + } + } + } + }, + "Add referral code to earn extra rewards" : { + "comment" : "Call to action", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "أضف رمز الإحالة لكسب مكافآت إضافية" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Přidáním kódu pro doporučení získáte další odměny" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Füge einen Empfehlungscode hinzu, um zusätzliche Belohnungen zu erhalten" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Προσθέστε κωδικό παραπομπής για να κερδίσετε επιπλέον ανταμοιβές" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Add referral code to earn extra rewards" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Añade un código de referencia para ganar recompensas adicionales" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "Agrega un código de referencia para ganar recompensas adicionales" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Agrega un código de referencia para ganar recompensas adicionales" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ajouter un code de parrainage pour gagner des récompenses supplémentaires" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "הוסף קוד הפניה כדי להרוויח תגמולים נוספים" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "अतिरिक्त पुरस्कार पाने के लिए रेफरल कोड जोड़ें" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tambahkan kode referensi untuk mendapatkan hadiah tambahan" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aggiungi un codice di riferimento per guadagnare ricompense extra" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "紹介コードを追加して追加の特典を獲得" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "추가 보상을 얻으려면 추천 코드를 추가하세요" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Voeg een referentiecode toe en verdien extra beloningen" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dodaj kod polecający, aby zdobyć dodatkowe nagrody" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Adicione um código de referência para ganhar recompensas extra" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Adicione um código de indicação para ganhar recompensas extras" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Adicione um código de referência para ganhar recompensas extras" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Добавьте реферальный код, чтобы получить дополнительные награды" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lägg till en referenskod för att tjäna extra belöningar" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "เพิ่มรหัสการแนะนำเพื่อรับรางวัลเสริม" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Додайте реферальний код, щоб отримати додаткові винагороди" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Thêm mã giới thiệu để nhận thêm phần thưởng" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "添加推荐码以获得额外奖励" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "添加推薦碼以獲取額外獎勵" + } + } + } + }, + "Adjust settings to maximize reliability. App runs in the background when connected and on Wi-Fi. Reliability is how many IPs you make continually available to the network" : { + "extractionState" : "stale", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "اضبط الإعدادات لتعظيم الموثوقية. يعمل التطبيق في الخلفية عند الاتصال وعند استخدام الواي فاي. الموثوقية هي عدد عناوين IP التي تجعلها متاحة باستمرار للشبكة." + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Upravte nastavení pro maximalizaci spolehlivosti. Aplikace běží na pozadí, když jste připojeni a na Wi-Fi. Spolehlivost znamená, kolik IP adres trvale zpřístupníte síti." + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Passen Sie die Einstellungen an, um die Zuverlässigkeit zu maximieren. Die App läuft im Hintergrund, wenn Sie verbunden sind und sich im WLAN befinden. Zuverlässigkeit bedeutet, wie viele IPs Sie dem Netzwerk kontinuierlich zur Verfügung stellen." + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ρυθμίστε τις ρυθμίσεις για μέγιστη αξιοπιστία. Η εφαρμογή λειτουργεί στο παρασκήνιο όταν είστε συνδεδεμένοι και σε Wi-Fi. Η αξιοπιστία είναι πόσες διευθύνσεις IP διαθέτετε συνεχώς στο δίκτυο." + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Adjust settings to maximize reliability. App runs in the background when connected and on Wi-Fi. Reliability is how many IPs you make continually available to the network" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ajusta la configuración para maximizar la fiabilidad. La app se ejecuta en segundo plano cuando está conectada y en Wi-Fi. La fiabilidad es cuántas IPs mantienes disponibles continuamente para la red." + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ajusta la configuración para maximizar la fiabilidad. La app se ejecuta en segundo plano cuando está conectada y en Wi-Fi. La fiabilidad es cuántas IPs mantienes disponibles continuamente para la red." + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ajustez les paramètres pour maximiser la fiabilité. L’application fonctionne en arrière-plan lorsque vous êtes connecté et en Wi-Fi. La fiabilité correspond au nombre d’IP que vous rendez continuellement disponibles pour le réseau." + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "התאם את ההגדרות כדי למקסם את האמינות. האפליקציה פועלת ברקע כאשר מחוברים וב-Wi-Fi. אמינות היא כמה כתובות IP אתה משאיר זמינות ברצף לרשת." + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "विश्वसनीयता अधिकतम करने के लिए सेटिंग्स समायोजित करें। ऐप कनेक्टेड और वाई-फाई पर बैकग्राउंड में चलता है। विश्वसनीयता यह है कि आप कितनी IPs नेटवर्क के लिए लगातार उपलब्ध रखते हैं।" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sesuaikan pengaturan untuk memaksimalkan keandalan. Aplikasi berjalan di latar belakang saat terhubung dan menggunakan Wi-Fi. Keandalan adalah berapa banyak IP yang Anda buat tersedia terus-menerus untuk jaringan." + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Regola le impostazioni per massimizzare l’affidabilità. L’app funziona in background quando è connessa e su Wi-Fi. L’affidabilità è il numero di IP che rendi continuamente disponibili alla rete." + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "信頼性を最大化するために設定を調整してください。アプリは接続時やWi-Fi利用時にバックグラウンドで動作します。信頼性とは、ネットワークに継続的に提供するIPの数です。" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "신뢰도를 극대화하려면 설정을 조정하세요. 앱은 연결되어 있고 Wi-Fi에 있을 때 백그라운드에서 실행됩니다. 신뢰도는 네트워크에 지속적으로 제공하는 IP의 수입니다." + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pas de instellingen aan om de betrouwbaarheid te maximaliseren. De app draait op de achtergrond wanneer verbonden en op Wi-Fi. Betrouwbaarheid is hoeveel IP's je continu beschikbaar maakt voor het netwerk." + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dostosuj ustawienia, aby zmaksymalizować niezawodność. Aplikacja działa w tle, gdy jest połączona i korzysta z Wi-Fi. Niezawodność to liczba adresów IP, które stale udostępniasz sieci." + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ajuste as configurações para maximizar a confiabilidade. O app funciona em segundo plano quando conectado e no Wi-Fi. Confiabilidade é quantos IPs você mantém continuamente disponíveis para a rede." + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ajuste as definições para maximizar a fiabilidade. A aplicação funciona em segundo plano quando está ligada e em Wi-Fi. Fiabilidade é o número de IPs que mantém continuamente disponíveis para a rede." + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Настройте параметры для максимальной надежности. Приложение работает в фоновом режиме при подключении и использовании Wi-Fi. Надежность — это количество IP-адресов, которые вы постоянно предоставляете сети." + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Justera inställningarna för att maximera tillförlitligheten. Appen körs i bakgrunden när du är ansluten och på Wi-Fi. Tillförlitlighet är hur många IP-adresser du kontinuerligt gör tillgängliga för nätverket." + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "ปรับการตั้งค่าเพื่อเพิ่มความน่าเชื่อถือ แอปจะทำงานในพื้นหลังเมื่อเชื่อมต่อและใช้ Wi-Fi ความน่าเชื่อถือคือจำนวน IP ที่คุณเปิดให้เครือข่ายใช้งานได้อย่างต่อเนื่อง" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Налаштуйте параметри для максимальної надійності. Додаток працює у фоновому режимі, коли підключений і на Wi-Fi. Надійність — це кількість IP-адрес, які ви постійно надаєте мережі." + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Điều chỉnh cài đặt để tối đa hóa độ tin cậy. Ứng dụng chạy nền khi kết nối và dùng Wi-Fi. Độ tin cậy là số lượng IP bạn liên tục cung cấp cho mạng." + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "调整设置以最大化可靠性。应用在连接和使用 Wi-Fi 时会在后台运行。可靠性是指你持续为网络提供的 IP 数量。" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "調整設定以提升可靠性。應用程式在連接及使用 Wi-Fi 時會於背景運行。可靠性是你持續為網絡提供的 IP 數量。" + } + } + } + }, + "All the content in the world from URnetwork" : { + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "كل المحتوى في العالم من URnetwork" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Veškerý obsah na světě od URnetwork" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Alle Inhalte der Welt von URnetwork" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Όλο το περιεχόμενο του κόσμου από το URnetwork" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "All the content in the world from URnetwork" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Todo el contenido del mundo desde URnetwork" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "Todo el contenido del mundo desde URnetwork" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tout le contenu du monde par URnetwork" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "כל התוכן בעולם מ-URnetwork" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "URnetwork से दुनिया भर की सामग्री" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Semua konten di dunia dari URnetwork" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tutti i contenuti del mondo da URnetwork" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "URnetwork が届ける世界中のコンテンツ" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "URnetwork의 전 세계 콘텐츠" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Alle content ter wereld, van URnetwork" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cała światowa treść z URnetwork" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Todo o conteúdo do mundo do URnetwork" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Todo o conteúdo do mundo da URnetwork" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Весь контент мира от URnetwork" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Allt innehåll i världen från URnetwork" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "เนื้อหาทั้งหมดในโลกจาก URnetwork" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Весь контент світу від URnetwork" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tất cả nội dung trên thế giới từ URnetwork" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "来自 URnetwork 的全球内容" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "來自 URnetwork 的全球內容" + } + } + } + }, + "Allow providing on cellular network" : { + "comment" : "Call to action", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "السماح بالتوفير على شبكة الجوال" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Povolit poskytování na mobilní síti" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bereitstellung über das Mobilfunknetz erlauben" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Να επιτρέπεται η παροχή στο κυψελοειδές δίκτυο" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Allow providing on cellular network" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Permitir proporcionar en la red celular" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "Permitir proveer en red celular" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Permitir proporcionar en la red celular" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Autoriser la fourniture sur le réseau cellulaire" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "אפשר אספקה ברשת סלולרית" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "सेलुलर नेटवर्क पर प्रदान करने की अनुमति दें" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Izinkan menyediakan di jaringan seluler" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Consenti la fornitura su rete cellulare" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "携帯ネットワークでの提供を許可する" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "셀룰러 네트워크에서 제공 허용" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Toestaan op mobiel netwerk" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zezwól na udostępnianie w sieci komórkowej" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Permitir fornecer na rede celular" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Permitir fornecer na rede celular" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Permitir fornecimento na rede móvel" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Разрешить предоставление в сотовой сети" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tillåt tillhandahållande på mobilnät" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "อนุญาตให้ให้บริการบนเครือข่ายมือถือ" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Дозволити надання в стільниковій мережі" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cho phép cung cấp trên mạng di động" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "允许在蜂窝网络上提供" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "允許在蜂窩網絡上提供" + } + } + } + }, + "Allowed" : { + "comment" : "Text for a chip that indicates that the action is allowed.", + "isCommentAutoGenerated" : true, + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "مسموح" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Povoleno" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Erlaubt" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Επιτρεπόμενα" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Allowed" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Permitidas" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "Permitidas" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Permitidas" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Autorisé" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "מותר" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "अनुमत" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Diizinkan" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Consentito" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "許可" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "허용" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Toegestaan" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dozwolone" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Permitidos" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Permitidos" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Permitidos" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Разрешено" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tillåtna" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "อนุญาต" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Дозволено" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Được phép" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "已允许" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "已允許" + } + } + } + }, + "Always" : { + "comment" : "Call to action", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "دائمًا" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vždy" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Immer" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Πάντα" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Always" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Siempre" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "Siempre" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Siempre" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Toujours" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "תמיד" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "हमेशा" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Selalu" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sempre" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "常に" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "항상" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Altijd" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zawsze" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sempre" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sempre" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sempre" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Всегда" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Alltid" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "ตลอดเวลา" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Завжди" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Luôn luôn" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "始终" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "始終" + } + } + } + }, + "Amount" : { + "comment" : "Call to action", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "المبلغ" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Částka" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Betrag" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ποσό" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Amount" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Monto" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cantidad" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Monto" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Montant" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "סכום" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "राशि" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Jumlah" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Importo" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "金額" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "금액" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bedrag" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kwota" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Quantia" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Quantia" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Valor" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Сумма" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Belopp" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "ยอด" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Сума" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Số tiền" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "金额" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "金額" + } + } + } + }, + "Apply bonus" : { + "comment" : "Call to action", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "تطبيق المكافأة" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Použít bonus" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bonus anwenden" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Εφαρμογή μπόνους" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Apply bonus" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aplicar bono" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aplicar bono" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aplicar bono" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Appliquer le bonus" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "החל בונוס" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "बोनस लागू करें" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Terapkan bonus" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Applica bonus" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "ボーナスを適用" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "보너스 적용" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bonus toepassen" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zastosuj bonus" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aplicar bónus" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aplicar bônus" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aplicar bônus" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Применить бонус" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tillämpa bonus" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "รับโบนัส" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Застосувати бонус" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Áp dụng tiền thưởng" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "应用奖励" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "應用獎金" + } + } + } + }, + "Are you sure you want to delete your account?" : { + "comment" : "Call to action", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "هل أنت متأكد أنك تريد حذف حسابك؟" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Opravdu chcete svůj účet odstranit?" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sind Sie sicher, dass Sie Ihr Konto löschen möchten?" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Είστε βέβαιοι ότι θέλετε να διαγράψετε τον λογαριασμό σας;" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Are you sure you want to delete your account?" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "¿Está seguro de que desea eliminar su cuenta?" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "¿Está seguro de que desea eliminar su cuenta?" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "¿Estás seguro de que quieres eliminar tu cuenta?" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Êtes-vous sûr de vouloir supprimer votre compte ?" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "האם אתה בטוח שברצונך למחוק את חשבונך?" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "क्या आप वाकई अपना अकाउंट डिलीट करना चाहते हैं?" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Apakah Anda yakin ingin menghapus akun Anda?" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sei sicuro di voler eliminare il tuo account?" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "本当にアカウントを削除しますか?" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "정말로 계정을 삭제하시겠습니까?" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Weet je zeker dat je je account wilt verwijderen?" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Czy na pewno chcesz usunąć swoje konto?" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tem certeza de que deseja excluir sua conta?" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tem certeza de que deseja excluir a sua conta?" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tem a certeza que pretende eliminar a sua conta?" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Вы уверены, что хотите удалить свой аккаунт?" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Är du säker på att du vill radera ditt konto?" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "คุณยืนยันที่จะลบบัญชีของคุณหรือไม่?" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ви впевнені, що хочете видалити свій обліковий запис?" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bạn có chắc chắn muốn xóa tài khoản của mình không?" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "您确定要删除您的帐户吗?" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "您確定要刪除您的帳戶嗎?" + } + } + } + }, + "Are you sure you want to remove this wallet?" : { + "comment" : "Used in Main/Account/AccountNavStackView.swift.", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "هل أنت متأكد أنك تريد إزالة هذه المحفظة؟" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Opravdu chcete tuto peněženku odstranit?" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sind Sie sicher, dass Sie diese Wallet entfernen möchten?" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Είστε βέβαιοι ότι θέλετε να αφαιρέσετε αυτό το πορτοφόλι;" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Are you sure you want to remove this wallet?" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "¿Estás seguro de que quieres eliminar esta billetera?" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "¿Estás seguro de que quieres eliminar esta billetera?" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Êtes-vous sûr de vouloir supprimer ce portefeuille ?" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "האם אתה בטוח שברצונך להסיר ארנק זה?" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "क्या आप वाकई इस वॉलेट को हटाना चाहते हैं?" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Apakah Anda yakin ingin menghapus dompet ini?" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sei sicuro di voler rimuovere questo portafoglio?" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "このウォレットを削除してもよろしいですか?" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "이 지갑을 삭제하시겠습니까?" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ben je zeker dat je deze wallet wilt verwijderen?" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Czy na pewno chcesz usunąć ten portfel?" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tem certeza que deseja remover esta carteira?" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tem a certeza que pretende remover esta carteira?" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Вы уверены, что хотите удалить этот кошелек?" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Är du säker på att du vill ta bort denna plånbok?" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "คุณแน่ใจหรือไม่ว่าต้องการลบกระเป๋าเงินนี้?" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ви впевнені, що хочете видалити цей гаманець?" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bạn có chắc chắn muốn xóa ví này không?" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "你确定要移除这个钱包吗?" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "你確定要移除這個錢包嗎?" + } + } + } + }, + "Attach logs to feedback (optional)" : { + "comment" : "Call to action", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "إرفاق السجلات مع الملاحظات (اختياري)" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Přiložit protokoly k zpětné vazbě (volitelné)" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Protokolle dem Feedback anhängen (optional)" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Επισυνάψτε τα αρχεία καταγραφής στα σχόλια (προαιρετικό)" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Attach logs to feedback (optional)" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Adjuntar registros al comentario (opcional)" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "Adjuntar registros al comentario (opcional)" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Adjuntar registros al comentario (opcional)" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Joindre les journaux au retour (optionnel)" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "צרף לוגים למשוב (אופציונלי)" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "फीडबैक में लॉग संलग्न करें (वैकल्पिक)" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lampirkan log ke umpan balik (opsional)" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Allega i log al feedback (opzionale)" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "フィードバックにログを添付(任意)" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "피드백에 로그 첨부 (선택 사항)" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Logs toevoegen aan feedback (optioneel)" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dołącz logi do opinii (opcjonalnie)" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Anexar logs ao feedback (opcional)" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Anexar logs ao feedback (opcional)" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Anexar logs ao feedback (opcional)" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Приложить логи к отзыву (необязательно)" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bifoga loggar till feedback (valfritt)" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "แนบไฟล์บันทึกกับข้อเสนอแนะ (ไม่บังคับ)" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Додати логи до відгуку (необов’язково)" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Đính kèm nhật ký vào phản hồi (tùy chọn)" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "附加日志到反馈(可选)" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "附加日誌到回饋(可選)" + } + } + } + }, + "Auth code" : { + "comment" : "Call to action", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "رمز المصادقة" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ověřovací kód" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Authentifizierungscode" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Κωδικός ταυτοποίησης" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Auth code" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Código de autenticación" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "Código de autenticación" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Código de autenticación" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Code d’authentification" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "קוד אימות" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "ऑथ कोड" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kode otentikasi" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Codice di autenticazione" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "認証コード" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "인증 코드" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Auth-code" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kod uwierzytelniający" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Código de autenticação" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Código de autenticação" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Código de autenticação" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Код аутентификации" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Autentiseringskod" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "รหัสยืนยัน" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Код автентифікації" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mã xác thực" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "验证码" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "認證碼" + } + } + } + }, + "Auth Code \n\n%@...%@" : { + "comment" : "A message that displays the first and last six characters of an auth code.", + "isCommentAutoGenerated" : true, + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Auth Code \n\n%1$@...%2$@" + } + } + } + }, + "Auth Code Create" : { + "comment" : "Used in Main/Account/Settings/AuthCodeCreate/AuthCodeCreate.swift.", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "إنشاء رمز المصادقة" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vytvořit autorizační kód" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Auth-Code erstellen" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Δημιουργία κωδικού ταυτοποίησης" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Auth Code Create" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Crear código de autenticación" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "Crear código de autenticación" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Créer un code d'authentification" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "צור קוד אימות" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "प्रमाणीकरण कोड बनाएं" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Buat kode otentikasi" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Crea codice di autenticazione" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "認証コード作成" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "인증 코드 생성" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Authenticatiecode aanmaken" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Utwórz kod uwierzytelniający" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Criar código de autenticação" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Criar código de autenticação" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Создать код авторизации" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Skapa autentiseringskod" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "สร้างรหัสยืนยันตัวตน" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Створити код автентифікації" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tạo mã xác thực" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "创建认证码" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "創建認證碼" + } + } + } + }, + "Auth code login" : { + "comment" : "Guidance for someone who has no code yet. Deliberately PLAIN TEXT with no link and no button: app store rules forbid steering a user to an external purchase, but nothing stops us saying where codes come from. Without this, the sheet asks for a code and gives no way to obtain one.", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "تسجيل الدخول برمز المصادقة" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Přihlášení ověřovacím kódem" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Anmeldung mit Authentifizierungscode" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Σύνδεση με κωδικό ταυτοποίησης" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Auth code login" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Iniciar sesión con código de autenticación" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "Inicio de sesión con código de autenticación" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Iniciar sesión con código de autenticación" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Connexion par code d’authentification" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "התחברות עם קוד אימות" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "ऑथ कोड लॉगिन" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Login dengan kode autentikasi" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Accesso con codice di autenticazione" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "認証コードでログイン" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "인증 코드 로그인" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Inloggen met authenticatiecode" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Logowanie kodem uwierzytelniającym" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Início de sessão com código de autenticação" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Login com código de autenticação" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Login com código de autenticação" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Вход по коду аутентификации" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Inloggning med autentiseringskod" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "เข้าสู่ระบบด้วยรหัสยืนยันตัวตน" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Вхід за кодом авторизації" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Đăng nhập bằng mã xác thực" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "验证码登录" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "認證碼登入" + } + } + } + }, + "Authentication code" : { + "comment" : "Guidance for someone who has no code yet. Deliberately PLAIN TEXT with no link and no button: app store rules forbid steering a user to an external purchase, but nothing stops us saying where codes come from. Without this, the sheet asks for a code and gives no way to obtain one.", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "رمز المصادقة" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ověřovací kód" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Authentifizierungscode" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Κωδικός ταυτοποίησης" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Authentication code" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Código de autenticación" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "Código de autenticación" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Código de autenticación" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Code d’authentification" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "קוד אימות" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "प्रमाणीकरण कोड" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kode autentikasi" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Codice di autenticazione" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "認証コード" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "인증 코드" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Authenticatiecode" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kod uwierzytelniający" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Código de autenticação" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Código de autenticação" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Código de autenticação" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Код аутентификации" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Autentiseringskod" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "รหัสยืนยันตัวตน" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Код авторизації" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mã xác thực" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "验证码" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "認證碼" + } + } + } + }, + "Auto" : { + "comment" : "Guidance for someone who has no code yet. Deliberately PLAIN TEXT with no link and no button: app store rules forbid steering a user to an external purchase, but nothing stops us saying where codes come from. Without this, the sheet asks for a code and gives no way to obtain one.", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "تلقائي" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automaticky" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automatisch" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Αυτόματο" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Auto" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automático" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automático" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automático" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automatique" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "אוטומטי" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "स्वचालित" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Otomatis" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automatico" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "自動" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "자동" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automatisch" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automatycznie" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automático" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automático" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automático" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Авто" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Auto" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "อัตโนมัติ" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Авто" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tự động" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "自动" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "自動" + } + } + } + }, + "Available providers" : { + "comment" : "Used in Main/Connect/ConnectView-iOS.swift.", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "مزودي الخدمة المتاحين" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dostupní poskytovatelé" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Verfügbare Anbieter" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Διαθέσιμοι πάροχοι" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Available providers" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Proveedores disponibles" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "Proveedores disponibles" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fournisseurs disponibles" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "ספקים זמינים" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "उपलब्ध प्रदाता" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Penyedia yang tersedia" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Provider disponibili" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "利用可能なプロバイダー" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "사용 가능한 제공업체" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Beschikbare providers" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dostępni dostawcy" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Provedores disponíveis" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fornecedores disponíveis" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Доступные провайдеры" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tillgängliga leverantörer" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "ผู้ให้บริการที่มีให้เลือก" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Доступні провайдери" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nhà cung cấp khả dụng" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "可用供应商" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "可用供應商" + } + } + } + }, + "Average reliability" : { + "comment" : "Call to action", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "متوسط الموثوقية" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Průměrná spolehlivost" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Durchschnittliche Zuverlässigkeit" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Μέση αξιοπιστία" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Average reliability" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fiabilidad promedio" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fiabilidad media" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fiabilidad promedio" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fiabilité moyenne" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "אמינות ממוצעת" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "औसत विश्वसनीयता" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rata-rata keandalan" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Affidabilità media" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "平均信頼性" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "평균 신뢰성" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gemiddelde betrouwbaarheid" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Średnia niezawodność" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fiabilidade média" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Confiabilidade média" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Confiabilidade média" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Средняя надёжность" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Genomsnittlig tillförlitlighet" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "ความน่าเชื่อถือเฉลี่ย" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Середня надійність" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Độ tin cậy trung bình" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "平均可靠性" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "平均可靠性" + } + } + } + }, + "Average Reliability" : { + "comment" : "Used in Main/Account/Wallets/NetworkReliability/NetworkReliabilityView.swift.", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "متوسط الموثوقية" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Průměrná spolehlivost" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Durchschnittliche Zuverlässigkeit" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Μέση αξιοπιστία" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Average Reliability" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Confiabilidad promedio" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "Confiabilidad promedio" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fiabilité moyenne" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "אמינות ממוצעת" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "औसत विश्वसनीयता" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Keandalan rata-rata" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Affidabilità media" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "平均信頼性" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "평균 신뢰성" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gemiddelde betrouwbaarheid" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Średnia niezawodność" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Confiabilidade média" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Confiabilidade média" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Средняя надежность" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Genomsnittlig tillförlitlighet" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "ความน่าเชื่อถือเฉลี่ย" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Середня надійність" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Độ tin cậy trung bình" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "平均可靠性" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "平均可靠性" + } + } + } + }, + "Balance code" : { + "comment" : "Guidance for someone who has no code yet. Deliberately PLAIN TEXT with no link and no button: app store rules forbid steering a user to an external purchase, but nothing stops us saying where codes come from. Without this, the sheet asks for a code and gives no way to obtain one.", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "رمز الرصيد" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kód zůstatku" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Guthabencode" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Κωδικός υπολοίπου" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Balance code" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Código de saldo" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "Código de saldo" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Código de saldo" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Code de solde" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "קוד יתרה" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "बैलेंस कोड" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kode saldo" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Codice saldo" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "残高コード" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "잔액 코드" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Saldo-code" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kod salda" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Código de saldo" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Código de saldo" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Código de saldo" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Код баланса" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Saldo-kod" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "รหัสยอดเงิน" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Код балансу" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mã số dư" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "余额码" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "餘額碼" + } + } + } + }, + "Balance Codes" : { + "comment" : "Guidance for someone who has no code yet. Deliberately PLAIN TEXT with no link and no button: app store rules forbid steering a user to an external purchase, but nothing stops us saying where codes come from. Without this, the sheet asks for a code and gives no way to obtain one.", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "رموز الرصيد" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kódy zůstatku" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Guthabencodes" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Κωδικοί υπολοίπου" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Balance Codes" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Códigos de saldo" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "Códigos de saldo" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Códigos de saldo" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Codes de solde" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "קודי יתרה" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "बैलेंस कोड्स" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kode saldo" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Codici saldo" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "残高コード" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "잔액 코드" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Saldocodes" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kody salda" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Códigos de saldo" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Códigos de saldo" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Códigos de saldo" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Коды баланса" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Saldo-koder" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "รหัสยอดเงิน" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Коди балансу" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Các mã số dư" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "余额码" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "餘額碼" + } + } + } + }, + "Become a" : { + "comment" : "Used in Shared/Views/UpgradeSubscriptionSheet.swift.", + "localizations" : { + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Staň se" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Become a" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Word een " + } + } + } + }, + "Best available provider" : { + "comment" : "VPN providers list option to select the best available provider", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "أفضل مزود متاح" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nejlepší dostupný poskytovatel" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bester verfügbarer Anbieter" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Καλύτερος διαθέσιμος πάροχος" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Best available provider" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mejor proveedor disponible" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mejor proveedor disponible" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mejor proveedor disponible" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Meilleur fournisseur disponible" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "הספק הזמין הטוב ביותר" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "सर्वश्रेष्ठ उपलब्ध प्रदाता" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Penyedia terbaik yang tersedia" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Miglior provider disponibile" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "最適な利用可能プロバイダー" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "최상의 사용 가능한 제공자" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Beste beschikbare aanbieder" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Najlepszy dostępny dostawca" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Melhor provedor disponível" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Melhor provedor disponível" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Melhor fornecedor disponível" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Лучший доступный провайдер" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bästa tillgängliga leverantör" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "ผู้ให้บริการที่ดีที่สุดที่พร้อมเชื่อมต่อ" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Найкращий доступний провайдер" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nhà cung cấp tốt nhất hiện có" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "最佳可用提供商" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "最佳可用供應商" + } + } + } + }, + "Bittensor" : { + "comment" : "Used in Main/Account/Wallet/WalletView.swift.", + "shouldTranslate" : false + }, + "Bittensor wallets are stored for future use and can't receive payouts yet." : { + "comment" : "Call to action", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "يتم حفظ محافظ Bittensor للاستخدام المستقبلي ولا يمكنها استلام المدفوعات بعد." + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Peněženky Bittensor se ukládají pro budoucí použití a zatím nemohou přijímat výplaty." + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bittensor-Wallets werden für die spätere Nutzung gespeichert und können noch keine Auszahlungen empfangen." + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Τα πορτοφόλια Bittensor αποθηκεύονται για μελλοντική χρήση και δεν μπορούν ακόμη να λάβουν πληρωμές." + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bittensor wallets are stored for future use and can't receive payouts yet." + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Las billeteras de Bittensor se guardan para uso futuro y aún no pueden recibir pagos." + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "Las billeteras de Bittensor se guardan para uso futuro y aún no pueden recibir pagos." + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Las billeteras de Bittensor se guardan para uso futuro y aún no pueden recibir pagos." + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Les portefeuilles Bittensor sont enregistrés pour un usage futur et ne peuvent pas encore recevoir de paiements." + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "ארנקי Bittensor נשמרים לשימוש עתידי ועדיין לא יכולים לקבל תשלומים." + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bittensor वॉलेट भविष्य के उपयोग के लिए सहेजे जाते हैं और अभी भुगतान प्राप्त नहीं कर सकते।" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dompet Bittensor disimpan untuk penggunaan mendatang dan belum bisa menerima pembayaran." + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "I portafogli Bittensor vengono salvati per un uso futuro e non possono ancora ricevere pagamenti." + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bittensorウォレットは将来の利用のために保存されますが、まだ支払いは受け取れません。" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bittensor 지갑은 향후 사용을 위해 저장되며 아직 지급금을 받을 수 없습니다." + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bittensor-wallets worden bewaard voor toekomstig gebruik en kunnen nog geen uitbetalingen ontvangen." + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Portfele Bittensor są zapisywane na przyszłość i nie mogą jeszcze otrzymywać wypłat." + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "As carteiras Bittensor são guardadas para uso futuro e ainda não podem receber pagamentos." + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "As carteiras Bittensor são armazenadas para uso futuro e ainda não podem receber pagamentos." + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "As carteiras Bittensor são guardadas para uso futuro e ainda não podem receber pagamentos." + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Кошельки Bittensor сохраняются для будущего использования и пока не могут получать выплаты." + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bittensor-plånböcker sparas för framtida bruk och kan inte ta emot utbetalningar än." + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "กระเป๋า Bittensor จะถูกบันทึกไว้ใช้ในอนาคต และยังรับยอดจ่ายไม่ได้" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Гаманці Bittensor зберігаються для майбутнього використання і поки не можуть отримувати виплати." + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ví Bittensor được lưu để dùng trong tương lai và chưa thể nhận thanh toán." + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bittensor 钱包会保存以备将来使用,目前还不能接收支付。" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bittensor 錢包會儲存以備日後使用,暫時未能接收支付。" + } + } + } + }, + "Block ads and trackers" : { + "comment" : "dns settings", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "حظر الإعلانات والمتعقبات" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Blokovat reklamy a trackery" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Werbung und Tracker blockieren" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Αποκλεισμός διαφημίσεων και ιχνηλατών" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Block ads and trackers" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bloquear anuncios y rastreadores" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bloquear anuncios y rastreadores" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bloquear anuncios y rastreadores" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bloquer les publicités et les traqueurs" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "חסום פרסומות ורכיבי מעקב" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "विज्ञापन और ट्रैकर ब्लॉक करें" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Blokir iklan dan pelacak" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Blocca pubblicità e tracker" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "広告とトラッカーをブロック" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "광고 및 트래커 차단" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Advertenties en trackers blokkeren" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Blokuj reklamy i trackery" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bloquear anúncios e rastreadores" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bloquear anúncios e rastreadores" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bloquear anúncios e rastreadores" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Блокировать рекламу и трекеры" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Blockera annonser och spårare" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "บล็อกโฆษณาและตัวติดตาม" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Блокувати рекламу та трекери" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Chặn quảng cáo và trình theo dõi" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "拦截广告和跟踪器" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "攔截廣告和追蹤器" + } + } + } + }, + "Blocked" : { + "comment" : "Label for the blocked traffic relayed for remote clients.", + "isCommentAutoGenerated" : true, + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "محظور" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Blokováno" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Blockiert" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Αποκλεισμένα" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Blocked" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bloqueadas" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bloqueadas" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bloqueadas" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bloqué" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "חסום" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "अवरुद्ध" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Diblokir" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bloccato" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "ブロック" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "차단" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Geblokkeerd" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zablokowane" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bloqueados" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bloqueados" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bloqueados" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Заблокировано" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Blockerade" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "ถูกบล็อก" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Заблоковано" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bị chặn" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "已拦截" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "已攔截" + } + } + } + }, + "Blocked location could not be added. Please try again later." : { + "comment" : "Used in Main/Account/BlockedLocations/BlockedLocationsViewModel.swift.", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "تعذّر إضافة الموقع المحظور. يرجى المحاولة مرة أخرى لاحقًا." + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Blokovanou lokalitu se nepodařilo přidat. Zkuste to prosím později." + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Blockierter Standort konnte nicht hinzugefügt werden. Bitte versuche es später erneut." + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Δεν ήταν δυνατή η προσθήκη της αποκλεισμένης τοποθεσίας. Δοκιμάστε ξανά αργότερα." + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Blocked location could not be added. Please try again later." + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "No se ha podido añadir la ubicación bloqueada. Inténtalo de nuevo más tarde." + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "No se pudo agregar la ubicación bloqueada. Inténtalo de nuevo más tarde." + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Impossible d'ajouter l'emplacement bloqué. Veuillez réessayer plus tard." + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "לא ניתן היה להוסיף את המיקום החסום. אנא נסה שוב מאוחר יותר." + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "अवरुद्ध स्थान जोड़ा नहीं जा सका। कृपया बाद में पुनः प्रयास करें।" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lokasi yang diblokir tidak dapat ditambahkan. Silakan coba lagi nanti." + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Impossibile aggiungere la posizione bloccata. Riprova più tardi." + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "ブロックされた場所を追加できませんでした。しばらくしてからもう一度お試しください。" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "차단된 위치를 추가할 수 없습니다. 나중에 다시 시도해 주세요." + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Geblokkeerde locatie kon niet worden toegevoegd. Probeer het later opnieuw." + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nie udało się dodać zablokowanej lokalizacji. Spróbuj ponownie później." + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Não foi possível adicionar a localização bloqueada. Tente novamente mais tarde." + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Não foi possível adicionar a localização bloqueada. Tente novamente mais tarde." + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Не удалось добавить заблокированную локацию. Попробуйте позже." + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Den blockerade platsen kunde inte läggas till. Försök igen senare." + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "ไม่สามารถเพิ่มตำแหน่งที่ถูกบล็อกได้ กรุณาลองใหม่ภายหลัง" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Не вдалося додати заблоковану локацію. Спробуйте пізніше." + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Không thể thêm vị trí bị chặn. Vui lòng thử lại sau." + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "无法添加屏蔽位置。请稍后再试。" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "無法添加屏蔽位置。請稍後再試。" + } + } + } + }, + "Blocked location could not be removed. Please try again later." : { + "comment" : "Used in Main/Account/BlockedLocations/BlockedLocationsViewModel.swift.", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "تعذّر إزالة الموقع المحظور. يرجى المحاولة مرة أخرى لاحقًا." + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Blokovanou lokalitu se nepodařilo odstranit. Zkuste to prosím později." + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Blockierter Standort konnte nicht entfernt werden. Bitte versuche es später erneut." + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Δεν ήταν δυνατή η αφαίρεση της αποκλεισμένης τοποθεσίας. Δοκιμάστε ξανά αργότερα." + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Blocked location could not be removed. Please try again later." + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "No se ha podido eliminar la ubicación bloqueada. Inténtalo de nuevo más tarde." + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "No se pudo eliminar la ubicación bloqueada. Inténtalo de nuevo más tarde." + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Impossible de supprimer l'emplacement bloqué. Veuillez réessayer plus tard." + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "לא ניתן היה להסיר את המיקום החסום. אנא נסה שוב מאוחר יותר." + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "अवरुद्ध स्थान हटाया नहीं जा सका। कृपया बाद में पुनः प्रयास करें।" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lokasi yang diblokir tidak dapat dihapus. Silakan coba lagi nanti." + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Impossibile rimuovere la posizione bloccata. Riprova più tardi." + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "ブロックされた場所を削除できませんでした。しばらくしてからもう一度お試しください。" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "차단된 위치를 제거할 수 없습니다. 나중에 다시 시도해 주세요." + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Geblokkeerde locatie kon niet worden verwijderd. Probeer het later opnieuw." + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nie udało się usunąć zablokowanej lokalizacji. Spróbuj ponownie później." + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Não foi possível remover a localização bloqueada. Tente novamente mais tarde." + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Não foi possível remover a localização bloqueada. Tente novamente mais tarde." + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Не удалось удалить заблокированную локацию. Попробуйте позже." + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Den blockerade platsen kunde inte tas bort. Försök igen senare." + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "ไม่สามารถลบตำแหน่งที่ถูกบล็อกได้ กรุณาลองใหม่ภายหลัง" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Не вдалося видалити заблоковану локацію. Спробуйте пізніше." + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Không thể xóa vị trí bị chặn. Vui lòng thử lại sau." + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "无法移除屏蔽位置。请稍后再试。" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "無法移除屏蔽位置。請稍後再試。" + } + } + } + }, + "Blocked locations" : { + "comment" : "Used in Main/Account/Settings/SettingsForm-iOS.swift.", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "المواقع المحظورة" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Blokované lokality" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Blockierte Standorte" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Αποκλεισμένες τοποθεσίες" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Blocked locations" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ubicaciones bloqueadas" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ubicaciones bloqueadas" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Emplacements bloqués" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "מיקומים חסומים" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "अवरुद्ध स्थान" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lokasi yang diblokir" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Posizioni bloccate" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "ブロックされた場所" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "차단된 위치" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Geblokkeerde locaties" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zablokowane lokalizacje" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Localizações bloqueadas" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Localizações bloqueadas" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Заблокированные локации" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Blockerade platser" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "ตำแหน่งที่ถูกบล็อก" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Заблоковані локації" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vị trí bị chặn" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "被屏蔽的位置" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "被屏蔽的位置" + } + } + } + }, + "Blocked Locations" : { + "comment" : "Call to action", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "المواقع المحجوبة" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Blokované lokality" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Blockierte Standorte" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Αποκλεισμένες τοποθεσίες" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Blocked Locations" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ubicaciones bloqueadas" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ubicaciones bloqueadas" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ubicaciones bloqueadas" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Emplacements bloqués" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "מיקומים חסומים" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "अवरुद्ध स्थान" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lokasi yang Diblokir" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Posizioni bloccate" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "ブロックされた場所" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "차단된 위치" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Geblokkeerde locaties" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zablokowane lokalizacje" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Localizações bloqueadas" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Localizações bloqueadas" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Localizações bloqueadas" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Заблокированные локации" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Blockerade platser" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "ตำแหน่งที่ถูกบล็อก" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Заблоковані локації" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vị trí bị chặn" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "被屏蔽的位置" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "被屏蔽的位置" + } + } + } + }, + "Bonus referral code" : { + "comment" : "Call to action", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "رمز إحالة إضافي" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bonusový doporučovací kód" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bonus-Empfehlungscode" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Μπόνους κωδικός παραπομπής" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bonus referral code" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Código de referido extra" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "Código de referencia de bonificación" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Código de referido extra" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Code de parrainage bonus" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "קוד הפניה בונוס" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "बोनस रेफ़रल कोड" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kode referensi bonus" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Codice di riferimento bonus" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "ボーナス紹介コード" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "보너스 추천 코드" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bonus-verwijzingscode" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bonusowy kod polecający" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Código de referência bónus" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Código de indicação bônus" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Código de referência bônus" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Бонусный реферальный код" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bonusreferenskod" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "รหัสแนะนำโบนัส" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Бонусний реферальний код" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mã giới thiệu thưởng" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "奖励推荐码" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "獎勵推薦碼" + } + } + } + }, + "Bonus referral code copied to clipboard" : { + "comment" : "Snackbar shown after copying the bonus referral code. Used in Shared/Views/Introduction/ParticipateReferView.swift, Main/Account/Settings/SettingsForm-iOS.swift and Main/Account/Settings/SettingsForm-macOS.swift.", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "تم نسخ رمز الإحالة المكافأة إلى الحافظة" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bonusový referální kód zkopírován do schránky" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bonus-Empfehlungscode in die Zwischenablage kopiert" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ο κωδικός παραπομπής μπόνους αντιγράφηκε στο πρόχειρο" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bonus referral code copied to clipboard" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Código de referencia de bonificación copiado al portapapeles" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "Código de referencia de bonificación copiado al portapapeles" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Código de referencia de bonificación copiado al portapapeles" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Code de parrainage bonus copié dans le presse-papiers" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "קוד הפניה בונוס הועתק ללוח" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "बोनस रेफरल कोड क्लिपबोर्ड पर कॉपी किया गया" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kode referensi bonus disalin ke papan klip" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Codice di riferimento bonus copiato negli appunti" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "ボーナス紹介コードをクリップボードにコピーしました" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "보너스 추천 코드가 클립보드에 복사되었습니다" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bonusreferentiecode gekopieerd naar klembord" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Skopiowano bonusowy kod polecający do schowka" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Código de referência de bónus copiado para a área de transferência" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Código de referência bônus copiado para a área de transferência" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Código de referência de bónus copiado para a área de transferência" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Бонусный реферальный код скопирован в буфер обмена" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bonusreferenskod kopierad till urklipp" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "คัดลอกรหัสอ้างอิงโบนัสไปยังคลิปบอร์ดแล้ว" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Бонусний реферальний код скопійовано в буфер обміну" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Đã sao chép mã giới thiệu thưởng vào bộ nhớ tạm" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "奖励推荐码已复制到剪贴板" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "獎勵推薦碼已複製到剪貼簿" + } + } + } + }, + "Boost Your Bandwidth" : { + "comment" : "A title for the introductory usage bar.", + "isCommentAutoGenerated" : true, + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "عزّز عرض النطاق الترددي لديك" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zvyšte svou šířku pásma" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Steigere deine Bandbreite" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ενισχύστε το εύρος ζώνης σας" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Boost Your Bandwidth" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aumenta tu ancho de banda" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aumenta tu ancho de banda" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Boostez votre bande passante" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "הגבר את רוחב הפס שלך" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "अपनी बैंडविड्थ बढ़ाएँ" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tingkatkan Bandwidth Anda" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aumenta la tua larghezza di banda" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "帯域幅を増やそう" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "대역폭 높이기" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vergroot je bandbreedte" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zwiększ przepustowość" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aumente sua largura de banda" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aumente a sua largura de banda" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Увеличьте пропускную способность" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Öka din bandbredd" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "เพิ่มแบนด์วิดท์ของคุณ" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Збільшіть пропускну здатність" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tăng băng thông của bạn" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "提升你的带宽" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "提升你的頻寬" + } + } + } + }, + "Boost Your Bandwidth - Earn More Every Day" : { + "extractionState" : "stale", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "عزّز عرض النطاق الترددي لديك - اربح المزيد كل يوم" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zvyšte svou šířku pásma – vydělávejte více každý den" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Steigern Sie Ihre Bandbreite – verdienen Sie jeden Tag mehr" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ενισχύστε το εύρος ζώνης σας - Κερδίστε περισσότερα κάθε μέρα" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Boost Your Bandwidth - Earn More Every Day" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aumenta tu ancho de banda - Gana más cada día" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aumenta tu ancho de banda - Gana más cada día" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Boostez votre bande passante - Gagnez plus chaque jour" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "הגבר את רוחב הפס שלך - תרוויח יותר כל יום" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "अपना बैंडविड्थ बढ़ाएं - हर दिन अधिक कमाएँ" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tingkatkan bandwidth Anda - Dapatkan lebih banyak setiap hari" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aumenta la tua larghezza di banda - Guadagna di più ogni giorno" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "帯域幅を増やして毎日もっと稼ごう" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "대역폭을 높이고 매일 더 많이 벌기" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vergroot je bandbreedte - Verdien elke dag meer" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zwiększ swoje pasmo – zarabiaj więcej każdego dnia" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aumente sua largura de banda - Ganhe mais todo dia" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aumente a sua largura de banda - Ganhe mais todos os dias" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Увеличьте свою пропускную способность — зарабатывайте больше каждый день" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Öka din bandbredd – tjäna mer varje dag" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "เพิ่มแบนด์วิดท์ของคุณ - รับรายได้มากขึ้นทุกวัน" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Збільшіть свою пропускну здатність — заробляйте більше щодня" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tăng băng thông của bạn - Kiếm nhiều hơn mỗi ngày" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "提升你的带宽——每天赚更多" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "提升你的頻寬 - 每天賺取更多" + } + } + } + }, + "By default you get 10 GiB/month. Here's how you can earn extra bandwidth:" : { + "comment" : "Used in Shared/Views/Introduction/IntroductionUsageBar.swift.", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "بشكل افتراضي، تحصل على 10 غيغابايت/شهر. إليك كيف يمكنك كسب عرض نطاق إضافي:" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ve výchozím nastavení získáte 10 GiB/měsíc. Zde je návod, jak můžete získat další šířku pásma:" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Standardmäßig erhalten Sie 10 GiB/Monat. So können Sie zusätzliche Bandbreite verdienen:" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Από προεπιλογή λαμβάνετε 10 GiB/μήνα. Δείτε πώς μπορείτε να κερδίσετε επιπλέον εύρος ζώνης:" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "By default you get 10 GiB/month. Here's how you can earn extra bandwidth:" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Por defecto recibes 10 GiB/mes. Así puedes ganar ancho de banda extra:" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "Por defecto recibes 10 GiB/mes. Así puedes ganar ancho de banda extra:" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Par défaut, vous obtenez 10 Go/mois. Voici comment gagner de la bande passante supplémentaire :" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "כברירת מחדל תקבל 10 GiB/חודש. כך תוכל להרוויח רוחב פס נוסף:" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "डिफ़ॉल्ट रूप से आपको 10 GiB/माह मिलता है। ऐसे आप अतिरिक्त बैंडविड्थ कमा सकते हैं:" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Secara default Anda mendapatkan 10 GiB/bulan. Berikut cara mendapatkan bandwidth tambahan:" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Di default ricevi 10 GiB/mese. Ecco come puoi guadagnare larghezza di banda extra:" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "デフォルトで毎月10 GiBが付与されます。追加の帯域幅を獲得する方法はこちら:" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "기본적으로 10 GiB/월을 받습니다. 추가 대역폭을 얻는 방법은 다음과 같습니다:" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Standaard krijg je 10 GiB/maand. Zo kun je extra bandbreedte verdienen:" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Domyślnie otrzymujesz 10 GiB/miesiąc. Oto jak możesz zdobyć dodatkowe pasmo:" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Por padrão, você recebe 10 GiB/mês. Veja como ganhar largura de banda extra:" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Por defeito, recebe 10 GiB/mês. Veja como pode ganhar largura de banda extra:" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "По умолчанию вы получаете 10 ГиБ/месяц. Вот как можно заработать дополнительную пропускную способность:" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Som standard får du 10 GiB/månad. Så här kan du tjäna extra bandbredd:" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "โดยปกติคุณจะได้รับ 10 GiB/เดือน นี่คือวิธีรับแบนด์วิดท์เพิ่มเติม:" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "За замовчуванням ви отримуєте 10 ГіБ/місяць. Ось як можна отримати додаткову пропускну здатність:" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mặc định bạn nhận được 10 GiB/tháng. Đây là cách bạn có thể kiếm thêm băng thông:" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "默认每月获得10 GiB。以下是赚取额外带宽的方法:" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "預設每月獲得10 GiB。以下是賺取額外頻寬的方法:" + } + } + } + }, + "By participating in the network and providing bandwidth you can earn rewards." : { + "extractionState" : "stale", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "من خلال المشاركة في الشبكة وتوفير عرض النطاق الترددي، يمكنك كسب المكافآت." + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Účastí v síti a poskytováním šířky pásma můžete získat odměny." + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Durch die Teilnahme am Netzwerk und das Bereitstellen von Bandbreite können Sie Belohnungen verdienen." + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Συμμετέχοντας στο δίκτυο και παρέχοντας εύρος ζώνης μπορείτε να κερδίσετε ανταμοιβές." + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "By participating in the network and providing bandwidth you can earn rewards." + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Al participar en la red y proporcionar ancho de banda puedes ganar recompensas." + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "Al participar en la red y proporcionar ancho de banda puedes ganar recompensas." + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "En participant au réseau et en fournissant de la bande passante, vous pouvez gagner des récompenses." + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "בהשתתפות ברשת ובמתן רוחב פס תוכל להרוויח תגמולים." + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "नेटवर्क में भाग लेकर और बैंडविड्थ प्रदान करके आप पुरस्कार कमा सकते हैं।" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dengan berpartisipasi di jaringan dan menyediakan bandwidth, Anda dapat memperoleh hadiah." + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Partecipando alla rete e fornendo larghezza di banda puoi guadagnare ricompense." + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "ネットワークに参加して帯域幅を提供することで報酬を獲得できます。" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "네트워크에 참여하고 대역폭을 제공하면 보상을 받을 수 있습니다." + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Door deel te nemen aan het netwerk en bandbreedte te leveren kun je beloningen verdienen." + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Uczestnicząc w sieci i udostępniając przepustowość, możesz zdobywać nagrody." + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ao participar da rede e fornecer largura de banda, você pode ganhar recompensas." + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ao participar na rede e fornecer largura de banda, pode ganhar recompensas." + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Участвуя в сети и предоставляя пропускную способность, вы можете получать награды." + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Genom att delta i nätverket och tillhandahålla bandbredd kan du tjäna belöningar." + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "โดยเข้าร่วมเครือข่ายและให้แบนด์วิดท์ คุณสามารถรับรางวัลได้" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Беріть участь у мережі та надавайте пропускну здатність, щоб отримувати винагороди." + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tham gia mạng lưới và cung cấp băng thông, bạn có thể nhận phần thưởng." + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "参与网络并提供带宽即可获得奖励。" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "參與網絡並提供頻寬即可獲得獎勵。" + } + } + } + }, + "By subscribing, you agree to URnetwork's [Terms and Services](https://ur.io/terms) and [Privacy Policy](https://ur.io/privacy)" : { + "comment" : "Used in Shared/Views/UpgradeSubscriptionSheet.swift.", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "بالاشتراك ، أنت توافق على [شروط خدمة] URnetwork (https://ur.io/terms) و [سياسة الخصوصية] (https://ur.io/privacy)" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Přihlášením k odběru souhlasíte s [Podmínkami a službami](https://ur.io/terms) a [Zásadami ochrany osobních údajů](https://ur.io/privacy) společnosti URnetwork." + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mit dem Abonnieren stimmen Sie den [Nutzungsbedingungen](https://ur.io/terms) und der [Datenschutzrichtlinie](https://ur.io/privacy) von URnetwork zu." + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Με την εγγραφή σας, συμφωνείτε με τους [Όρους και τις Υπηρεσίες](https://ur.io/terms) και την [Πολιτική Απορρήτου](https://ur.io/privacy) της URnetwork" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "By subscribing, you agree to URnetwork's [Terms and Services](https://ur.io/terms) and [Privacy Policy](https://ur.io/privacy)" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Al suscribirte, aceptas los [Términos y servicios](https://ur.io/terms) y la [Política de privacidad](https://ur.io/privacy) de URnetwork" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "Al suscribirte, aceptas los [Términos y servicios](https://ur.io/terms) y la [Política de privacidad](https://ur.io/privacy) de URnetwork" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "En vous abonnant, vous acceptez les [Conditions d'utilisation](https://ur.io/terms) et la [Politique de confidentialité](https://ur.io/privacy) d'URnetwork" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "על ידי הרשמה כמנוי, אתה מסכים ל[תנאים והשירותים](https://ur.io/terms) ול[מדיניות הפרטיות](https://ur.io/privacy) של URnetwork" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "सदस्यता लेने पर, आप URnetwork के [नियम और सेवाएँ](https://ur.io/terms) और [गोपनीयता नीति](https://ur.io/privacy) से सहमत हैं" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dengan berlangganan, Anda menyetujui [Persyaratan dan Layanan](https://ur.io/terms) dan [Kebijakan Privasi](https://ur.io/privacy) URnetwork" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Con l'iscrizione, accetti i [Termini di servizio](https://ur.io/terms) e l'[Informativa sulla privacy](https://ur.io/privacy) di URnetwork" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "サブスクライブすると、URnetworkの[利用規約](https://ur.io/terms)および[プライバシーポリシー](https://ur.io/privacy)に同意したことになります" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "구독하면 URnetwork의 [이용약관](https://ur.io/terms) 및 [개인정보 보호정책](https://ur.io/privacy)에 동의하는 것입니다." + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Door je te abonneren, ga je akkoord met de [Algemene Voorwaarden](https://ur.io/terms) en [Privacybeleid](https://ur.io/privacy) van URnetwork" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Subskrybując, zgadzasz się na [Warunki świadczenia usług](https://ur.io/terms) i [Politykę prywatności](https://ur.io/privacy) URnetwork" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ao se inscrever, você concorda com os [Termos e Serviços](https://ur.io/terms) e a [Política de Privacidade](https://ur.io/privacy) do URnetwork" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ao subscrever, concorda com os [Termos e Serviços](https://ur.io/terms) e a [Política de Privacidade](https://ur.io/privacy) da URnetwork" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Подписываясь, вы соглашаетесь с [Условиями обслуживания](https://ur.io/terms) и [Политикой конфиденциальности](https://ur.io/privacy) URnetwork" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Genom att prenumerera godkänner du URnetworks [Användarvillkor](https://ur.io/terms) och [Integritetspolicy](https://ur.io/privacy)" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "ในการสมัครสมาชิก แสดงว่าคุณยอมรับ [ข้อกำหนดในการให้บริการ](https://ur.io/terms) และ [นโยบายความเป็นส่วนตัว](https://ur.io/privacy) ของ URnetwork" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Підписуючись, ви погоджуєтесь з [Умовами надання послуг](https://ur.io/terms) та [Політикою конфіденційності](https://ur.io/privacy) URnetwork" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bằng cách đăng ký, bạn đồng ý với [Điều khoản và Dịch vụ](https://ur.io/terms) và [Chính sách Bảo mật](https://ur.io/privacy) của URnetwork" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "订阅即表示您同意 URnetwork 的[条款和服务](https://ur.io/terms)和[隐私政策](https://ur.io/privacy)" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "訂閱即表示您同意 URnetwork 的[條款和服務](https://ur.io/terms)和[隱私政策](https://ur.io/privacy)" + } + } + } + }, + "Cancel" : { + "comment" : "Call to action", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "ألغِ" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zrušit" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Brich ab" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ακύρωση" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cancel" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cancela" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cancelar" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cancela" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Annule" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "ביטול" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "रद्द करें" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Batal" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Annulla" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "キャンセル" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "취소" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Annuleren" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Anuluj" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cancele" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cancelar" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cancelar" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Отмена" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Avbryt" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "ยกเลิก" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Скасувати" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hủy" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "取消" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "取消" + } + } + } + }, + "Change" : { + "comment" : "Call to action", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "غيّر" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Změnit" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ändere" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Αλλαγή" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Change" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cambia" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cambiar" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cambia" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Change" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "שנה" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "बदलें" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ubah" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cambia" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "変更" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "변경" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Veranderen" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zmień" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Altere" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Alterar" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Alterar" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Изменить" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ändra" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "เปลี่ยน" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Змінити" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Thay đổi" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "更改" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "更改" + } + } + } + }, + "Check my IP" : { + "comment" : "Call to action", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "تحقق من عنوان IP الخاص بي" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zkontrolovat mou IP" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Meine IP prüfen" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Έλεγχος της διεύθυνσης IP μου" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Check my IP" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Verificar mi IP" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "Comprobar mi IP" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Verificar mi IP" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vérifier mon IP" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "בדוק את ה-IP שלי" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "मेरा IP जांचें" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Periksa IP saya" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Controlla il mio IP" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "自分のIPを確認" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "내 IP 확인" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Controleer mijn IP" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sprawdź mój IP" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Verificar o meu IP" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Verificar meu IP" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Verificar o meu IP" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Проверить мой IP" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kontrollera min IP" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "ตรวจสอบ IP ของฉัน" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Перевірити мою IP" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kiểm tra IP của tôi" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "检查我的 IP" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "檢查我的 IP" + } + } + } + }, + "Check your phone" : { + "comment" : "Call to action", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "تحقق من هاتفك" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zkontrolujte telefon" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Überprüfe dein Telefon" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ελέγξτε το τηλέφωνό σας" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Check your phone" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Verifica tu teléfono" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "Revisa tu teléfono" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Verifica tu teléfono" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vérifiez votre téléphone" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "בדוק את הטלפון שלך" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "अपना फ़ोन जांचें" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Periksa ponsel Anda" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Controlla il tuo telefono" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "携帯電話を確認してください" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "휴대폰을 확인하세요" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Controleer je telefoon" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sprawdź swój telefon" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Verifique o seu telefone" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Verifique seu telefone" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Verifique o seu telemóvel" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Проверьте свой телефон" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kontrollera din telefon" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "ตรวจสอบมือถือของคุณ" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Перевірте свій телефон" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kiểm tra điện thoại của bạn" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "检查你的手机" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "檢查您的手機" + } + } + } + }, + "Claim multiplier" : { + "comment" : "Call to action", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "المطالبة بالمضاعف" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nárokujte si násobej" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Multiplikator beanspruchen" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Αξιώστε τον πολλαπλασιαστή" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Claim Multiplier" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Reclamar multiplicador" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "Multiplicador de reclamo" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Reclamar multiplicador" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Réclamer le multiplicateur" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "מכפיל תביעה" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "गुणक का दावा करें" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pengganda Klaim" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Moltiplicatore di richiesta" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "乗数を受け取る" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "배율 받기" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Claim je vermenigvuldiger" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Odbierz mnożnik" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Reivindicar Multiplicador" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Reivindicar Multiplicador" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Multiplicador de reivindicação" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Получить множитель" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kravmultiplikator" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "รับสิทธิ์ตัวคูณ" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Множник вимог" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hệ số nhân yêu cầu" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "领取倍数" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "申領倍數" + } + } + } + }, + "Client contracts" : { + "comment" : "transfer stats and split rules", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "عقود العميل" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kontrakty klienta" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Client-Verträge" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Συμβόλαια πελάτη" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Client contracts" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Contratos de cliente" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "Contratos de cliente" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Contratos de cliente" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Contrats client" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "חוזי לקוח" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "क्लाइंट कॉन्ट्रैक्ट" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kontrak klien" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Contratti client" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "クライアント契約" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "클라이언트 계약" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Clientcontracten" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kontrakty klienta" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Contratos do cliente" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Contratos do cliente" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Contratos do cliente" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Контракты клиента" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Klientkontrakt" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "สัญญาไคลเอ็นต์" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Контракти клієнта" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hợp đồng máy khách" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "客户端合约" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "客戶端合約" + } + } + } + }, + "Client ID" : { + "comment" : "Used in Main/Account/Settings/SettingsForm-iOS.swift.", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "معرّف العميل" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "ID klienta" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Client-ID" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "ID πελάτη" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Client ID" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "ID de cliente" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "ID de cliente" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "ID client" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "מזהה לקוח" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "क्लाइंट ID" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "ID klien" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "ID client" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "クライアントID" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "클라이언트 ID" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Client-ID" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "ID klienta" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "ID do cliente" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "ID do cliente" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "ID клиента" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Klient-ID" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "ID ไคลเอ็นต์" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "ID клієнта" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "ID máy khách" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "客户端 ID" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "客戶端 ID" + } + } + } + }, + "Client ID copied" : { + "comment" : "transfer stats and split rules", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "تم نسخ معرّف العميل" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "ID klienta zkopírováno" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Client-ID kopiert" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Το ID πελάτη αντιγράφηκε" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Client ID copied" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "ID de cliente copiado" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "ID de cliente copiado" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "ID de cliente copiado" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "ID client copié" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "מזהה הלקוח הועתק" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "क्लाइंट ID कॉपी किया गया" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "ID klien disalin" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "ID client copiato" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "クライアントIDをコピーしました" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "클라이언트 ID가 복사되었습니다" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Client-ID gekopieerd" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Skopiowano ID klienta" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "ID do cliente copiado" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "ID do cliente copiado" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "ID do cliente copiado" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "ID клиента скопирован" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Klient-ID kopierat" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "คัดลอก ID ไคลเอ็นต์แล้ว" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "ID клієнта скопійовано" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Đã sao chép ID máy khách" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "客户端 ID 已复制" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "客戶端 ID 已複製" + } + } + } + }, + "Client ID copied to clipboard" : { + "comment" : "Used in Main/Account/Settings/SettingsForm-iOS.swift.", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "تم نسخ معرّف العميل إلى الحافظة" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "ID klienta zkopírováno do schránky" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Client-ID in die Zwischenablage kopiert" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Το ID πελάτη αντιγράφηκε στο πρόχειρο" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Client ID copied to clipboard" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "ID de cliente copiado al portapapeles" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "ID de cliente copiado al portapapeles" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "ID client copié dans le presse-papiers" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "מזהה הלקוח הועתק ללוח" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "क्लाइंट ID क्लिपबोर्ड पर कॉपी किया गया" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "ID klien disalin ke papan klip" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "ID client copiato negli appunti" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "クライアントIDをクリップボードにコピーしました" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "클라이언트 ID가 클립보드에 복사되었습니다" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Client-ID gekopieerd naar klembord" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Skopiowano ID klienta do schowka" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "ID do cliente copiado para a área de transferência" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "ID do cliente copiado para a área de transferência" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "ID клиента скопирован в буфер обмена" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Klient-ID kopierat till urklipp" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "คัดลอก ID ไคลเอ็นต์ไปยังคลิปบอร์ดแล้ว" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "ID клієнта скопійовано в буфер обміну" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Đã sao chép ID máy khách vào bộ nhớ tạm" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "客户端 ID 已复制到剪贴板" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "客戶端 ID 已複製到剪貼簿" + } + } + } + }, + "Client statistics" : { + "comment" : "Title of a card that shows statistics about the client's network usage.", + "isCommentAutoGenerated" : true, + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "إحصائيات العميل" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Statistiky klienta" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Client-Statistiken" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Στατιστικά πελάτη" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Client statistics" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Estadísticas del cliente" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "Estadísticas del cliente" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Estadísticas del cliente" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Statistiques client" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "סטטיסטיקת לקוח" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "क्लाइंट आँकड़े" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Statistik klien" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Statistiche client" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "クライアント統計" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "클라이언트 통계" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Clientstatistieken" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Statystyki klienta" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Estatísticas do cliente" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Estatísticas do cliente" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Estatísticas do cliente" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Статистика клиента" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Klientstatistik" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "สถิติไคลเอ็นต์" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Статистика клієнта" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Thống kê máy khách" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "客户端统计" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "客戶端統計" + } + } + } + }, + "Clients" : { + "comment" : "Call to action", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "العملاء" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Klienti" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Clients" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Πελάτες" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Clients" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Clientes" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "Clientes" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Clientes" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Clients" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "לקוחות" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "क्लाइंट्स" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Klien" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Client" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "クライアント" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "클라이언트" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Clients" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Klienci" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Clientes" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Clientes" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Clientes" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Клиенты" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Klienter" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "ไคลเอ็นต์" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Клієнти" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Máy khách" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "客户端" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "客戶端" + } + } + } + }, + "Close" : { + "comment" : "Billing errors. Every one is shown to the user WITH a remedy: a payment error they cannot see is a payment error they will hit again.", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "إغلاق" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zavřít" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Schließen" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Κλείσιμο" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Close" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cerrar" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cerrar" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cerrar" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fermer" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "סגור" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "बंद करें" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tutup" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Chiudi" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "閉じる" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "닫기" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sluiten" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zamknij" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fechar" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fechar" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fechar" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Закрыть" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Stäng" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "ปิด" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Закрити" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Đóng" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "关闭" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "關閉" + } + } + } + }, + "Code" : { + "comment" : "Used in Main/Account/TransferBalanceCodes/TransferBalanceCodesView.swift.", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "الرمز" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kód" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Code" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Κωδικός" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Code" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Código" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "Código" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Code" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "קוד" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "कोड" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kode" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Codice" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "コード" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "코드" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Code" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kod" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Código" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Código" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Код" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kod" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "รหัส" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Код" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mã" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "代码" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "代碼" + } + } + } + }, + "Commitment issues?" : { + "comment" : "Login initial", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "مشاكل في الالتزام؟" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Problémy s angažovaností?" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bindungsprobleme?" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Προβλήματα δέσμευσης;" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Commitment issues?" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "¿Problemas de compromiso?" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "¿Miedo al compromiso?" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "¿Problemas para comprometerte?" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Problèmes d'engagement ?" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "חושש להתחייב?" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "प्रतिबद्धता संबंधी समस्याएं?" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Masih ragu?" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Paura di impegnarsi?" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "やることを決めかねていますか?" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "결정하기 어려우신가요?" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Problemen met commitment?" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Problemy z zobowiązaniem?" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Problemas de compromisso?" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Problemas de compromisso?" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Receio de compromisso?" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Не хотите регистрироваться?" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Svårt att bestämma dig?" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "ยังตัดสินใจไม่ได้ใช่ไหม?" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Вагаєтесь?" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Còn phân vân?" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "承诺问题?" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "猶豫不決?" + } + } + } + }, + "Community Edition" : { + "comment" : "Call to action", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "إصدار المجتمع" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Komunitní edice" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Community Edition" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Έκδοση κοινότητας" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Community Edition" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Edición comunitaria" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "Edición comunitaria" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Edición comunitaria" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Édition communautaire" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "מהדורת קהילה" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "कम्युनिटी एडिशन" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Edisi Komunitas" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Edizione Community" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "コミュニティ版" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "커뮤니티 에디션" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Community-editie" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Edycja społecznościowa" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Edição comunitária" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Edição comunitária" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Edição Comunitária" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Версия для сообщества" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Community Edition" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "ฉบับชุมชน" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Спільнотна версія" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Phiên bản cộng đồng" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "社区版" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "社群版" + } + } + } + }, + "Companion" : { + "comment" : "A label displayed next to a companion contract.", + "isCommentAutoGenerated" : true, + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "الطرف المقابل" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Protějšek" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gegenstelle" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Συμβαλλόμενος" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Companion" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Compañero" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "Compañero" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Compañero" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Partenaire" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "הצד השני" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "साथी" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rekan" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Partner" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "相手" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "상대방" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tegenpartij" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Partner" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Parceiro" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Parceiro" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Parceiro" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Партнёр" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Motpart" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "คู่สัญญา" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Партнер" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Đối tác" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "对端" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "對端" + } + } + } + }, + "Confirm Seeker Token" : { + "comment" : "Used in Main/Account/Settings/SettingsView.swift.", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "تأكيد رمز Seeker" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Potvrďte Seeker Token" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Seeker-Token bestätigen" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Επιβεβαίωση Token Seeker" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Confirm Seeker Token" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Confirmar Token de Seeker" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "Confirmar Token de Seeker" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Confirmer le jeton Seeker" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "אמת אסימון Seeker" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "सीकर टोकन की पुष्टि करें" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Konfirmasi Token Seeker" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Conferma Token Seeker" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "シーカートークンを確認" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "Seeker 토큰 확인" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bevestig Seeker Token" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Potwierdź token Seeker" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Confirmar Token Seeker" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Confirmar Token Seeker" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Подтвердить токен Seeker" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bekräfta Seeker-token" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "ยืนยัน Seeker Token" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Підтвердити токен Seeker" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Xác nhận Token Seeker" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "确认 Seeker 令牌" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "確認 Seeker 代幣" + } + } + } + }, + "Connect" : { + "comment" : "Button text to connect to URnetwork VPN", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "اربط" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Připojit" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Verbinde" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Σύνδεση" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Connect" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Conecta" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "Conectar" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Conecta" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Connecte" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "התחבר" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "कनेक्ट करें" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hubungkan" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Connetti" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "接続" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "연결" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Verbinden" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Połącz" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Conecte" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Conectar" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ligar" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Подключить" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Anslut" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "เชื่อมต่อ" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Підключитися" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kết nối" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "连接" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "連接" + } + } + } + }, + "Connect a wallet" : { + "comment" : "Used in Main/Account/Settings/SettingsForm-iOS.swift.", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "ربط محفظة" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Připojit peněženku" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Verbinde eine Wallet" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Συνδέστε ένα πορτοφόλι" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Connect a wallet" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Conecta una billetera" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "Conecta una billetera" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Connectez un portefeuille" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "חבר ארנק" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "एक वॉलेट कनेक्ट करें" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hubungkan dompet" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Collega un wallet" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "ウォレットを接続" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "지갑 연결" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Verbind een wallet" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Połącz portfel" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Conecte uma carteira" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Conecte uma carteira" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Подключите кошелек" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Anslut en plånbok" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "เชื่อมต่อกระเป๋าเงิน" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Підключіть гаманець" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kết nối ví" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "连接钱包" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "連接錢包" + } + } + } + }, + "Connect a wallet with the Seeker Pre-Order Token" : { + "comment" : "Used in Main/Account/Settings/SettingsForm-iOS.swift.", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "ربط محفظة برمز طلب Seeker المسبق" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Připojit peněženku s Seeker Pre-Order tokenem" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Verbinde eine Wallet mit dem Seeker Pre-Order Token" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Συνδέστε ένα πορτοφόλι με το Seeker Pre-Order Token" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Connect a wallet with the Seeker Pre-Order Token" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Conecta una billetera con el token de pedido anticipado de Seeker" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "Conecta una billetera con el token de pedido anticipado de Seeker" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Connectez un portefeuille avec le jeton de précommande Seeker" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "חבר ארנק עם אסימון הזמנה מוקדמת של Seeker" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Seeker प्री-ऑर्डर टोकन के साथ एक वॉलेट कनेक्ट करें" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hubungkan dompet dengan Token Pra-Pemesanan Seeker" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Collega un wallet con il token di pre-ordine Seeker" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "Seekerプレオーダートークンでウォレットを接続" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "Seeker 선주문 토큰으로 지갑 연결" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Verbind een wallet me een Seeker Pre-Order Token" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Połącz portfel z tokenem zamówienia przedpremierowego Seeker" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Conecte uma carteira com o token de pré-venda Seeker" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Conecte uma carteira com o token de pré-venda Seeker" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Подключите кошелек с токеном предзаказа Seeker" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Anslut en plånbok med Seeker Förbeställnings-token" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "เชื่อมต่อกระเป๋าเงินกับ Seeker Pre-Order Token" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Підключіть гаманець за допомогою токена попереднього замовлення Seeker" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kết nối ví với Mã thông báo Đặt hàng trước Seeker" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "连接带有 Seeker 预售代币的钱包" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "連接帶有 Seeker 預售代幣的錢包" + } + } + } + }, + "Connect external wallet" : { + "comment" : "Wallets Screen", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "ربط محفظة خارجية" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Připojit externí peněženku" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Externe Wallet verbinden" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Σύνδεση εξωτερικού πορτοφολιού" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Connect external wallet" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Conectar una billetera externa" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "Conectar billetera" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Conectar una cartera externa" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Connecter un portefeuille externe" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "חבר ארנק" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "बाहरी वॉलेट कनेक्ट करें" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hubungkan Dompet" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Connetti il portafoglio" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "外部ウォレットを接続" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "외부 지갑 연결" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Externe wallet verbinden" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Połącz zewnętrzny portfel" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Conectar carteira externa" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Conectar carteira externa" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Conectar carteira" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Подключить внешний кошелек" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Anslut plånbok" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "เชื่อมต่อกระเป๋า" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Підключити гаманець" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kết nối ví" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "连接外部钱包" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "連接錢包" + } + } + } + }, + "Connect options" : { + "comment" : "A label displayed above the options for connecting to a tunnel.", + "isCommentAutoGenerated" : true, + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "خيارات الاتصال" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Možnosti připojení" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Verbindungsoptionen" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Επιλογές σύνδεσης" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Connect options" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Opciones de conexión" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "Opciones de conexión" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Options de connexion" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "אפשרויות חיבור" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "कनेक्शन विकल्प" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Opsi koneksi" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Opzioni di connessione" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "接続オプション" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "연결 옵션" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Verbindingsopties" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Opcje połączenia" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Opções de conexão" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Opções de conexão" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Параметры подключения" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Anslutningsalternativ" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "ตัวเลือกการเชื่อมต่อ" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Параметри підключення" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tùy chọn kết nối" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "连接选项" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "連接選項" + } + } + } + }, + "Connect URnetwork VPN" : { + "comment" : "Used in Shared/Intents/ConnectIntent.swift.", + "localizations" : { + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Připojit se k URnetwork VPN" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Connect URnetwork VPN" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Verbind URnetwork VPN" + } + } + } + }, + "Connect Wallet" : { + "comment" : "Used in Main/Account/Wallets/ConnectWalletSheet/EnterWalletAddress/EnterWalletAddressView.swift.", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "ربط المحفظة" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Připojit peněženku" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Wallet verbinden" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Σύνδεση πορτοφολιού" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Connect Wallet" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Conectar billetera" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "Conectar billetera" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Connecter le portefeuille" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "חבר ארנק" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "वॉलेट कनेक्ट करें" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hubungkan Dompet" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Connetti il portafoglio" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "ウォレットを接続" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "지갑 연결" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Verbind Wallet" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Połącz portfel" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Conectar carteira" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Conectar carteira" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Подключить кошелек" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Anslut plånbok" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "เชื่อมต่อกระเป๋าเงิน" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Підключити гаманець" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kết nối ví" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "连接钱包" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "連接錢包" + } + } + } + }, + "Connected" : { + "comment" : "Used in Main/Connect/ConnectButton/StatusIndicator/ConnectStatusIndicator.swift.", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "متصل" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Připojeno" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Verbunden" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Συνδεδεμένο" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Connected" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Conectado" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "Conectado" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Connecté" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "מחובר" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "कनेक्टेड" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Terhubung" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Connesso" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "接続済み" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "연결됨" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Verbonden" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Połączono" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Conectado" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Conectado" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Подключено" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ansluten" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "เชื่อมต่อแล้ว" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Підключено" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Đã kết nối" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "已连接" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "已連接" + } + } + } + }, + "Connected to %d %@" : { + "comment" : "A message indicating that the device is connected to a certain number of providers. The argument is the number of providers.", + "extractionState" : "stale", + "isCommentAutoGenerated" : true, + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Connected to %1$d %2$@" + } + } + } + }, + "Connected to %d providers" : { + "comment" : "A message indicating that the device is connected to a certain number of providers. The argument is the number of providers.", + "isCommentAutoGenerated" : true, + "localizations" : { + "ar" : { + "variations" : { + "plural" : { + "zero" : { + "stringUnit" : { + "state" : "translated", + "value" : "متصل بـ %d من مزودي الخدمة" + } + }, + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "متصل بمزود خدمة واحد" + } + }, + "two" : { + "stringUnit" : { + "state" : "translated", + "value" : "متصل بمزودَي خدمة" + } + }, + "few" : { + "stringUnit" : { + "state" : "translated", + "value" : "متصل بـ %d من مزودي الخدمة" + } + }, + "many" : { + "stringUnit" : { + "state" : "translated", + "value" : "متصل بـ %d من مزودي الخدمة" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "متصل بـ %d من مزودي الخدمة" + } + } + } + } + }, + "cs" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "Připojení k %d poskytovateli" + } + }, + "few" : { + "stringUnit" : { + "state" : "translated", + "value" : "Připojení k %d poskytovatelům" + } + }, + "many" : { + "stringUnit" : { + "state" : "translated", + "value" : "Připojení k %d poskytovatelům" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "Připojení k %d poskytovatelům" + } + } + } + } + }, + "de" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mit %d Anbieter verbunden" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mit %d Anbietern verbunden" + } + } + } + } + }, + "el" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "Συνδεδεμένο με %d πάροχο" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "Συνδεδεμένο με %d παρόχους" + } + } + } + } + }, + "en" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "Connected to %d provider" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "Connected to %d providers" + } + } + } + } + }, + "es" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "Conectado a %d proveedor" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "Conectado a %d proveedores" + } + } + } + } + }, + "es-419" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "Conectado a %d proveedor" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "Conectado a %d proveedores" + } + } + } + } + }, + "es-MX" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "Conectado a %d proveedor" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "Conectado a %d proveedores" + } + } + } + } + }, + "fr" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "Connecté à %d fournisseur" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "Connecté à %d fournisseurs" + } + } + } + } + }, + "he" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "מחובר לספק אחד" + } + }, + "two" : { + "stringUnit" : { + "state" : "translated", + "value" : "מחובר ל-%d ספקים" + } + }, + "many" : { + "stringUnit" : { + "state" : "translated", + "value" : "מחובר ל-%d ספקים" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "מחובר ל-%d ספקים" + } + } + } + } + }, + "hi" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "%d प्रदाता से कनेक्ट किया गया" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%d प्रदाताओं से कनेक्ट किया गया" + } + } + } + } + }, + "id" : { + "variations" : { + "plural" : { + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "Terhubung ke %d penyedia" + } + } + } + } + }, + "it" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "Connesso a %d provider" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "Connesso a %d provider" + } + } + } + } + }, + "ja" : { + "variations" : { + "plural" : { + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%d プロバイダーに接続済み" + } + } + } + } + }, + "ko" : { + "variations" : { + "plural" : { + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%d개의 제공업체에 연결됨" + } + } + } + } + }, + "nl" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "Verbonden met %d provider" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "Verbonden met %d providers" + } + } + } + } + }, + "pl" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "Połączono z %d dostawcą" + } + }, + "few" : { + "stringUnit" : { + "state" : "translated", + "value" : "Połączono z %d dostawcami" + } + }, + "many" : { + "stringUnit" : { + "state" : "translated", + "value" : "Połączono z %d dostawcami" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "Połączono z %d dostawcami" + } + } + } + } + }, + "pt" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ligado a %d fornecedor" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ligado a %d fornecedores" + } + } + } + } + }, + "pt-BR" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "Conectado a %d provedor" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "Conectado a %d provedores" + } + } + } + } + }, + "pt-PT" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "Conectado a %d fornecedor" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "Conectado a %d fornecedores" + } + } + } + } + }, + "ru" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "Подключено к %d провайдеру" + } + }, + "few" : { + "stringUnit" : { + "state" : "translated", + "value" : "Подключено к %d провайдерам" + } + }, + "many" : { + "stringUnit" : { + "state" : "translated", + "value" : "Подключено к %d провайдерам" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "Подключено к %d провайдерам" + } + } + } + } + }, + "sv" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ansluten till %d leverantör" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ansluten till %d leverantörer" + } + } + } + } + }, + "th" : { + "variations" : { + "plural" : { + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "เชื่อมต่อกับผู้ให้บริการ %d แล้ว" + } + } + } + } + }, + "uk" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "Підключено до %d провайдера" + } + }, + "few" : { + "stringUnit" : { + "state" : "translated", + "value" : "Підключено до %d провайдерів" + } + }, + "many" : { + "stringUnit" : { + "state" : "translated", + "value" : "Підключено до %d провайдерів" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "Підключено до %d провайдерів" + } + } + } + } + }, + "vi" : { + "variations" : { + "plural" : { + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "Đã kết nối với %d nhà cung cấp" + } + } + } + } + }, + "zh-Hans" : { + "variations" : { + "plural" : { + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "已连接到 %d 个提供商" + } + } + } + } + }, + "zh-HK" : { + "variations" : { + "plural" : { + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "已連接到 %d 個供應商" + } + } + } + } + } + } + }, + "Connecting to providers" : { + "comment" : "Call to action", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "جاري الاتصال بمزودي الخدمة" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Připojení k poskytovatelům" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Verbindung zu Anbietern wird hergestellt" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Σύνδεση με παρόχους" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Connecting to providers" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Conectando a proveedores" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "Conectando a los proveedores" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Conectando a proveedores" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Connexion aux fournisseurs" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "מתחבר לספקים" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "प्रदाताओं से कनेक्ट हो रहा है" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Menghubungkan ke penyedia" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Connessione ai provider" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "プロバイダーに接続中" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "제공업체에 연결 중" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Verbinden met providers" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Łączenie z dostawcami" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "A conectar aos provedores" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Conectando aos provedores" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "A ligar aos fornecedores" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Подключение к провайдерам" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ansluter till leverantörer" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "กำลังเชื่อมต่อกับผู้ให้บริการ" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Підключення до провайдерів" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Đang kết nối với nhà cung cấp" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "正在连接到提供商" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "正在連接到供應商" + } + } + } + }, + "Connection Mode" : { + "comment" : "Used in Main/Connect/ConnectActions/ConnectActions.swift.", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "وضع الاتصال" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Režim připojení" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Verbindungsmodus" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Λειτουργία σύνδεσης" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Connection Mode" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Modo de conexión" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "Modo de conexión" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mode de connexion" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "מצב חיבור" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "कनेक्शन मोड" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mode Koneksi" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Modalità di connessione" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "接続モード" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "연결 모드" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Verbindingsmodus" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tryb połączenia" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Modo de conexão" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Modo de conexão" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Режим подключения" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Anslutningsläge" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "โหมดการเชื่อมต่อ" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Режим підключення" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Chế độ kết nối" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "连接模式" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "連接模式" + } + } + } + }, + "Connections" : { + "comment" : "Settings", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "الاتصالات" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Připojení" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Verbindungen" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Συνδέσεις" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Connections" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Conexiones" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "Conexiones" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Conexiones" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Connexions" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "חיבורים" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "कनेक्शन" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Koneksi" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Connessioni" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "接続" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "연결" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Verbindingen" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Połączenia" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Conexões" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Conexões" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Conexões" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Подключения" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Anslutningar" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "การเชื่อมต่อ" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "З'єднання" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kết nối" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "连接" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "連接" + } + } + } + }, + "Continue" : { + "comment" : "Continue button label.", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "تابع" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pokračovat" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Weiter" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Συνέχεια" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Continue" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Continúa" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "Continuar" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Continúa" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Continue" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "המשך" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "जारी रखें" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lanjutkan" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Continua" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "続ける" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "계속" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Doorgaan" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kontynuuj" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Continua" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Continua" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Continuar" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Продолжить" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fortsätt" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "ดำเนินการต่อ" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Продовжити" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tiếp tục" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "继续" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "繼續" + } + } + } + }, + "Contract" : { + "comment" : "A label displayed in the center of a contract circle.", + "isCommentAutoGenerated" : true, + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "عقد" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kontrakt" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vertrag" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Συμβόλαιο" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Contract" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Contrato" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "Contrato" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Contrato" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Contrat" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "חוזה" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "कॉन्ट्रैक्ट" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kontrak" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Contratto" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "契約" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "계약" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Contract" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kontrakt" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Contrato" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Contrato" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Contrato" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Контракт" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kontrakt" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "สัญญา" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Контракт" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hợp đồng" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "合约" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "合約" + } + } + } + }, + "Contracts appear here while connected." : { + "comment" : "A description of the contract details view.", + "isCommentAutoGenerated" : true, + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "تظهر العقود هنا أثناء الاتصال." + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kontrakty se zde zobrazí po připojení." + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Verträge erscheinen hier, während du verbunden bist." + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Τα συμβόλαια εμφανίζονται εδώ όταν είστε συνδεδεμένοι." + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Contracts appear here while connected." + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Los contratos aparecen aquí mientras estás conectado." + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "Los contratos aparecen aquí mientras estás conectado." + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Los contratos aparecen aquí mientras estás conectado." + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Les contrats apparaissent ici lorsque vous êtes connecté." + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "החוזים מופיעים כאן בזמן החיבור." + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "कनेक्ट होने पर कॉन्ट्रैक्ट यहाँ दिखाई देंगे।" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kontrak muncul di sini saat terhubung." + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "I contratti appaiono qui mentre sei connesso." + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "接続中に契約がここに表示されます。" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "연결 중에 계약이 여기에 표시됩니다." + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Contracten verschijnen hier terwijl je verbonden bent." + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kontrakty pojawią się tutaj po połączeniu." + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Os contratos aparecem aqui enquanto estiver ligado." + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Os contratos aparecem aqui enquanto você estiver conectado." + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Os contratos aparecem aqui enquanto estiver ligado." + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Контракты появятся здесь при подключении." + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kontrakt visas här när du är ansluten." + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "สัญญาจะแสดงที่นี่ขณะเชื่อมต่อ" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Контракти з'являться тут під час підключення." + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hợp đồng xuất hiện ở đây khi đang kết nối." + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "连接后,合约会显示在这里。" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "連接後,合約會在此顯示。" + } + } + } + }, + "Contracts appear here while providing." : { + "comment" : "A description of the contract details view.", + "isCommentAutoGenerated" : true, + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "تظهر العقود هنا أثناء التوفير." + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kontrakty se zde zobrazí během poskytování." + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Verträge erscheinen hier, während die Bereitstellung läuft." + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Τα συμβόλαια εμφανίζονται εδώ όταν παρέχετε." + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Contracts appear here while providing." + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Los contratos aparecen aquí mientras proporcionas datos." + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "Los contratos aparecen aquí mientras proporcionas datos." + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Los contratos aparecen aquí mientras proporcionas datos." + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Les contrats apparaissent ici lorsque vous fournissez." + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "החוזים מופיעים כאן בזמן האספקה." + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "प्रदान करते समय कॉन्ट्रैक्ट यहाँ दिखाई देंगे।" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kontrak muncul di sini saat menyediakan." + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "I contratti appaiono qui mentre fornisci." + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "提供中に契約がここに表示されます。" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "제공 중에 계약이 여기에 표시됩니다." + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Contracten verschijnen hier tijdens het leveren." + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kontrakty pojawią się tutaj podczas udostępniania." + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Os contratos aparecem aqui enquanto estiver a fornecer." + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Os contratos aparecem aqui enquanto você estiver fornecendo." + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Os contratos aparecem aqui enquanto estiver a fornecer." + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Контракты появятся здесь во время предоставления." + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kontrakt visas här när du tillhandahåller." + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "สัญญาจะแสดงที่นี่ขณะให้บริการ" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Контракти з'являться тут під час надання." + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hợp đồng xuất hiện ở đây khi đang cung cấp." + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "提供数据时,合约会显示在这里。" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "提供數據時,合約會在此顯示。" + } + } + } + }, + "Contribute Bandwidth" : { + "comment" : "Used in Shared/Views/Introduction/IntroductionParticipateSettingsView.swift.", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "ساهم بعرض النطاق الترددي" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Poskytněte šířku pásma" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bandbreite bereitstellen" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Συνεισφέρετε εύρος ζώνης" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Contribute Bandwidth" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aportar ancho de banda" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aportar ancho de banda" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fournir de la bande passante" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "תרום רוחב פס" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "बैंडविड्थ में योगदान दें" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sumbangkan Bandwidth" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fornisci larghezza di banda" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "帯域幅の提供" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "대역폭 제공" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bandbreedte leveren" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Udostępnij przepustowość" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Contribuir com largura de banda" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Contribuir com largura de banda" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Делитесь пропускной способностью" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bidra med bandbredd" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "แบ่งปันแบนด์วิดท์" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Діліться пропускною здатністю" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Đóng góp băng thông" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "贡献带宽" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "貢獻頻寬" + } + } + } + }, + "Copy Auth Code" : { + "comment" : "Call to action", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "نسخ رمز المصادقة" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kopírovat ověřovací kód" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Authentifizierungscode kopieren" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Αντιγραφή κωδικού ταυτοποίησης" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Copy Auth Code" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Copiar código de autenticación" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "Copiar código de autenticación" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Copiar código de autenticación" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Copier le code d’authentification" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "העתק קוד אימות" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "ऑथ कोड कॉपी करें" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Salin kode otentikasi" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Copia codice di autenticazione" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "認証コードをコピー" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "인증 코드 복사" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Auth-code kopiëren" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Skopiuj kod uwierzytelniający" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Copiar código de autenticação" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Copiar código de autenticação" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Copiar código de autenticação" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Скопировать код аутентификации" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kopiera autentiseringskod" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "คัดลอกรหัสยืนยัน" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Скопіювати код автентифікації" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sao chép mã xác thực" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "复制验证码" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "複製認證碼" + } + } + } + }, + "Copy client ID" : { + "comment" : "A label for a context menu item that copies a client ID to the clipboard.", + "isCommentAutoGenerated" : true, + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "نسخ معرّف العميل" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kopírovat ID klienta" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Client-ID kopieren" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Αντιγραφή ID πελάτη" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Copy client ID" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Copiar ID de cliente" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "Copiar ID de cliente" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Copier l'ID client" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "העתק מזהה לקוח" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "क्लाइंट ID कॉपी करें" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Salin ID klien" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Copia l'ID client" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "クライアントIDをコピー" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "클라이언트 ID 복사" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Client-ID kopiëren" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kopiuj ID klienta" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Copiar ID do cliente" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Copiar ID do cliente" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Скопировать ID клиента" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kopiera klient-ID" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "คัดลอก ID ไคลเอ็นต์" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Скопіювати ID клієнта" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sao chép ID máy khách" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "复制客户端 ID" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "複製客戶端 ID" + } + } + } + }, + "Couldn't open wallet. Please install it and try again." : { + "comment" : "Snackbar shown when the external wallet app could not be opened. Used in Shared/Views/SolanaSignMessageSheet.swift and Main/Account/Wallets/ConnectWalletSheet/ConnectWalletSheetView.swift.", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "تعذّر فتح المحفظة. يرجى تثبيتها والمحاولة مرة أخرى." + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Peněženku se nepodařilo otevřít. Nainstalujte ji prosím a zkuste to znovu." + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Wallet konnte nicht geöffnet werden. Bitte installiere sie und versuche es erneut." + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Δεν ήταν δυνατό το άνοιγμα του πορτοφολιού. Εγκαταστήστε το και δοκιμάστε ξανά." + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Couldn't open wallet. Please install it and try again." + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "No se ha podido abrir la billetera. Instálala e inténtalo de nuevo." + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "No se pudo abrir la billetera. Instálala e inténtalo de nuevo." + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "No se pudo abrir la cartera. Instálala e inténtalo de nuevo." + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Impossible d'ouvrir le portefeuille. Veuillez l'installer et réessayer." + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "לא ניתן היה לפתוח את הארנק. אנא התקן אותו ונסה שוב." + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "वॉलेट खोला नहीं जा सका। कृपया इसे इंस्टॉल करें और पुनः प्रयास करें।" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tidak dapat membuka dompet. Silakan instal dan coba lagi." + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Impossibile aprire il portafoglio. Installalo e riprova." + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "ウォレットを開けませんでした。インストールしてからもう一度お試しください。" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "지갑을 열 수 없습니다. 설치 후 다시 시도해 주세요." + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kon de wallet niet openen. Installeer deze en probeer het opnieuw." + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nie udało się otworzyć portfela. Zainstaluj go i spróbuj ponownie." + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Não foi possível abrir a carteira. Instale-a e tente novamente." + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Não foi possível abrir a carteira. Instale-a e tente novamente." + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Não foi possível abrir a carteira. Instale-a e tente novamente." + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Не удалось открыть кошелек. Установите его и попробуйте снова." + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kunde inte öppna plånboken. Installera den och försök igen." + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "ไม่สามารถเปิดกระเป๋าเงินได้ กรุณาติดตั้งแล้วลองใหม่อีกครั้ง" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Не вдалося відкрити гаманець. Установіть його та спробуйте ще раз." + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Không thể mở ví. Vui lòng cài đặt và thử lại." + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "无法打开钱包。请安装后再试。" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "無法開啟錢包。請安裝後再試。" + } + } + } + }, + "Couldn't parse public key, please try again later." : { + "comment" : "Snackbar shown when the wallet's public key could not be parsed. Used in Main/Account/Settings/SettingsView.swift.", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "تعذّر تحليل المفتاح العام، يرجى المحاولة مرة أخرى لاحقًا." + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Veřejný klíč se nepodařilo zpracovat, zkuste to prosím později." + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Öffentlicher Schlüssel konnte nicht verarbeitet werden. Bitte versuche es später erneut." + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Δεν ήταν δυνατή η ανάλυση του δημόσιου κλειδιού, δοκιμάστε ξανά αργότερα." + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Couldn't parse public key, please try again later." + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "No se ha podido procesar la clave pública. Inténtalo de nuevo más tarde." + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "No se pudo procesar la clave pública. Inténtalo de nuevo más tarde." + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "No se pudo procesar la clave pública. Inténtalo de nuevo más tarde." + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Impossible d'analyser la clé publique, veuillez réessayer plus tard." + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "לא ניתן היה לנתח את המפתח הציבורי, אנא נסה שוב מאוחר יותר." + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "सार्वजनिक कुंजी पार्स नहीं की जा सकी, कृपया बाद में पुनः प्रयास करें।" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tidak dapat memproses kunci publik, silakan coba lagi nanti." + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Impossibile analizzare la chiave pubblica, riprova più tardi." + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "公開鍵を解析できませんでした。後でもう一度お試しください。" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "공개 키를 해석할 수 없습니다. 나중에 다시 시도해 주세요." + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kon de publieke sleutel niet verwerken, probeer het later opnieuw." + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nie udało się przetworzyć klucza publicznego, spróbuj ponownie później." + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Não foi possível processar a chave pública. Tente novamente mais tarde." + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Não foi possível processar a chave pública. Tente novamente mais tarde." + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Não foi possível processar a chave pública. Tente novamente mais tarde." + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Не удалось обработать открытый ключ, попробуйте позже." + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kunde inte tolka den publika nyckeln, försök igen senare." + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "ไม่สามารถแยกวิเคราะห์กุญแจสาธารณะได้ กรุณาลองใหม่ภายหลัง" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Не вдалося обробити відкритий ключ, спробуйте пізніше." + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Không thể phân tích khóa công khai, vui lòng thử lại sau." + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "无法解析公钥,请稍后再试。" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "無法解析公鑰,請稍後再試。" + } + } + } + }, + "Couldn't update your preferences. Please try again." : { + "comment" : "Snackbar shown when saving account preferences fails and the toggle snaps back. Used in Shared/ViewModels/AccountPreferencesViewModel.swift.", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "تعذّر تحديث تفضيلاتك. يرجى المحاولة مرة أخرى." + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Předvolby se nepodařilo aktualizovat. Zkuste to prosím znovu." + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Deine Einstellungen konnten nicht aktualisiert werden. Bitte versuche es erneut." + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Δεν ήταν δυνατή η ενημέρωση των προτιμήσεών σας. Δοκιμάστε ξανά." + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Couldn't update your preferences. Please try again." + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "No se han podido actualizar tus preferencias. Inténtalo de nuevo." + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "No se pudieron actualizar tus preferencias. Inténtalo de nuevo." + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "No se pudieron actualizar tus preferencias. Inténtalo de nuevo." + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Impossible de mettre à jour vos préférences. Veuillez réessayer." + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "לא ניתן היה לעדכן את ההעדפות שלך. אנא נסה שוב." + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "आपकी प्राथमिकताएँ अपडेट नहीं की जा सकीं। कृपया पुनः प्रयास करें।" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tidak dapat memperbarui preferensi Anda. Silakan coba lagi." + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Impossibile aggiornare le tue preferenze. Riprova." + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "設定を更新できませんでした。もう一度お試しください。" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "환경설정을 업데이트할 수 없습니다. 다시 시도해 주세요." + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kon je voorkeuren niet bijwerken. Probeer het opnieuw." + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nie udało się zaktualizować preferencji. Spróbuj ponownie." + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Não foi possível atualizar as suas preferências. Tente novamente." + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Não foi possível atualizar suas preferências. Tente novamente." + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Não foi possível atualizar as suas preferências. Tente novamente." + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Не удалось обновить ваши настройки. Попробуйте еще раз." + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kunde inte uppdatera dina inställningar. Försök igen." + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "ไม่สามารถอัปเดตการตั้งค่าของคุณได้ กรุณาลองใหม่อีกครั้ง" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Не вдалося оновити ваші налаштування. Спробуйте ще раз." + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Không thể cập nhật tùy chọn của bạn. Vui lòng thử lại." + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "无法更新您的偏好设置。请重试。" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "無法更新您的偏好設定。請重試。" + } + } + } + }, + "Country" : { + "comment" : "Call to action", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "الدولة" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Země" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Land" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Χώρα" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Country" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "País" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "País" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "País" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pays" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "מדינה" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "देश" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Negara" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Paese" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "国" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "국가" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Land" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kraj" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "País" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "País" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "País" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Страна" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Land" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "ประเทศ" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Країна" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Quốc gia" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "国家" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "國家" + } + } + } + }, + "Country multipliers" : { + "comment" : "Call to action", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "مضاعفات الدول" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Násobitele podle zemí" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Länder-Multiplikatoren" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Πολλαπλασιαστές χώρας" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Country multipliers" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Multiplicadores por país" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "Multiplicadores por país" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Multiplicadores por país" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Multiplicateurs par pays" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "מכפילים לפי מדינה" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "देश गुणक" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pengali negara" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Moltiplicatori per paese" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "国別倍率" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "국가별 배수" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Landmultipliers" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mnożniki krajów" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Multiplicadores por país" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Multiplicadores por país" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Multiplicadores por país" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Множители по странам" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Landsmultiplikatorer" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "ตัวคูณตามประเทศ" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Країнові множники" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hệ số quốc gia" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "国家倍增器" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "國家乘數" + } + } + } + }, + "Create" : { + "comment" : "Call to action", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "إنشاء" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vytvořit" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Erstellen" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Δημιουργία" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Create" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Crear" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "Crear" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Crear" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Créer" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "צור" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "बनाएँ" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Buat" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Crea" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "作成" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "생성" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Maken" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Utwórz" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Criar" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Criar" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Criar" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Создать" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Skapa" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "สร้าง" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Створити" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tạo" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "创建" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "創建" + } + } + } + }, + "Create account" : { + "comment" : "Display name for the paid plan. The WIRE value stays \"supporter\" (subscription_type / current_subscription.plan) because already-shipped app versions parse that string; only what the user reads changes.", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "إنشاء حساب" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vytvořit účet" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Konto erstellen" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Δημιουργία λογαριασμού" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Create account" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Crear cuenta" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "Crear una cuenta" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Crear cuenta" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Créer un compte" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "צור חשבון" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "खाता बनाएँ" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Buat akun" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Crea un account" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "アカウントを作成" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "계정 만들기" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Account creëren" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Utwórz konto" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Criar conta" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Criar conta" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Criar uma conta" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Создать аккаунт" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Skapa ett konto" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "สร้างบัญชี" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Створити обліковий запис" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tạo tài khoản" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "创建帐户" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "創建帳戶" + } + } + } + }, + "Create Account" : { + "comment" : "Used in Authenticate/LoginInitial/LoginInitialView.swift.", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "إنشاء حساب" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vytvořit účet" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ein Konto erstellen" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Δημιουργία λογαριασμού" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Create Account" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Crear una cuenta" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "Crear una cuenta" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Créer un compte" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "צור חשבון" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "खाता बनाएं" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Buat akun" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Crea un account" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "アカウントを作成" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "계정 만들기" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Account aanmaken" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Utwórz konto" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Criar uma conta" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Criar uma conta" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Создать аккаунт" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Skapa ett konto" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "สร้างบัญชี" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Створити обліковий запис" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tạo tài khoản" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "创建账户" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "創建帳戶" + } + } + } + }, + "Create an account" : { + "comment" : "Used in Main/Account/AccountRootView.swift.", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "إنشاء حساب" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vytvořte si účet" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ein Konto erstellen" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Δημιουργία λογαριασμού" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Create an account" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Crear una cuenta" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "Crear una cuenta" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Créer un compte" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "צור חשבון" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "खाता बनाएं" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Buat akun" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Crea un account" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "アカウントを作成" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "계정 만들기" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Account aanmaken" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Utwórz konto" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Criar uma conta" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Criar uma conta" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Создать аккаунт" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Skapa ett konto" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "สร้างบัญชี" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Створити обліковий запис" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tạo tài khoản" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "创建账户" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "創建帳戶" + } + } + } + }, + "Created auth codes expire after 5 minutes" : { + "comment" : "Used in Main/Account/Settings/SettingsForm-iOS.swift.", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "تنتهي صلاحية رموز المصادقة التي تم إنشاؤها بعد 5 دقائق" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vytvořené autorizační kódy vyprší po 5 minutách" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Erstellte Auth-Codes verfallen nach 5 Minuten" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Οι δημιουργημένοι κωδικοί ταυτοποίησης λήγουν μετά από 5 λεπτά" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Created auth codes expire after 5 minutes" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Los códigos de autenticación creados caducan después de 5 minutos" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "Los códigos de autenticación creados caducan después de 5 minutos" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Les codes d'authentification créés expirent après 5 minutes" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "קודי אימות שנוצרו פגים לאחר 5 דקות" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "निर्मित प्रमाणीकरण कोड 5 मिनट बाद समाप्त हो जाते हैं" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kode otentikasi yang dibuat akan kedaluwarsa setelah 5 menit" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "I codici di autenticazione creati scadono dopo 5 minuti" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "作成された認証コードは5分後に失効します" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "생성된 인증 코드는 5분 후 만료됩니다" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aangemaakte authenticatiecodes verlopen na 5 minuten" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Utworzone kody uwierzytelniające wygasają po 5 minutach" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Códigos de autenticação criados expiram após 5 minutos" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Códigos de autenticação criados expiram após 5 minutos" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Созданные коды авторизации истекают через 5 минут" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Skapade autentiseringskoder upphör efter 5 minuter" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "รหัสยืนยันตัวตนที่สร้างขึ้นจะหมดอายุหลังจาก 5 นาที" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Створені коди автентифікації закінчуються через 5 хвилин" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mã xác thực được tạo sẽ hết hạn sau 5 phút" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "创建的认证码将在5分钟后失效" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "創建的認證碼將於5分鐘後失效" + } + } + } + }, + "Current Ranking" : { + "comment" : "Call to action", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "الترتيب الحالي" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktuální pořadí" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktuelles Ranking" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Τρέχουσα κατάταξη" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Current Ranking" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Clasificación actual" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "Clasificación actual" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Clasificación actual" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Classement actuel" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "דירוג נוכחי" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "वर्तमान रैंकिंग" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Peringkat Saat Ini" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Classifica attuale" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "現在のランキング" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "현재 순위" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Huidige Scorebord" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktualny ranking" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Classificação Atual" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Classificação Atual" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Classificação atual" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Текущий рейтинг" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktuell rankning" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "อันดับปัจจุบัน" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Поточний рейтинг" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Xếp hạng hiện tại" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "当前排名" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "目前排名" + } + } + } + }, + "Current referral network" : { + "comment" : "Used in Main/Account/Settings/UpdateReferralNetworkSheet/UpdateReferralNetworkSheet.swift.", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "شبكة الإحالة الحالية" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktuální referální sít" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktuelles Empfehlungsnetzwerk" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Τρέχον δίκτυο παραπομπών" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Current referral network" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Red de referidos actual" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "Red de referidos actual" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Réseau de parrainage actuel" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "רשת ההפניות הנוכחית" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "वर्तमान रेफ़रल नेटवर्क" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Jaringan referensi saat ini" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rete di riferimento attuale" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "現在のリファラルネットワーク" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "현재 추천 네트워크" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Huidig referentienetwerk" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Obecna sieć poleceń" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rede de indicações atual" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rede de recomendações atual" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Текущая реферальная сеть" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktuellt referensnätverk" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "เครือข่ายผู้อ้างอิงปัจจุบัน" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Поточна реферальна мережа" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mạng giới thiệu hiện tại" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "当前推荐网络" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "當前推薦網絡" + } + } + } + }, + "Custom DNS" : { + "comment" : "Title of a section in the connect drawer that shows the custom dns settings.", + "isCommentAutoGenerated" : true, + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "DNS مخصص" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vlastní DNS" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Eigenes DNS" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Προσαρμοσμένο DNS" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Custom DNS" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "DNS personalizado" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "DNS personalizado" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "DNS personalizado" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "DNS personnalisé" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "DNS מותאם אישית" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "कस्टम DNS" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "DNS khusus" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "DNS personalizzato" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "カスタムDNS" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "사용자 지정 DNS" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aangepaste DNS" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Własny DNS" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "DNS personalizado" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "DNS personalizado" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "DNS personalizado" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Пользовательский DNS" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Anpassad DNS" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "DNS ที่กำหนดเอง" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Власний DNS" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "DNS tùy chỉnh" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "自定义 DNS" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "自訂 DNS" + } + } + } + }, + "Daily Data Balance:" : { + "comment" : "Guidance for someone who has no code yet. Deliberately PLAIN TEXT with no link and no button: app store rules forbid steering a user to an external purchase, but nothing stops us saying where codes come from. Without this, the sheet asks for a code and gives no way to obtain one.", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "رصيد البيانات اليومي:" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Denní datový zůstatek:" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tägliches Datenvolumen:" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ημερήσιο υπόλοιπο δεδομένων:" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Daily Data Balance:" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Saldo diario de datos:" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "Saldo de datos diario:" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Saldo diario de datos:" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Solde de données quotidien :" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "יתרת נתונים יומית:" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "दैनिक डेटा बैलेंस:" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Saldo Data Harian:" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Saldo dati giornaliero:" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "毎日のデータ残高:" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "일일 데이터 잔액:" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dagelijkse databundel:" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dzienny limit danych:" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Saldo diário de dados:" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Saldo diário de dados:" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Saldo diário de dados:" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Дневной баланс данных:" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Daglig databalans:" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "ยอดคงเหลือข้อมูลรายวัน:" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Щоденний баланс даних:" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Số dư dữ liệu hàng ngày:" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "每日数据余额:" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "每日數據餘額:" + } + } + } + }, + "Danger" : { + "comment" : "Used in Main/Account/Settings/SettingsForm-iOS.swift.", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "خطر" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gefahr" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Κίνδυνος" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Danger" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Peligro" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "Peligro" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Danger" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "סכנה" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "खतरा" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bahaya" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pericolo" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "危険" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "위험" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gevaar" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Niebezpieczeństwo" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Perigo" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Perigo" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Опасность" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fara" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "อันตราย" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Небезпека" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nguy hiểm" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "危险" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "危險" + } + } + } + }, + "Data" : { + "comment" : "Guidance for someone who has no code yet. Deliberately PLAIN TEXT with no link and no button: app store rules forbid steering a user to an external purchase, but nothing stops us saying where codes come from. Without this, the sheet asks for a code and gives no way to obtain one.", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "البيانات" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Data" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Daten" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Δεδομένα" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Data" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Datos" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "Datos" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Datos" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Données" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "נתונים" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "डेटा" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Data" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dati" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "データ" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "데이터" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Data" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dane" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dados" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dados" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dados" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Данные" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Data" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "ข้อมูล" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Дані" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dữ liệu" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "数据" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "數據" + } + } + } + }, + "DEFAULT" : { + "comment" : "Used in Shared/Views/PayoutWalletTag.swift.", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "افتراضي" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "STANDARD" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "ΠΡΟΕΠΙΛΟΓΗ" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "DEFAULT" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "POR DEFECTO" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "POR DEFECTO" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "PAR DÉFAUT" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "בְּרִירַת מֶחדָל" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "डिफ़ॉल्ट" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "DEFAULT" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "DEFAULT" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "デフォルト" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "기본값" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Standaard" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "DOMYŚLNY" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "PADRÃO" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "PADRÃO" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "ПО УМОЛЧАНИЮ" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "STANDARD" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "ค่าเริ่มต้น" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "ЗА ЗАМОВЧУВАННЯМ" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "MẶC ĐỊNH" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "默认" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "默認" + } + } + } + }, + "Delete" : { + "comment" : "Used in Main/Account/BlockedLocations/BlockedLocationsView.swift.", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "حذف" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Odstranit" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Löschen" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Διαγραφή" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Delete" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Eliminar" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "Eliminar" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Supprimer" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "מחק" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "डिलीट करें" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hapus" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elimina" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "削除" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "삭제" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Verwijderen" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Usuń" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Excluir" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Eliminar" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Удалить" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Radera" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "ลบ" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Видалити" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Xóa" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "删除" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "刪除" + } + } + } + }, + "Delete account" : { + "comment" : "Used in Main/Account/Settings/SettingsForm-iOS.swift.", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "حذف الحساب" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vymazat účet" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Konto löschen" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Διαγραφή λογαριασμού" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Delete account" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Eliminar cuenta" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "Eliminar cuenta" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Supprimer le compte" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "מחק חשבון" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "खाता हटाएं" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hapus akun" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elimina account" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "アカウントを削除" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "계정 삭제" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Verwijder account" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Usuń konto" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Excluir conta" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Eliminar conta" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Удалить аккаунт" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Radera konto" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "ลบบัญชี" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Видалити обліковий запис" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Xóa tài khoản" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "删除账户" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "刪除帳戶" + } + } + } + }, + "Device" : { + "comment" : "device", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "الجهاز" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zařízení" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gerät" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Συσκευή" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Device" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dispositivo" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dispositivo" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dispositivo" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Appareil" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "מכשיר" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "डिवाइस" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Perangkat" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dispositivo" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "デバイス" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "기기" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Apparaat" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Urządzenie" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dispositivo" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dispositivo" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dispositivo" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Устройство" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enhet" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "อุปกรณ์" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Пристрій" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Thiết bị" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "设备" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "裝置" + } + } + } + }, + "Device name" : { + "comment" : "Used in Main/Account/Settings/SettingsView.swift.", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "اسم الجهاز" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Název zařízení" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gerätename" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Όνομα συσκευής" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Device name" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nombre del dispositivo" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nombre del dispositivo" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nom de l'appareil" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "שם המכשיר" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "डिवाइस का नाम" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nama perangkat" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nome del dispositivo" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "デバイス名" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "기기 이름" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Apparaatnaam" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nazwa urządzenia" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nome do dispositivo" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nome do dispositivo" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Имя устройства" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enhetsnamn" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "ชื่ออุปกรณ์" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Назва пристрою" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tên thiết bị" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "设备名称" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "裝置名稱" + } + } + } + }, + "Device name updated" : { + "comment" : "Snackbar shown after the device name is saved. Used in Main/Account/Settings/SettingsView.swift.", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "تم تحديث اسم الجهاز" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Název zařízení byl aktualizován" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gerätename aktualisiert" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Το όνομα της συσκευής ενημερώθηκε" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Device name updated" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nombre del dispositivo actualizado" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nombre del dispositivo actualizado" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nombre del dispositivo actualizado" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nom de l'appareil mis à jour" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "שם המכשיר עודכן" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "डिवाइस का नाम अपडेट किया गया" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nama perangkat diperbarui" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nome del dispositivo aggiornato" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "デバイス名を更新しました" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "기기 이름이 업데이트되었습니다" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Apparaatnaam bijgewerkt" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zaktualizowano nazwę urządzenia" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nome do dispositivo atualizado" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nome do dispositivo atualizado" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nome do dispositivo atualizado" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Имя устройства обновлено" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enhetsnamn uppdaterat" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "อัปเดตชื่ออุปกรณ์แล้ว" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Назву пристрою оновлено" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Đã cập nhật tên thiết bị" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "设备名称已更新" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "裝置名稱已更新" + } + } + } + }, + "Disconnect" : { + "comment" : "Disconnect VPN button text", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "قطع الاتصال" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Odpojení" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Trennen" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Αποσύνδεση" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Disconnect" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Desconectar" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "Desconectar" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Desconectar" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Déconnecter" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "התנתק" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "डिस्कनेक्ट क" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Putuskan" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Disconnetti" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "切断する" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "연결 해제" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Verbinding verbreken" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rozłącz" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Desconectar" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Desconectar" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Desligar" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Отключить" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Koppla från" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "ตัดการเชื่อมต่อ" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Відключитися" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ngắt kết nối" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "断开连接" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "斷開連接" + } + } + } + }, + "Disconnect URnetwork VPN" : { + "comment" : "Used in Shared/Intents/DisconnectIntent.swift.", + "localizations" : { + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Odpojit se od URnetwork VPN" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Disconnect URnetwork VPN" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "URnetwork VPM verbinding verbreken" + } + } + } + }, + "Display network on leaderboard" : { + "comment" : "Call to action", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "عرض الشبكة على لوحة الصدارة" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zobrazení sítě na žebříčku" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Netzwerk in Bestenliste anzeigen" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Εμφάνιση δικτύου στον πίνακα κατάταξης" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Display network on leaderboard" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mostrar red en la tabla de clasificación" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mostrar red en la tabla de posiciones" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mostrar red en la tabla de clasificación" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Afficher le réseau dans le classement" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "הצג רשת בטבלת המובילים" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "लीडरबोर्ड पर नेटवर्क प्रदर्शित करें" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tampilkan jaringan di papan peringkat" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mostra rete nella classifica" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "リーダーボードにネットワークを表示" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "리더보드에 네트워크 표시" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Toon netwerk op het scorebord" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Wyświetlaj sieć w tablicy wyników" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Exibir rede no quadro de líderes" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Exibir rede no placar de líderes" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mostrar rede na tabela de classificação" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Отображать сеть в таблице лидеров" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Visa nätverk på topplistan" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "แสดงชื่อเครือข่ายบนกระดาน" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Показувати мережу в таблиці лідерів" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hiển thị mạng trên bảng xếp hạng" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "在排行榜上显示网络" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "在排行榜上顯示網絡" + } + } + } + }, + "DNS over HTTPS" : { + "comment" : "A label displayed in a row of a custom dns settings.", + "isCommentAutoGenerated" : true, + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "DNS عبر HTTPS" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "DNS přes HTTPS" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "DNS über HTTPS" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "DNS μέσω HTTPS" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "DNS over HTTPS" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "DNS sobre HTTPS" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "DNS sobre HTTPS" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "DNS sobre HTTPS" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "DNS via HTTPS" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "DNS דרך HTTPS" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "HTTPS पर DNS" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "DNS melalui HTTPS" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "DNS su HTTPS" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "DNS over HTTPS" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "DNS over HTTPS" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "DNS over HTTPS" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "DNS przez HTTPS" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "DNS sobre HTTPS" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "DNS sobre HTTPS" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "DNS sobre HTTPS" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "DNS через HTTPS" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "DNS över HTTPS" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "DNS ผ่าน HTTPS" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "DNS через HTTPS" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "DNS qua HTTPS" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "DNS over HTTPS" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "DNS over HTTPS" + } + } + } + }, + "DNS settings unavailable" : { + "comment" : "A message displayed when the dns settings are unavailable.", + "isCommentAutoGenerated" : true, + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "إعدادات DNS غير متاحة" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nastavení DNS není k dispozici" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "DNS-Einstellungen nicht verfügbar" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Οι ρυθμίσεις DNS δεν είναι διαθέσιμες" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "DNS settings unavailable" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ajustes de DNS no disponibles" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configuración de DNS no disponible" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configuración de DNS no disponible" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Paramètres DNS indisponibles" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "הגדרות DNS אינן זמינות" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "DNS सेटिंग्स उपलब्ध नहीं" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pengaturan DNS tidak tersedia" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Impostazioni DNS non disponibili" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "DNS設定は利用できません" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "DNS 설정을 사용할 수 없습니다" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "DNS-instellingen niet beschikbaar" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ustawienia DNS niedostępne" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Definições de DNS indisponíveis" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configurações de DNS indisponíveis" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Definições de DNS indisponíveis" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Настройки DNS недоступны" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "DNS-inställningar är inte tillgängliga" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "ไม่สามารถตั้งค่า DNS ได้" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Налаштування DNS недоступні" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cài đặt DNS không khả dụng" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "DNS 设置不可用" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "DNS 設定不可用" + } + } + } + }, + "Don't have a code? Data codes can be purchased at ur.io and emailed to you." : { + "comment" : "Guidance for someone who has no code yet. Deliberately PLAIN TEXT with no link and no button: app store rules forbid steering a user to an external purchase, but nothing stops us saying where codes come from. Without this, the sheet asks for a code and gives no way to obtain one.", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "ليس لديك رمز؟ يمكن شراء رموز البيانات من ur.io وإرسالها إلى بريدك الإلكتروني." + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nemáte kód? Datové kódy si můžete koupit na ur.io a přijdou vám e-mailem." + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Noch keinen Code? Datencodes kannst du auf ur.io kaufen und dir per E-Mail schicken lassen." + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Δεν έχετε κωδικό; Οι κωδικοί δεδομένων αγοράζονται στο ur.io και αποστέλλονται με email." + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Don't have a code? Data codes can be purchased at ur.io and emailed to you." + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "¿No tienes un código? Puedes comprar códigos de datos en ur.io y te llegarán por correo electrónico." + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "¿No tienes un código? Puedes comprar códigos de datos en ur.io y te llegarán por correo electrónico." + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "¿No tienes un código? Puedes comprar códigos de datos en ur.io y te llegarán por correo electrónico." + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pas de code ? Les codes de données s'achètent sur ur.io et vous sont envoyés par e-mail." + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "אין לך קוד? אפשר לרכוש קודי נתונים ב-ur.io ולקבל אותם במייל." + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "कोड नहीं है? डेटा कोड ur.io पर खरीदे जा सकते हैं और आपको ईमेल कर दिए जाते हैं।" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Belum punya kode? Kode data bisa dibeli di ur.io dan dikirim ke email Anda." + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Non hai un codice? I codici dati si acquistano su ur.io e ti vengono inviati via email." + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "コードをお持ちでない方は、ur.ioでデータコードを購入すると、メールで届きます。" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "코드가 없으신가요? 데이터 코드는 ur.io에서 구매할 수 있으며 이메일로 전송됩니다." + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nog geen code? Datacodes koop je op ur.io en krijg je per e-mail toegestuurd." + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nie masz kodu? Kody danych można kupić na ur.io i otrzymać e-mailem." + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Não tem um código? Pode comprar códigos de dados em ur.io e recebê-los por e-mail." + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Não tem um código? Você pode comprar códigos de dados em ur.io e recebê-los por e-mail." + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Não tem um código? Pode comprar códigos de dados em ur.io e recebê-los por e-mail." + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Нет кода? Коды данных можно купить на ur.io — их пришлют вам на почту." + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Har du ingen kod? Datakoder kan köpas på ur.io och skickas till din e-post." + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "ยังไม่มีโค้ด? ซื้อรหัสข้อมูลได้ที่ ur.io แล้วระบบจะส่งให้ทางอีเมล" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Немає коду? Коди даних можна придбати на ur.io — їх надішлють вам на пошту." + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Chưa có mã? Bạn có thể mua mã dữ liệu tại ur.io và nhận qua email." + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "没有余额码?可在 ur.io 购买,购买后会通过电子邮件发送给你。" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "沒有餘額碼?可在 ur.io 購買,購買後會透過電郵發送給你。" + } + } + } + }, + "Don't see it? " : { + "comment" : "Prompt shown when user doesn't see the verification email or SMS.", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "ألا تراه؟" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nevidíš to?" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Siehst du ihn nicht?" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Δεν το βλέπεις;" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Don't see it?" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "¿No lo ves?" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "¿No lo ves?" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "¿No lo ves?" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vous ne le voyez pas ?" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "לא רואה את זה?" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "आपको यह दिखाई नहीं दे रहा है?" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tidak melihatnya?" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Non lo vedi?" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "メールが見つかりませんか?" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "메일이 보이지 않나요?" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zie je het niet?" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nie widzisz go?" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Não está vendo?" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Não está vendo?" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Não está vendo?" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Не видите письмо?" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ser du det inte?" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "ไม่เห็นใช่ไหม?" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Не бачите?" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Không thấy?" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "没有看到?" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "看不到嗎?" + } + } + } + }, + "Earning multipliers" : { + "comment" : "Call to action", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "مضاعفات الأرباح" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Násobitele výdělku" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Verdienst-Multiplikatoren" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Πολλαπλασιαστές κερδών" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Earning multipliers" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Multiplicadores de ganancias" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "Multiplicadores de ganancias" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Multiplicadores de ganancias" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Multiplicateurs de gains" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "מכפילי רווחים" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "कमाई गुणक" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pengganda penghasilan" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Moltiplicatori di guadagno" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "獲得ポイントの倍率" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "수익 배수" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Verdienvermenigvuldigers" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mnożniki zarobków" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Multiplicadores de ganhos" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Multiplicadores de ganhos" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Multiplicadores de ganhos" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Множители заработка" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Intäktsmultiplikatorer" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "ตัวคูณรายได้" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Множники заробітку" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hệ số nhân thu nhập" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "收益倍增器" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "收益倍增器" + } + } + } + }, + "Earnings" : { + "comment" : "Wallet Screen", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "الأرباح" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Výdělky" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Einnahmen" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Κέρδη" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Earnings" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ganancias" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ganancias" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ganancias" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gains" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "רווחים" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "आय" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pendapatan" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Guadagni" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "収益" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "수익" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Opbrengsten" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zarobki" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ganhos" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ganhos" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ganhos" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Заработок" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Intäkter" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "การสร้างรายได้" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Заробіток" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Thu nhập" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "收益" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "收益" + } + } + } + }, + "Edit referral code" : { + "comment" : "Call to action", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "تعديل رمز الإحالة" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Upravit doporučující kód" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Empfehlungscode bearbeiten" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Επεξεργασία κωδικού παραπομπής" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Edit referral code" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Editar código de referencia" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "Editar código de referencia" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Editar código de referencia" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Modifier le code de parrainage" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "ערוך קוד הפניה" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "रेफरल कोड संपादित करें" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Edit kode referensi" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Modifica codice di riferimento" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "紹介コードを編集" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "추천 코드 편집" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Referentiecode bewerken" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Edytuj kod polecający" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Editar código de referência" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Editar código de indicação" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Editar código de referência" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Изменить реферальный код" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Redigera referenskod" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "แก้ไขรหัสแนะนำ" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Редагувати реферальний код" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Chỉnh sửa mã giới thiệu" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "编辑推荐码" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "編輯推薦碼" + } + } + } + }, + "Edit split rule" : { + "comment" : "A label displayed above the split rule editor.", + "isCommentAutoGenerated" : true, + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "تعديل قاعدة التقسيم" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Upravit pravidlo rozdělení" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Split-Regel bearbeiten" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Επεξεργασία κανόνα διαχωρισμού" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Edit split rule" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Editar regla de división" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "Editar regla de división" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Editar regla de división" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Modifier la règle de séparation" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "ערוך כלל פיצול" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "स्प्लिट नियम संपादित करें" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Edit aturan pemisahan" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Modifica regola di suddivisione" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "分割ルールを編集" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "분할 규칙 편집" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Splitsregel bewerken" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Edytuj regułę podziału" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Editar regra de divisão" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Editar regra de divisão" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Editar regra de divisão" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Изменить правило разделения" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Redigera delningsregel" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "แก้ไขกฎแยกเส้นทาง" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Редагувати правило розділення" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Chỉnh sửa quy tắc tách luồng" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "编辑分流规则" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "編輯分流規則" + } + } + } + }, + "Email or phone number" : { + "comment" : "Label for the user authentication input field.", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "البريد الإلكتروني أو رقم الهاتف" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Email nebo telefonní číslo" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "E-Mail oder Telefonnummer" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Email ή αριθμός τηλεφώνου" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Email or phone number" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Correo electrónico o número de teléfono" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "Correo electrónico o número de teléfono" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Correo electrónico o número de teléfono" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "E-mail ou numéro de téléphone" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "דוא״ל או מספר טלפון" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "ईमेल या फ़ोन नंबर" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Email atau nomor telepon" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Email o numero di telefono" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "メールアドレスまたは電話番号" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "이메일 또는 전화번호" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "E-mail of telefonnummer" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "E-mail lub numer telefonu" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "E-mail ou número de telefone" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "E-mail ou número de telefone" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "E-mail ou número de telemóvel" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Электронная почта или номер телефона" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "E-post eller telefonnummer" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "อีเมลหรือหมายเลขโทรศัพท์" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Електронна пошта або номер телефону" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Email hoặc số điện thoại" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "邮箱或手机号码" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "電郵或電話號碼" + } + } + } + }, + "Enter" : { + "comment" : "Login initial", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "ادخل" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enter" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rein" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Εισαγωγή" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enter" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Entra" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "Entrar" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Entra" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Entrée" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "היכנס" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "दर्ज" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Masuk" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Entra" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "ゴー!" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "들어가기" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enter" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Wejdź" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Entra" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Entra" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Entrar" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Войти" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gå in" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "เข้า" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Увійти" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vào" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "进入" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "進入" + } + } + } + }, + "Enter a bonus referral code" : { + "comment" : "Used in Authenticate/CreateNetwork/CreateNetworkView.swift.", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "أدخل رمز الإحالة المكافأة" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vložte bonusový referální kód" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Geben Sie einen Bonus-Empfehlungscode ein" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Εισαγάγετε έναν κωδικό παραπομπής μπόνους" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enter a bonus referral code" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Introduce un código de referencia de bonificación" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ingresa un código de referencia de bonificación" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Entrez un code de parrainage bonus" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "הזן קוד הפניה בונוס" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "बोनस रेफरल कोड दर्ज करें" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Masukkan kode referensi bonus" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Inserisci un codice di riferimento bonus" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "ボーナス紹介コードを入力" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "보너스 추천 코드를 입력하세요" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Voer een bonusreferentiecode in" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Wprowadź kod polecający bonus" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Digite um código de referência bônus" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Digite um código de referência bônus" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Введите бонусный реферальный код" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ange en bonusreferenskod" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "ป้อนรหัสอ้างอิงโบนัส" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Введіть бонусний реферальний код" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nhập mã giới thiệu thưởng" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "输入奖励推荐码" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "輸入獎勵推薦碼" + } + } + } + }, + "Enter a name for your network" : { + "comment" : "Used in Authenticate/CreateNetwork/CreateNetworkView.swift.", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "أدخل اسمًا لشبكتك" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vložte jméno pro vaší sít" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Geben Sie einen Namen für Ihr Netzwerk ein" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Εισάγετε ένα όνομα για το δίκτυό σας" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enter a name for your network" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Introduce un nombre para tu red" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ingresa un nombre para tu red" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Entrez un nom pour votre réseau" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "הזן שם לרשת שלך" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "अपने नेटवर्क के लिए एक नाम दर्ज करें" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Masukkan nama untuk jaringan Anda" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Inserisci un nome per la tua rete" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "ネットワーク名を入力" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "네트워크 이름을 입력하세요" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Voer een naam in voor uw netwerk" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Wprowadź nazwę swojej sieci" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Digite um nome para sua rede" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Introduza um nome para a sua rede" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Введите название вашей сети" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ange ett namn för ditt nätverk" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "ป้อนชื่อสำหรับเครือข่ายของคุณ" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Введіть назву вашої мережі" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nhập tên cho mạng của bạn" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "输入您的网络名称" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "輸入你的網絡名稱" + } + } + } + }, + "Enter a Solana or Bittensor wallet address" : { + "comment" : "Used in Main/Account/Wallets/ConnectWalletSheet/EnterWalletAddress/EnterWalletAddressView.swift.", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "أدخل عنوان محفظة Solana أو Bittensor" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zadejte adresu peněženky Solana nebo Bittensor" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gib eine Solana- oder Bittensor-Wallet-Adresse ein" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Εισαγάγετε μια διεύθυνση πορτοφολιού Solana ή Bittensor" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enter a Solana or Bittensor wallet address" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Introduce una dirección de billetera de Solana o Bittensor" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ingresa una dirección de billetera de Solana o Bittensor" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Entrez une adresse de portefeuille Solana ou Bittensor" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "הזן כתובת ארנק של Solana או Bittensor" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Solana या Bittensor वॉलेट पता दर्ज करें" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Masukkan alamat dompet Solana atau Bittensor" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Inserisci un indirizzo di portafoglio Solana o Bittensor" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "SolanaまたはBittensorのウォレットアドレスを入力" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "Solana 또는 Bittensor 지갑 주소를 입력하세요" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Voer een Solana- of Bittensor-walletadres in" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Wprowadź adres portfela Solana lub Bittensor" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Digite um endereço de carteira Solana ou Bittensor" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Introduza um endereço de carteira Solana ou Bittensor" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Введите адрес кошелька Solana или Bittensor" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ange en Solana- eller Bittensor-plånboksadress" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "กรอกที่อยู่กระเป๋าเงิน Solana หรือ Bittensor" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Введіть адресу гаманця Solana або Bittensor" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nhập địa chỉ ví Solana hoặc Bittensor" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "请输入 Solana 或 Bittensor 钱包地址" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "請輸入 Solana 或 Bittensor 錢包地址" + } + } + } + }, + "Enter a Solana USDC wallet address" : { + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "أدخل عنوان محفظة USDC على Solana" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zadejte adresu peněženky USDC v síti Solana" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Geben Sie eine Solana USDC-Wallet-Adresse ein" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Εισαγάγετε μια διεύθυνση πορτοφολιού USDC στο Solana" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enter a Solana USDC wallet address" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Introduce una dirección de monedero USDC en Solana" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ingresa una dirección de billetera USDC en Solana" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Saisissez une adresse de portefeuille USDC sur Solana" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "הזן כתובת ארנק USDC ב-Solana" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Solana USDC वॉलेट पता दर्ज करें" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Masukkan alamat dompet USDC Solana" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Inserisci un indirizzo wallet USDC su Solana" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "Solana USDCウォレットアドレスを入力してください" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "Solana USDC 지갑 주소를 입력하세요" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Voer een Solana USDC-walletadres in" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Wprowadź adres portfela USDC w sieci Solana" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Digite um endereço de carteira USDC na Solana" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Introduza um endereço de carteira USDC na Solana" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Введите адрес кошелька USDC в сети Solana" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ange en Solana USDC-plånboksadress" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "ป้อนที่อยู่กระเป๋าเงิน USDC บน Solana" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Введіть адресу гаманця USDC на Solana" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nhập địa chỉ ví USDC trên Solana" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "请输入 Solana USDC 钱包地址" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "請輸入 Solana USDC 錢包地址" + } + } + } + }, + "Enter balance code" : { + "comment" : "Guidance for someone who has no code yet. Deliberately PLAIN TEXT with no link and no button: app store rules forbid steering a user to an external purchase, but nothing stops us saying where codes come from. Without this, the sheet asks for a code and gives no way to obtain one.", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "أدخل رمز الرصيد" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zadejte kód zůstatku" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Guthabencode eingeben" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Εισαγάγετε τον κωδικό υπολοίπου" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enter balance code" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ingresa el código de saldo" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ingresa el código de saldo" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ingresa el código de saldo" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Entrez le code de solde" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "הזן קוד יתרה" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "बैलेंस कोड दर्ज करें" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Masukkan kode saldo" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Inserisci il codice saldo" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "残高コードを入力" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "잔액 코드를 입력하세요" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Voer saldo-code in" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Wpisz kod salda" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Insira o código de saldo" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Insira o código de saldo" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Introduza o código de saldo" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Введите код баланса" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ange saldo-kod" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "ป้อนรหัสยอดเงิน" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Введіть код балансу" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nhập mã số dư" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "输入余额码" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "輸入餘額碼" + } + } + } + }, + "Enter network referral code" : { + "comment" : "Call to action", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "+%@ غيغابايت/يوم" + "value" : "أدخل رمز الإحالة للشبكة" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "+%@ GiB/den" + "value" : "Zadejte síťový referenční kód" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "+%@ GiB/Tag" + "value" : "Netzwerbe-Empfehlungscode eingeben" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "+%@ GiB/ημέρα" + "value" : "Εισαγάγετε τον κωδικό παραπομπής δικτύου" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "+%@ GiB/Day" + "value" : "Enter network referral code" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "+%@ GiB/día" + "value" : "Introduce el código de referido de la red" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "+%@ GiB/día" + "value" : "Ingrese el código de referencia de la red" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ingresa el código de referido de la red" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "+%@ Go/jour" + "value" : "Entrez le code de parrainage réseau" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "+%@ GiB/יום" + "value" : "הזן קוד הפניה של הרשת" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "+%@ GiB/दिन" + "value" : "नेटवर्क रेफ़रल कोड दर्ज करें" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "+%@ GiB/hari" + "value" : "Masukkan kode referensi jaringan" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "+%@ GiB/giorno" + "value" : "Inserisci il codice di riferimento della rete" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "+%@ GiB/日" + "value" : "ネットワーク紹介コードを入力してください" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "+%@ GiB/일" + "value" : "네트워크 추천 코드를 입력하세요" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "+%@ GiB/dag" + "value" : "Referentiecode ingeven" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "+%@ GiB/dzień" + "value" : "Wprowadź kod polecenia sieci" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Insira o código de referência da rede" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "+%@ GiB/dia" + "value" : "Insira o código de indicação da rede" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "+%@ GiB/dia" + "value" : "Introduza o código de referência da rede" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "+%@ ГиБ/день" + "value" : "Введите реферальный код сети" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "+%@ GiB/dag" + "value" : "Ange nätverksreferenskod" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "+%@ GiB/วัน" + "value" : "กรอกรหัสการแนะนำเครือข่าย" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "+%@ ГіБ/день" + "value" : "Введіть реферальний код мережі" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "+%@ GiB/ngày" + "value" : "Nhập mã giới thiệu mạng" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "+%@ GiB/天" + "value" : "输入网络推荐码" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "+%@ GiB/日" + "value" : "輸入網絡推薦碼" } } } }, - "+%@ USDC" : { - "shouldTranslate" : false - }, - "+%lf GiB/Day" : { - "extractionState" : "stale", + "Enter URnetwork" : { + "comment" : "Used in Authenticate/LoginInitial/GuestModeSheet.swift.", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "+%lf غيغابايت/يوم" + "value" : "أدخل URnetwork" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "+%lf GiB/den" + "value" : "Vkročte do URnetwork" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "+%lf GiB/Tag" + "value" : "URnetwork betreten" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "+%lf GiB/ημέρα" + "value" : "Εισαγωγή στο URnetwork" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "+%lf GiB/Day" + "value" : "Enter URnetwork" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "+%lf GiB/día" + "value" : "Entrar en URnetwork" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "+%lf GiB/día" + "value" : "Entrar en URnetwork" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "+%lf Go/jour" + "value" : "Entrer dans URnetwork" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "+%lf GiB/יום" + "value" : "היכנס ל-URnetwork" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "+%lf GiB/दिन" + "value" : "URnetwork में प्रवेश करें" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "+%lf GiB/hari" + "value" : "Masuk URnetwork" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "+%lf GiB/giorno" + "value" : "Entra in URnetwork" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "+%lf GiB/日" + "value" : "URnetworkに入る" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "+%lf GiB/일" + "value" : "URnetwork에 들어가기" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "+%lf GiB/dag" + "value" : "Voer URnetwork in" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "+%lf GiB/dzień" + "value" : "Wejdź do URnetwork" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "+%lf GiB/dia" + "value" : "Entrar no URnetwork" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "+%lf GiB/dia" + "value" : "Entrar no URnetwork" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "+%lf ГиБ/день" + "value" : "Войти в URnetwork" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "+%lf GiB/dag" + "value" : "Gå in i URnetwork" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "+%lf GiB/วัน" + "value" : "เข้าสู่ URnetwork" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "+%lf ГіБ/день" + "value" : "Увійти в URnetwork" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "+%lf GiB/ngày" + "value" : "Vào URnetwork" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "+%lf GiB/天" + "value" : "进入 URnetwork" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "+%lf GiB/日" + "value" : "進入 URnetwork" } } } }, - "+%lld GiB/Month" : { + "Enter wallet address" : { + "comment" : "Used in Main/Account/Wallets/ConnectWalletSheet/EnterWalletAddress/EnterWalletAddressView.swift.", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "+%lld غيغابايت/شهر" + "value" : "أدخل عنوان المحفظة" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "+%lld GiB/měsíc" + "value" : "Vložte adresu peněženky" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "+%lld GiB/Monat" + "value" : "Geben Sie die Wallet-Adresse ein" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "+%lld GiB/μήνα" + "value" : "Εισαγάγετε τη διεύθυνση πορτοφολιού" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "+%lld GiB/Month" + "value" : "Enter wallet address" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "+%lld GiB/mes" + "value" : "Ingrese la dirección de la billetera" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "+%lld GiB/mes" + "value" : "Ingrese la dirección de la billetera" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "+%lld Go/mois" + "value" : "Entrez l'adresse du portefeuille" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "+%lld GiB/חודש" + "value" : "הזן כתובת ארנק" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "+%lld GiB/माह" + "value" : "वॉलेट पता दर्ज करें" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "+%lld GiB/bulan" + "value" : "Masukkan alamat dompet" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "+%lld GiB/mese" + "value" : "Inserisci l'indirizzo del portafoglio" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "+%lld GiB/月" + "value" : "ウォレットアドレスを入力" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "+%lld GiB/월" + "value" : "지갑 주소 입력" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "+%lld GiB/maand" + "value" : "Voer walletadres in" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "+%lld GiB/miesiąc" + "value" : "Wprowadź adres portfela" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "+%lld GiB/mês" + "value" : "Digite o endereço da carteira" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "+%lld GiB/mês" + "value" : "Digite o endereço da carteira" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "+%lld ГиБ/месяц" + "value" : "Введите адрес кошелька" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "+%lld GiB/månad" + "value" : "Ange plånboksadress" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "+%lld GiB/เดือน" + "value" : "ป้อนที่อยู่กระเป๋าเงิน" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "+%lld ГіБ/місяць" + "value" : "Введіть адресу гаманця" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "+%lld GiB/tháng" + "value" : "Nhập địa chỉ ví" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "+%lld GiB/月" + "value" : "输入钱包地址" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "+%lld GiB/月" + "value" : "輸入錢包地址" } } } }, - "0" : { - "shouldTranslate" : false - }, - "100% open source and transparent" : { + "Enter your one-time auth code" : { + "comment" : "Placeholder text for the authentication code input field", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "مفتوح المصدر وشفاف بنسبة 100٪" + "value" : "أدخل رمز المصادقة لمرة واحدة" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "100% open source a transparentní" + "value" : "Zadejte svůj jednorázový ověřovací kód" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "100% Open Source und transparent" + "value" : "Geben Sie Ihren einmaligen Authentifizierungscode ein" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "100% ανοιχτού κώδικα και διαφανές" + "value" : "Εισαγάγετε τον μοναδικό κωδικό ταυτοποίησης" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "100% open source and transparent" + "value" : "Enter your one-time auth code" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "100% código abierto y transparente" + "value" : "Ingresa tu código de autenticación de un solo uso" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "100% código abierto y transparente" + "value" : "Ingresa tu código de autenticación de una sola vez" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ingresa tu código de autenticación de un solo uso" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "100 % open source et transparent" + "value" : "Entrez votre code d’authentification à usage unique" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "100% קוד פתוח ושקוף" + "value" : "הזן את קוד האימות החד-פעמי שלך" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "100% ओपन सोर्स और पारदर्शी" + "value" : "अपना एक बार का ऑथ कोड दर्ज करें" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "100% open source dan transparan" + "value" : "Masukkan kode autentikasi satu kali Anda" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "100% open source e trasparente" + "value" : "Inserisci il tuo codice di autenticazione monouso" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "100%オープンソースで透明性があります" + "value" : "ワンタイム認証コードを入力してください" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "100% 오픈 소스 및 투명" + "value" : "일회용 인증 코드를 입력하세요" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "100% open source en transparant" + "value" : "Voer uw eenmalige authenticatiecode in" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "100% otwarte oprogramowanie i przejrzystość" + "value" : "Wpisz swój jednorazowy kod uwierzytelniający" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Insira o seu código de autenticação único" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "100% open source e transparente" + "value" : "Insira seu código de autenticação único" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "100% open source e transparente" + "value" : "Introduza o seu código de autenticação único" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "100% открытый исходный код и прозрачность" + "value" : "Введите ваш одноразовый код аутентификации" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "100 % öppen källkod och transparent" + "value" : "Ange din engångsautentiseringskod" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "โอเพ่นซอร์สและโปร่งใส 100%" + "value" : "ป้อนรหัสยืนยันตัวตนแบบใช้ครั้งเดียวของคุณ" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "100% відкритий код і прозорість" + "value" : "Введіть ваш одноразовий код авторизації" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "100% mã nguồn mở và minh bạch" + "value" : "Nhập mã xác thực một lần của bạn" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "100%开源且透明" + "value" : "输入您的一次性验证码" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "100%開源及透明" + "value" : "輸入你的一次性認證碼" } } } }, - "Account" : { + "Enter your phone number or email" : { + "comment" : "Login initial", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "الحساب" + "value" : "أدخل رقم هاتفك أو بريدك الإلكتروني" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Účet" + "value" : "Zadejte své telefonní číslo nebo e-mail" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Konto" + "value" : "Gib deine Telefonnummer oder E-Mail-Adresse ein" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Λογαριασμός" + "value" : "Εισαγάγετε τον αριθμό τηλεφώνου ή το email σας" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Account" + "value" : "Enter your phone number or email" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Cuenta" + "value" : "Introduce tu número de teléfono o correo electrónico" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Cuenta" + "value" : "Ingresa tu número de teléfono o correo" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Introduce tu número de teléfono o correo electrónico" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Compte" + "value" : "Entrez votre numéro de téléphone ou votre email" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "חשבון" + "value" : "הזן את מספר הטלפון או האימייל שלך" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "खाता" + "value" : "अपना फ़ोन नंबर या ईमेल डालें" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Akun" + "value" : "Masukkan nomor telepon atau email Anda" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Account" + "value" : "Inserisci il tuo numero di telefono o email" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "アカウント" + "value" : "電話番号またはメールアドレスを入力してください" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "계정" + "value" : "전화번호 또는 이메일을 입력하세요" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Account" + "value" : "Voer je telefoonnummer of e-mailadres in" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Konto" + "value" : "Wprowadź numer telefonu lub adres e-mail" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Digite seu número de telefone ou e-mail" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Conta" + "value" : "Digite seu número de telefone ou e-mail" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Conta" + "value" : "Introduza o seu número de telefone ou email" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Аккаунт" + "value" : "Введите номер телефона или email" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Konto" + "value" : "Ange ditt telefonnummer eller e-post" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "บัญชี" + "value" : "กรอกเบอร์โทรศัพท์ หรืออีเมลของคุณ" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Обліковий запис" + "value" : "Введіть номер телефону або email" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Tài khoản" + "value" : "Nhập số điện thoại hoặc email của bạn" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "账号" + "value" : "输入您的电话号码或电子邮件" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "帳戶" + "value" : "輸入你的電話號碼或電郵" } } } }, - "Account points" : { + "Error connecting to URnetwork" : { + "extractionState" : "stale", + "localizations" : { + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Chyba při připojování k síti URnetwork" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Error connecting to URnetwork" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fout bij verbinden met URnetwork" + } + } + } + }, + "Error loading your account plan." : { + "comment" : "Call to action", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "نقاط الحساب" + "value" : "حدث خطأ أثناء تحميل خطة حسابك." } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Body na účtě" + "value" : "Chyba při načítání plánu vašeho účtu." } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Kontopunkte" + "value" : "Fehler beim Laden Ihres Kontoplans." } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Πόντοι λογαριασμού" + "value" : "Σφάλμα κατά τη φόρτωση του προγράμματος λογαριασμού σας." } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Account points" + "value" : "Error loading your account plan." } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Puntos de cuenta" + "value" : "Error al cargar el plan de tu cuenta." } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Puntos de cuenta" + "value" : "Error al cargar tu plan de cuenta." + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Error al cargar el plan de tu cuenta." } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Points du compte" + "value" : "Erreur lors du chargement de votre formule de compte." } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "נקודות חשבון" + "value" : "שגיאה בטעינת תוכנית החשבון שלך." } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "खाता अंक" + "value" : "आपके अकाउंट प्लान को लोड करने में त्रुटि हुई।" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Poin akun" + "value" : "Terjadi kesalahan saat memuat paket akun Anda." } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Punti account" + "value" : "Errore nel caricamento del tuo piano account." } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "アカウントポイント" + "value" : "アカウントプランの読み込み中にエラーが発生しました。" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "계정 포인트" + "value" : "계정 플랜을 불러오는 중 오류가 발생했습니다." } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Account punten" + "value" : "Fout bij het laden van uw accountplan." } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Punkty konta" + "value" : "Błąd podczas ładowania planu konta." + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Erro ao carregar o seu plano de conta." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Pontos da conta" + "value" : "Erro ao carregar seu plano de conta." } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Pontos da conta" + "value" : "Erro ao carregar o seu plano de conta." } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Баллы аккаунта" + "value" : "Ошибка загрузки вашего тарифного плана." } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Kontopoäng" + "value" : "Fel vid inläsning av din kontoplan." } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "คะแนนบัญชี" + "value" : "เกิดข้อผิดพลาดในการโหลดแผนบัญชีของคุณ" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Бали акаунта" + "value" : "Помилка завантаження вашого тарифного плану." } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Điểm tài khoản" + "value" : "Lỗi khi tải gói tài khoản của bạn." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "账户积分" + "value" : "加载您的账户方案时出错。" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "帳戶積分" + "value" : "載入你的帳戶方案時發生錯誤。" } } } }, - "Activity" : { - "comment" : "A heading for the list of current block actions.", - "isCommentAutoGenerated" : true - }, - "Add referral code" : { + "Error sending password reset link" : { + "comment" : "Snackbar shown when sending the password reset link fails. Used in Main/Account/Profile/ProfileView.swift.", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "أضف رمز الإحالة" + "value" : "خطأ في إرسال رابط إعادة تعيين كلمة المرور" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Přidání doporučujícího kódu" + "value" : "Chyba při odesílání odkazu pro obnovení hesla" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Empfehlungscode hinzufügen" + "value" : "Fehler beim Senden des Links zum Zurücksetzen des Passworts" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Προσθήκη κωδικού παραπομπής" + "value" : "Σφάλμα κατά την αποστολή του συνδέσμου επαναφοράς κωδικού πρόσβασης" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Add referral code" + "value" : "Error sending password reset link" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Agregar código de referencia" + "value" : "Error al enviar el enlace para restablecer la contraseña" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Agregar código de referencia" + "value" : "Error al enviar el enlace para restablecer la contraseña" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Error al enviar el enlace para restablecer la contraseña" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Ajouter un code de parrainage" + "value" : "Erreur lors de l'envoi du lien de réinitialisation du mot de passe" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "הוסף קוד הפניה" + "value" : "שגיאה בשליחת הקישור לאיפוס הסיסמה" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "रेफरल कोड जोड़ें" + "value" : "पासवर्ड रीसेट लिंक भेजने में त्रुटि" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Tambahkan kode referensi" + "value" : "Kesalahan saat mengirim tautan atur ulang kata sandi" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Aggiungi codice di riferimento" + "value" : "Errore durante l'invio del link per reimpostare la password" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "紹介コードを追加" + "value" : "パスワードリセット用リンクの送信中にエラーが発生しました" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "추천 코드 추가" + "value" : "비밀번호 재설정 링크를 보내는 중 오류가 발생했습니다" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Referentiecode toevoegen" + "value" : "Fout bij het verzenden van de wachtwoordherstellink" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Dodaj kod polecający" + "value" : "Błąd podczas wysyłania linku do zresetowania hasła" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Erro ao enviar a ligação para repor a palavra-passe" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Adicionar código de referência" + "value" : "Erro ao enviar o link de redefinição de senha" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Adicionar código de referência" + "value" : "Erro ao enviar a ligação para repor a palavra-passe" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Добавить реферальный код" + "value" : "Ошибка при отправке ссылки для сброса пароля" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Lägg till referenskod" + "value" : "Fel vid skickande av länk för lösenordsåterställning" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "เพิ่มรหัสอ้างอิง" + "value" : "เกิดข้อผิดพลาดในการส่งลิงก์รีเซ็ตรหัสผ่าน" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Додати реферальний код" + "value" : "Помилка під час надсилання посилання для скидання пароля" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Thêm mã giới thiệu" + "value" : "Lỗi khi gửi liên kết đặt lại mật khẩu" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "添加推荐码" + "value" : "发送密码重置链接时出错" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "添加推薦碼" + "value" : "發送密碼重設連結時發生錯誤" } } } }, - "Add referral code to earn extra rewards" : { + "Error setting default wallet" : { + "comment" : "Snackbar shown when setting the default payout wallet fails. Used in Main/Account/Wallet/WalletView.swift.", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "أضف رمز الإحالة لكسب مكافآت إضافية" + "value" : "خطأ في تعيين المحفظة الافتراضية" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Přidáním kódu pro doporučení získáte další odměny" + "value" : "Chyba při nastavování výchozí peněženky" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Füge einen Empfehlungscode hinzu, um zusätzliche Belohnungen zu erhalten" + "value" : "Fehler beim Festlegen der Standard-Wallet" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Προσθέστε κωδικό παραπομπής για να κερδίσετε επιπλέον ανταμοιβές" + "value" : "Σφάλμα κατά τον ορισμό του προεπιλεγμένου πορτοφολιού" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Add referral code to earn extra rewards" + "value" : "Error setting default wallet" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Agrega un código de referencia para ganar recompensas adicionales" + "value" : "Error al establecer la billetera predeterminada" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Agrega un código de referencia para ganar recompensas adicionales" + "value" : "Error al establecer la billetera predeterminada" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Error al establecer la cartera predeterminada" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Ajoutez un code de parrainage pour gagner des récompenses supplémentaires" + "value" : "Erreur lors de la définition du portefeuille par défaut" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "הוסף קוד הפניה כדי להרוויח תגמולים נוספים" + "value" : "שגיאה בהגדרת ארנק ברירת המחדל" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "अतिरिक्त पुरस्कार अर्जित करने के लिए रेफरल कोड जोड़ें" + "value" : "डिफ़ॉल्ट वॉलेट सेट करने में त्रुटि" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Tambahkan kode referensi untuk mendapatkan hadiah tambahan" + "value" : "Kesalahan saat mengatur dompet default" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Aggiungi un codice di riferimento per guadagnare ricompense extra" + "value" : "Errore durante l'impostazione del portafoglio predefinito" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "紹介コードを追加して追加報酬を獲得" + "value" : "デフォルトウォレットの設定中にエラーが発生しました" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "추천 코드를 추가하여 추가 보상을 받으세요" + "value" : "기본 지갑을 설정하는 중 오류가 발생했습니다" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Voeg een referentiecode toe en verdien extra beloningen" + "value" : "Fout bij het instellen van de standaardwallet" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Dodaj kod polecający, aby zdobyć dodatkowe nagrody" + "value" : "Błąd podczas ustawiania domyślnego portfela" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Erro ao definir a carteira predefinida" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Adicione um código de referência para ganhar recompensas extras" + "value" : "Erro ao definir a carteira padrão" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Adicione um código de referência para ganhar recompensas extras" + "value" : "Erro ao definir a carteira predefinida" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Добавьте реферальный код, чтобы заработать дополнительные награды" + "value" : "Ошибка при установке кошелька по умолчанию" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Lägg till en referenskod för att tjäna extra belöningar" + "value" : "Fel vid inställning av standardplånbok" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "เพิ่มรหัสอ้างอิงเพื่อรับรางวัลพิเศษ" + "value" : "เกิดข้อผิดพลาดในการตั้งค่ากระเป๋าเงินเริ่มต้น" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Додайте реферальний код, щоб отримати додаткові винагороди" + "value" : "Помилка під час встановлення гаманця за замовчуванням" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Thêm mã giới thiệu để nhận thêm phần thưởng" + "value" : "Lỗi khi đặt ví mặc định" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "添加推荐码以赚取额外奖励" + "value" : "设置默认钱包时出错" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "添加推薦碼以獲取額外獎勵" + "value" : "設定預設錢包時發生錯誤" } } } }, - "Adjust settings to maximize reliability. App runs in the background when connected and on Wi-Fi. Reliability is how many IPs you make continually available to the network" : { - "extractionState" : "stale", + "Error setting default wallet: %@" : { + "comment" : "Snackbar shown when setting the default payout wallet fails, with the underlying error message. Used in Main/Account/Wallet/WalletView.swift.", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "اضبط الإعدادات لتعظيم الموثوقية. يعمل التطبيق في الخلفية عند الاتصال وعند استخدام الواي فاي. الموثوقية هي عدد عناوين IP التي تجعلها متاحة باستمرار للشبكة." + "value" : "خطأ في تعيين المحفظة الافتراضية: %@" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Upravte nastavení pro maximalizaci spolehlivosti. Aplikace běží na pozadí, když jste připojeni a na Wi-Fi. Spolehlivost znamená, kolik IP adres trvale zpřístupníte síti." + "value" : "Chyba při nastavování výchozí peněženky: %@" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Passen Sie die Einstellungen an, um die Zuverlässigkeit zu maximieren. Die App läuft im Hintergrund, wenn Sie verbunden sind und sich im WLAN befinden. Zuverlässigkeit bedeutet, wie viele IPs Sie dem Netzwerk kontinuierlich zur Verfügung stellen." + "value" : "Fehler beim Festlegen der Standard-Wallet: %@" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Ρυθμίστε τις ρυθμίσεις για μέγιστη αξιοπιστία. Η εφαρμογή λειτουργεί στο παρασκήνιο όταν είστε συνδεδεμένοι και σε Wi-Fi. Η αξιοπιστία είναι πόσες διευθύνσεις IP διαθέτετε συνεχώς στο δίκτυο." + "value" : "Σφάλμα κατά τον ορισμό του προεπιλεγμένου πορτοφολιού: %@" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Adjust settings to maximize reliability. App runs in the background when connected and on Wi-Fi. Reliability is how many IPs you make continually available to the network" + "value" : "Error setting default wallet: %@" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Ajusta la configuración para maximizar la fiabilidad. La app se ejecuta en segundo plano cuando está conectada y en Wi-Fi. La fiabilidad es cuántas IPs mantienes disponibles continuamente para la red." + "value" : "Error al establecer la billetera predeterminada: %@" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Ajusta la configuración para maximizar la fiabilidad. La app se ejecuta en segundo plano cuando está conectada y en Wi-Fi. La fiabilidad es cuántas IPs mantienes disponibles continuamente para la red." + "value" : "Error al establecer la billetera predeterminada: %@" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Error al establecer la cartera predeterminada: %@" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Ajustez les paramètres pour maximiser la fiabilité. L’application fonctionne en arrière-plan lorsque vous êtes connecté et en Wi-Fi. La fiabilité correspond au nombre d’IP que vous rendez continuellement disponibles pour le réseau." + "value" : "Erreur lors de la définition du portefeuille par défaut : %@" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "התאם את ההגדרות כדי למקסם את האמינות. האפליקציה פועלת ברקע כאשר מחוברים וב-Wi-Fi. אמינות היא כמה כתובות IP אתה משאיר זמינות ברצף לרשת." + "value" : "שגיאה בהגדרת ארנק ברירת המחדל: %@" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "विश्वसनीयता अधिकतम करने के लिए सेटिंग्स समायोजित करें। ऐप कनेक्टेड और वाई-फाई पर बैकग्राउंड में चलता है। विश्वसनीयता यह है कि आप कितनी IPs नेटवर्क के लिए लगातार उपलब्ध रखते हैं।" + "value" : "डिफ़ॉल्ट वॉलेट सेट करने में त्रुटि: %@" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Sesuaikan pengaturan untuk memaksimalkan keandalan. Aplikasi berjalan di latar belakang saat terhubung dan menggunakan Wi-Fi. Keandalan adalah berapa banyak IP yang Anda buat tersedia terus-menerus untuk jaringan." + "value" : "Kesalahan saat mengatur dompet default: %@" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Regola le impostazioni per massimizzare l’affidabilità. L’app funziona in background quando è connessa e su Wi-Fi. L’affidabilità è il numero di IP che rendi continuamente disponibili alla rete." + "value" : "Errore durante l'impostazione del portafoglio predefinito: %@" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "信頼性を最大化するために設定を調整してください。アプリは接続時やWi-Fi利用時にバックグラウンドで動作します。信頼性とは、ネットワークに継続的に提供するIPの数です。" + "value" : "デフォルトウォレットの設定中にエラーが発生しました:%@" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "신뢰도를 극대화하려면 설정을 조정하세요. 앱은 연결되어 있고 Wi-Fi에 있을 때 백그라운드에서 실행됩니다. 신뢰도는 네트워크에 지속적으로 제공하는 IP의 수입니다." + "value" : "기본 지갑을 설정하는 중 오류가 발생했습니다: %@" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Pas de instellingen aan om de betrouwbaarheid te maximaliseren. De app draait op de achtergrond wanneer verbonden en op Wi-Fi. Betrouwbaarheid is hoeveel IP's je continu beschikbaar maakt voor het netwerk." + "value" : "Fout bij het instellen van de standaardwallet: %@" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Dostosuj ustawienia, aby zmaksymalizować niezawodność. Aplikacja działa w tle, gdy jest połączona i korzysta z Wi-Fi. Niezawodność to liczba adresów IP, które stale udostępniasz sieci." + "value" : "Błąd podczas ustawiania domyślnego portfela: %@" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Erro ao definir a carteira predefinida: %@" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Ajuste as configurações para maximizar a confiabilidade. O app funciona em segundo plano quando conectado e no Wi-Fi. Confiabilidade é quantos IPs você mantém continuamente disponíveis para a rede." + "value" : "Erro ao definir a carteira padrão: %@" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Ajuste as definições para maximizar a fiabilidade. A aplicação funciona em segundo plano quando está ligada e em Wi-Fi. Fiabilidade é o número de IPs que mantém continuamente disponíveis para a rede." + "value" : "Erro ao definir a carteira predefinida: %@" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Настройте параметры для максимальной надежности. Приложение работает в фоновом режиме при подключении и использовании Wi-Fi. Надежность — это количество IP-адресов, которые вы постоянно предоставляете сети." + "value" : "Ошибка при установке кошелька по умолчанию: %@" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Justera inställningarna för att maximera tillförlitligheten. Appen körs i bakgrunden när du är ansluten och på Wi-Fi. Tillförlitlighet är hur många IP-adresser du kontinuerligt gör tillgängliga för nätverket." + "value" : "Fel vid inställning av standardplånbok: %@" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "ปรับการตั้งค่าเพื่อเพิ่มความน่าเชื่อถือ แอปจะทำงานในพื้นหลังเมื่อเชื่อมต่อและใช้ Wi-Fi ความน่าเชื่อถือคือจำนวน IP ที่คุณเปิดให้เครือข่ายใช้งานได้อย่างต่อเนื่อง" + "value" : "เกิดข้อผิดพลาดในการตั้งค่ากระเป๋าเงินเริ่มต้น: %@" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Налаштуйте параметри для максимальної надійності. Додаток працює у фоновому режимі, коли підключений і на Wi-Fi. Надійність — це кількість IP-адрес, які ви постійно надаєте мережі." + "value" : "Помилка під час встановлення гаманця за замовчуванням: %@" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Điều chỉnh cài đặt để tối đa hóa độ tin cậy. Ứng dụng chạy nền khi kết nối và dùng Wi-Fi. Độ tin cậy là số lượng IP bạn liên tục cung cấp cho mạng." + "value" : "Lỗi khi đặt ví mặc định: %@" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "调整设置以最大化可靠性。应用在连接和使用 Wi-Fi 时会在后台运行。可靠性是指你持续为网络提供的 IP 数量。" + "value" : "设置默认钱包时出错:%@" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "調整設定以提升可靠性。應用程式在連接及使用 Wi-Fi 時會於背景運行。可靠性是你持續為網絡提供的 IP 數量。" + "value" : "設定預設錢包時發生錯誤:%@" } } } }, - "All the content in the world from URnetwork" : { + "Exclusions apply to the whole co-associated network cluster, so related traffic is caught together." : { + "comment" : "transfer stats and split rules", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "كل المحتوى في العالم من URnetwork" + "value" : "تنطبق الاستثناءات على مجموعة الشبكة المرتبطة بالكامل، لذا يتم استثناء حركة المرور ذات الصلة معًا." } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Veškerý obsah na světě od URnetwork" + "value" : "Výjimky platí pro celý propojený síťový cluster, takže související provoz je zachycen společně." } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Alle Inhalte der Welt von URnetwork" + "value" : "Ausschlüsse gelten für den gesamten zusammengehörigen Netzwerk-Cluster, sodass verwandter Datenverkehr gemeinsam erfasst wird." } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Όλο το περιεχόμενο του κόσμου από το URnetwork" + "value" : "Οι εξαιρέσεις ισχύουν για ολόκληρη τη συσχετισμένη ομάδα δικτύων, ώστε η σχετική κίνηση να καλύπτεται μαζί." + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Exclusions apply to the whole co-associated network cluster, so related traffic is caught together." } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Todo el contenido del mundo desde URnetwork" + "value" : "Las exclusiones se aplican a todo el clúster de red asociado, así que el tráfico relacionado queda cubierto en conjunto." } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Todo el contenido del mundo desde URnetwork" + "value" : "Las exclusiones se aplican a todo el clúster de red asociado, así que el tráfico relacionado queda cubierto en conjunto." + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Las exclusiones se aplican a todo el clúster de red asociado, así que el tráfico relacionado queda cubierto en conjunto." } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Tout le contenu du monde par URnetwork" + "value" : "Les exclusions s'appliquent à tout le groupe de réseaux associés, afin que le trafic lié soit exclu en même temps." } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "כל התוכן בעולם מ-URnetwork" + "value" : "ההחרגות חלות על כל אשכול הרשת המשויך, כך שתעבורה קשורה נתפסת יחד." } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork से दुनिया भर की सामग्री" + "value" : "अपवाद पूरे संबद्ध नेटवर्क क्लस्टर पर लागू होते हैं, इसलिए संबंधित ट्रैफ़िक एक साथ शामिल हो जाता है।" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Semua konten di dunia dari URnetwork" + "value" : "Pengecualian berlaku untuk seluruh klaster jaringan terkait, sehingga lalu lintas yang berhubungan tercakup bersama." } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Tutti i contenuti del mondo da URnetwork" + "value" : "Le esclusioni si applicano all'intero gruppo di reti associate, così il traffico correlato viene escluso insieme." } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork が届ける世界中のコンテンツ" + "value" : "除外は関連するネットワーククラスタ全体に適用されるため、関連するトラフィックもまとめて対象になります。" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork의 전 세계 콘텐츠" + "value" : "제외는 연관된 네트워크 클러스터 전체에 적용되므로 관련 트래픽이 함께 처리됩니다." } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Alle content ter wereld, van URnetwork" + "value" : "Uitsluitingen gelden voor het hele samenhangende netwerkcluster, zodat gerelateerd verkeer samen wordt meegenomen." } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Cała światowa treść z URnetwork" + "value" : "Wykluczenia dotyczą całego powiązanego klastra sieciowego, więc pokrewny ruch jest obejmowany razem." + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "As exclusões aplicam-se a todo o cluster de rede coassociado, para que o tráfego relacionado seja abrangido em conjunto." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Todo o conteúdo do mundo do URnetwork" + "value" : "As exclusões se aplicam a todo o cluster de rede coassociado, para que o tráfego relacionado seja capturado em conjunto." } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Todo o conteúdo do mundo da URnetwork" + "value" : "As exclusões aplicam-se a todo o cluster de rede coassociado, para que o tráfego relacionado seja abrangido em conjunto." } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Весь контент мира от URnetwork" + "value" : "Исключения применяются ко всему связанному сетевому кластеру, поэтому сопутствующий трафик исключается вместе." } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Allt innehåll i världen från URnetwork" + "value" : "Undantag gäller hela det sammankopplade nätverksklustret, så relaterad trafik fångas upp tillsammans." } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "เนื้อหาทั้งหมดในโลกจาก URnetwork" + "value" : "การยกเว้นจะมีผลกับคลัสเตอร์เครือข่ายที่เกี่ยวข้องกันทั้งกลุ่ม ทราฟฟิกที่เกี่ยวข้องจึงถูกยกเว้นไปพร้อมกัน" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Весь контент світу від URnetwork" + "value" : "Винятки застосовуються до всього пов'язаного мережевого кластера, тому супутній трафік виключається разом." } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Tất cả nội dung trên thế giới từ URnetwork" + "value" : "Loại trừ áp dụng cho toàn bộ cụm mạng liên quan, nên lưu lượng liên quan được gộp chung." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "来自 URnetwork 的全球内容" + "value" : "排除会应用到整个关联网络集群,因此相关流量会被一并排除。" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "來自 URnetwork 的全球內容" + "value" : "排除會套用至整個關聯網絡叢集,因此相關流量會一併排除。" } } } }, - "Allow providing on cellular network" : { + "Expires" : { + "comment" : "Guidance for someone who has no code yet. Deliberately PLAIN TEXT with no link and no button: app store rules forbid steering a user to an external purchase, but nothing stops us saying where codes come from. Without this, the sheet asks for a code and gives no way to obtain one.", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "السماح بالتوفير على الشبكة الخلوية" + "value" : "ينتهي" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Povolit poskytování na mobilní síti" + "value" : "Vyprší" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Bereitstellung im Mobilfunknetz erlauben" + "value" : "Läuft ab" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Να επιτρέπεται η παροχή σε κυψελοειδές δίκτυο" + "value" : "Λήγει" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Allow providing on cellular network" + "value" : "Expires" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Permitir proveer en red celular" + "value" : "Expira" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Permitir proveer en red celular" + "value" : "Expira" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Expira" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Autoriser la fourniture sur le réseau cellulaire" + "value" : "Expire" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "אפשר אספקה ברשת סלולרית" + "value" : "פג תוקף" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "सेलुलर नेटवर्क पर प्रदान करने की अनुमति दें" + "value" : "समाप्त होता है" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Izinkan menyediakan di jaringan seluler" + "value" : "Kedaluwarsa" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Consenti la fornitura su rete cellulare" + "value" : "Scade" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "携帯ネットワークでの提供を許可" + "value" : "有効期限" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "셀룰러 네트워크에서 제공 허용" + "value" : "만료" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Toestaan op mobiel netwerk" + "value" : "Vervalt" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Zezwól na udostępnianie w sieci komórkowej" + "value" : "Wygasa" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Expira" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Permitir fornecimento na rede celular" + "value" : "Expira" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Permitir fornecimento na rede móvel" + "value" : "Expira" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Разрешить предоставление в сотовой сети" + "value" : "Истекает" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Tillåt tillhandahållande på mobilnät" + "value" : "Utgår" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "อนุญาตให้ให้บริการบนเครือข่ายเซลลูลาร์" + "value" : "หมดอายุ" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Дозволити надання в стільниковій мережі" + "value" : "Закінчується" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Cho phép cung cấp trên mạng di động" + "value" : "Hết hạn" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "允许在蜂窝网络上提供" + "value" : "到期" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "允許在蜂窩網絡上提供" + "value" : "到期" } } } }, - "Allowed" : { - "comment" : "Text for a chip that indicates that the action is allowed.", - "isCommentAutoGenerated" : true - }, - "Always" : { + "Export Logs" : { + "comment" : "Used in Shared/Views/ExportLogsButton/ExportLogsButton.swift.", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "دائمًا" + "value" : "تصدير السجلات" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Vždy" + "value" : "Exportovat logy" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Immer" + "value" : "Logs exportieren" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Πάντα" + "value" : "Εξαγωγή αρχείων καταγραφής" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Always" + "value" : "Export Logs" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Siempre" + "value" : "Exportar registros" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Siempre" + "value" : "Exportar registros" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Toujours" + "value" : "Exporter les journaux" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "תמיד" + "value" : "ייצא לוגים" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "हमेशा" + "value" : "लॉग्स एक्सपोर्ट करें" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Selalu" + "value" : "Ekspor log" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Sempre" + "value" : "Esporta log" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "常に" + "value" : "ログをエクスポート" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "항상" + "value" : "로그 내보내기" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "altijd" + "value" : "Logbestanden exporteren" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Zawsze" + "value" : "Eksportuj logi" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Sempre" + "value" : "Exportar logs" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Sempre" + "value" : "Exportar logs" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Всегда" + "value" : "Экспорт логов" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Alltid" + "value" : "Exportera loggar" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "เสมอ" + "value" : "ส่งออกล็อก" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Завжди" + "value" : "Експортувати логи" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Luôn luôn" + "value" : "Xuất nhật ký" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "始终" + "value" : "导出日志" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "始終" + "value" : "匯出日誌" } } } }, - "Amount" : { + "Failed to connect" : { + "comment" : "Used in Shared/Intents/ConnectIntent.swift.", + "localizations" : { + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nepodařilo se připojit" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Failed to connect" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fout tijdens verbinden" + } + } + } + }, + "Failed to connect URnetwork" : { + "comment" : "Used in Shared/Intents/DisconnectIntent.swift.", + "localizations" : { + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nepodařilo se připojit síť URnetwork" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Failed to connect URnetwork" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Verbinding met URnetwork mislukt" + } + } + } + }, + "Failed to disconnect URnetwork VPN" : { + "comment" : "Used in Shared/Intents/DisconnectIntent.swift.", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "المبلغ" + "value" : "فشل قطع الاتصال بـ URnetwork VPN" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Částka" + "value" : "Nepodařilo se odpojit VPN URnetwork" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Betrag" + "value" : "URnetwork VPN konnte nicht getrennt werden" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Ποσό" + "value" : "Αποτυχία αποσύνδεσης του URnetwork VPN" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Amount" + "value" : "Failed to disconnect URnetwork VPN" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Cantidad" + "value" : "No se ha podido desconectar la VPN de URnetwork" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Cantidad" + "value" : "No se pudo desconectar la VPN de URnetwork" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Montant" + "value" : "Échec de la déconnexion d'URnetwork VPN" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "סכום" + "value" : "ניתוק URnetwork VPN נכשל" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "राशि" + "value" : "URnetwork VPN डिस्कनेक्ट नहीं हो सका" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Jumlah" + "value" : "Gagal memutuskan URnetwork VPN" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Importo" + "value" : "Impossibile disconnettere URnetwork VPN" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "金額" + "value" : "URnetwork VPNの切断に失敗しました" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "금액" + "value" : "URnetwork VPN 연결 해제에 실패했습니다" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Aantal" + "value" : "Verbinding met URnetwork VPN verbreken mislukt" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Kwota" + "value" : "Nie udało się rozłączyć VPN URnetwork" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Valor" + "value" : "Não foi possível desconectar a URnetwork VPN" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Valor" + "value" : "Não foi possível desligar a URnetwork VPN" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Сумма" + "value" : "Не удалось отключить URnetwork VPN" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Belopp" + "value" : "Kunde inte koppla från URnetwork VPN" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "จำนวนเงิน" + "value" : "ตัดการเชื่อมต่อ URnetwork VPN ไม่สำเร็จ" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Сума" + "value" : "Не вдалося відключити URnetwork VPN" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Số tiền" + "value" : "Không thể ngắt kết nối URnetwork VPN" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "金额" + "value" : "无法断开 URnetwork VPN 连接" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "金額" + "value" : "無法斷開 URnetwork VPN 連接" } } } }, - "Apply bonus" : { + "Failed to start URnetwork VPN" : { + "comment" : "Used in Shared/Intents/ConnectIntent.swift.", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "تطبيق المكافأة" + "value" : "فشل تشغيل URnetwork VPN" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Použít bonus" + "value" : "Nepodařilo se spustit VPN URnetwork" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Bonus anwenden" + "value" : "URnetwork VPN konnte nicht gestartet werden" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Εφαρμογή μπόνους" + "value" : "Αποτυχία εκκίνησης του URnetwork VPN" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Apply bonus" + "value" : "Failed to start URnetwork VPN" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Aplicar bono" + "value" : "No se ha podido iniciar la VPN de URnetwork" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Aplicar bono" + "value" : "No se pudo iniciar la VPN de URnetwork" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Appliquer le bonus" + "value" : "Échec du démarrage d'URnetwork VPN" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "החל בונוס" + "value" : "הפעלת URnetwork VPN נכשלה" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "बोनस लागू करें" + "value" : "URnetwork VPN शुरू नहीं हो सका" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Terapkan bonus" + "value" : "Gagal memulai URnetwork VPN" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Applica bonus" + "value" : "Impossibile avviare URnetwork VPN" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "ボーナスを適用" + "value" : "URnetwork VPNの起動に失敗しました" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "보너스 적용" + "value" : "URnetwork VPN 시작에 실패했습니다" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Bonus toepassen" + "value" : "URnetwork VPN starten mislukt" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Zastosuj bonus" + "value" : "Nie udało się uruchomić VPN URnetwork" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Aplicar bônus" + "value" : "Não foi possível iniciar a URnetwork VPN" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Aplicar bônus" + "value" : "Não foi possível iniciar a URnetwork VPN" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Применить бонус" + "value" : "Не удалось запустить URnetwork VPN" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Tillämpa bonus" + "value" : "Kunde inte starta URnetwork VPN" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "ใช้โบนัส" + "value" : "เริ่ม URnetwork VPN ไม่สำเร็จ" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Застосувати бонус" + "value" : "Не вдалося запустити URnetwork VPN" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Áp dụng tiền thưởng" + "value" : "Không thể khởi động URnetwork VPN" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "应用奖金" + "value" : "无法启动 URnetwork VPN" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "應用獎金" + "value" : "無法啟動 URnetwork VPN" } } } }, - "Are you sure you want to delete your account?" : { + "Feedback" : { + "comment" : "Used in Main/Feedback/FeedbackView.swift.", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "هل أنت متأكد من رغبتك في حذف حسابك؟" + "value" : "ملاحظات" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Opravdu chcete svůj účet odstranit?" + "value" : "Odezva" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Möchten Sie Ihr Konto wirklich löschen?" + "value" : "Feedback" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Είστε βέβαιοι ότι θέλετε να διαγράψετε τον λογαριασμό σας;" + "value" : "Ανατροφοδότηση" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Are you sure you want to delete your account?" + "value" : "Feedback" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "¿Está seguro de que desea eliminar su cuenta?" + "value" : "Retroalimentación" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "¿Está seguro de que desea eliminar su cuenta?" + "value" : "Retroalimentación" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Êtes-vous sûr de vouloir supprimer votre compte ?" + "value" : "Retour d'information" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "האם אתה בטוח שברצונך למחוק את חשבונך?" + "value" : "משוב" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "क्या आप वाकई अपना खाता हटाना चाहते हैं?" + "value" : "प्रतिक्रिया" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Apakah Anda yakin ingin menghapus akun Anda?" + "value" : "Umpan balik" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Sei sicuro di voler eliminare il tuo account?" + "value" : "Feedback" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "アカウントを削除してもよろしいですか?" + "value" : "フィードバック" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "계정을 삭제하시겠습니까?" + "value" : "피드백" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Weet je zeker dat je je account wilt verwijderen?" + "value" : "Feedback" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Czy na pewno chcesz usunąć swoje konto?" + "value" : "Informacja zwrotna" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Tem certeza de que deseja excluir sua conta?" + "value" : "Feedback" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Tem a certeza que pretende eliminar a sua conta?" + "value" : "Feedback" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Вы уверены, что хотите удалить свой аккаунт?" + "value" : "Обратная связь" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Är du säker på att du vill radera ditt konto?" + "value" : "Feedback" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "คุณแน่ใจหรือไม่ว่าต้องการลบบัญชีของคุณ?" + "value" : "ข้อเสนอแนะ" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Ви впевнені, що хочете видалити свій обліковий запис?" + "value" : "Зворотній зв'язок" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Bạn có chắc chắn muốn xóa tài khoản của mình không?" + "value" : "Phản hồi" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "您确定要删除您的账户吗?" + "value" : "反馈" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "您確定要刪除您的帳戶嗎?" + "value" : "反饋" } } } }, - "Are you sure you want to remove this wallet?" : { + "Fixed IP" : { + "comment" : "A toggle that allows users to enable or disable a fixed IP for their connection.", + "isCommentAutoGenerated" : true, "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "هل أنت متأكد أنك تريد إزالة هذه المحفظة؟" + "value" : "IP ثابت" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Opravdu chcete tuto peněženku odstranit?" + "value" : "Pevná IP" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Sind Sie sicher, dass Sie diese Wallet entfernen möchten?" + "value" : "Feste IP" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Είστε βέβαιοι ότι θέλετε να αφαιρέσετε αυτό το πορτοφόλι;" + "value" : "Σταθερή IP" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fixed IP" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "¿Estás seguro de que quieres eliminar esta billetera?" + "value" : "IP fija" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "¿Estás seguro de que quieres eliminar esta billetera?" + "value" : "IP fija" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "IP fija" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Êtes-vous sûr de vouloir supprimer ce portefeuille ?" + "value" : "IP fixe" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "האם אתה בטוח שברצונך להסיר ארנק זה?" + "value" : "IP קבוע" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "क्या आप वाकई इस वॉलेट को हटाना चाहते हैं?" + "value" : "स्थिर IP" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Apakah Anda yakin ingin menghapus dompet ini?" + "value" : "IP Tetap" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Sei sicuro di voler rimuovere questo portafoglio?" + "value" : "IP fisso" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "このウォレットを削除してもよろしいですか?" + "value" : "固定IP" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "이 지갑을 삭제하시겠습니까?" + "value" : "고정 IP" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Ben je zeker dat je deze wallet wilt verwijderen?" + "value" : "Vast IP-adres" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Czy na pewno chcesz usunąć ten portfel?" + "value" : "Stały adres IP" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "IP fixo" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Tem certeza que deseja remover esta carteira?" + "value" : "IP fixo" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Tem a certeza que pretende remover esta carteira?" + "value" : "IP fixo" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Вы уверены, что хотите удалить этот кошелек?" + "value" : "Фиксированный IP" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Är du säker på att du vill ta bort denna plånbok?" + "value" : "Fast IP" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "คุณแน่ใจหรือไม่ว่าต้องการลบกระเป๋าเงินนี้?" + "value" : "IP แบบคงที่" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Ви впевнені, що хочете видалити цей гаманець?" + "value" : "Фіксований IP" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Bạn có chắc chắn muốn xóa ví này không?" + "value" : "IP cố định" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "你确定要移除这个钱包吗?" + "value" : "固定 IP" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "你確定要移除這個錢包嗎?" + "value" : "固定 IP" } } } }, - "Attach logs to feedback (optional)" : { + "Forgot your password?" : { + "comment" : "Forgot your password?", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "إرفاق السجلات مع الملاحظات (اختياري)" + "value" : "هل نسيت كلمة مرورك؟" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Přiložit logy k zpětné vazbě (volitelné)" + "value" : "Zapomněli jste heslo?" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Logs an Feedback anhängen (optional)" + "value" : "Passwort vergessen?" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Επισυνάψτε τα αρχεία καταγραφής στα σχόλια (προαιρετικό)" + "value" : "Ξέχασες τον κωδικό σου;" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Attach logs to feedback (optional)" + "value" : "Forgot your password?" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Adjuntar registros al comentario (opcional)" + "value" : "¿Olvidaste tu contraseña?" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Adjuntar registros al comentario (opcional)" + "value" : "¿Olvidaste tu contraseña?" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "¿Olvidaste tu contraseña?" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Joindre les journaux au retour (optionnel)" + "value" : "Mot de passe oublié ?" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "צרף לוגים למשוב (אופציונלי)" + "value" : "שכחת את הסיסמה?" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "फीडबैक में लॉग संलग्न करें (वैकल्पिक)" + "value" : "अपना पासवर्ड भूल गए?" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Lampirkan log ke umpan balik (opsional)" + "value" : "Lupa kata sandi Anda?" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Allega i log al feedback (opzionale)" + "value" : "Hai dimenticato la password?" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "フィードバックにログを添付(任意)" + "value" : "パスワードをお忘れですか?" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "피드백에 로그 첨부 (선택 사항)" + "value" : "비밀번호를 잊으셨나요?" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Logbestanden toevoegen aan feedback (optioneel)" + "value" : "Wachtwoord vergeten?" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Dołącz logi do opinii (opcjonalnie)" + "value" : "Zapomniałeś hasła?" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Esqueceu sua senha?" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Anexar logs ao feedback (opcional)" + "value" : "Esqueceu sua senha?" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Anexar logs ao feedback (opcional)" + "value" : "Esqueceu sua senha?" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Прикрепить логи к отзыву (необязательно)" + "value" : "Забыли пароль?" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Bifoga loggar till feedback (valfritt)" + "value" : "Glömt ditt lösenord?" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "แนบล็อกไปกับความคิดเห็น (ไม่บังคับ)" + "value" : "ลืมรหัสผ่านใช่หรือไม่?" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Додати логи до відгуку (необов’язково)" + "value" : "Забули пароль?" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Đính kèm nhật ký vào phản hồi (tùy chọn)" + "value" : "Quên mật khẩu?" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "附加日志到反馈(可选)" + "value" : "忘记密码?" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "附加日誌到回饋(可選)" + "value" : "忘記密碼?" } } } }, - "Auth code" : { + "Free" : { + "comment" : "Display name for the paid plan. The WIRE value stays \"supporter\" (subscription_type / current_subscription.plan) because already-shipped app versions parse that string; only what the user reads changes.", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "رمز المصادقة" + "value" : "مجاني" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Autorizační kód" + "value" : "Zdarma" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Auth-Code" + "value" : "Kostenlos" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Κωδικός ταυτοποίησης" + "value" : "Δωρεάν" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Auth code" + "value" : "Free" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Código de autenticación" + "value" : "Gratis" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Código de autenticación" + "value" : "Gratis" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gratis" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Code d'authentification" + "value" : "Gratuit" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "קוד אימות" + "value" : "חינם" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "प्रमाणीकरण कोड" + "value" : "निःशुल्क" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Kode otentikasi" + "value" : "Gratis" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Codice di autenticazione" + "value" : "Gratuito" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "認証コード" + "value" : "無料" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "인증 코드" + "value" : "무료" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Authenticatiecode" + "value" : "Gratis" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Kod uwierzytelniający" + "value" : "Darmowy" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Grátis" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Código de autenticação" + "value" : "Grátis" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Código de autenticação" + "value" : "Grátis" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Код авторизации" + "value" : "Бесплатно" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Autentiseringskod" + "value" : "Gratis" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "รหัสยืนยันตัวตน" + "value" : "แบบฟรี" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Код автентифікації" + "value" : "Безкоштовно" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Mã xác thực" + "value" : "Miễn phí" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "认证码" + "value" : "自由" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "認證碼" - } - } - } - }, - "Auth Code \n\n%@...%@" : { - "comment" : "A message that displays the first and last six characters of an auth code.", - "isCommentAutoGenerated" : true, - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "new", - "value" : "Auth Code \n\n%1$@...%2$@" + "value" : "免費" } } } }, - "Auth Code Create" : { + "General" : { + "comment" : "Call to action", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "إنشاء رمز المصادقة" + "value" : "عام" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Vytvořit autorizační kód" + "value" : "Obecné" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Auth-Code erstellen" + "value" : "Allgemein" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Δημιουργία κωδικού ταυτοποίησης" + "value" : "Γενικά" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Auth Code Create" + "value" : "General" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Crear código de autenticación" + "value" : "General" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Crear código de autenticación" + "value" : "General" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "General" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Créer un code d'authentification" + "value" : "Général" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "צור קוד אימות" + "value" : "כללי" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "प्रमाणीकरण कोड बनाएं" + "value" : "सामान्य" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Buat kode otentikasi" + "value" : "Umum" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Crea codice di autenticazione" + "value" : "Generale" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "認証コード作成" + "value" : "一般" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "인증 코드 생성" + "value" : "일반" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Authenticatiecode aanmaken" + "value" : "Algemeen" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Utwórz kod uwierzytelniający" + "value" : "Ogólne" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Geral" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Criar código de autenticação" + "value" : "Geral" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Criar código de autenticação" + "value" : "Geral" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Создать код авторизации" + "value" : "Общие" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Skapa autentiseringskod" + "value" : "Allmänt" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "สร้างรหัสยืนยันตัวตน" + "value" : "ทั่วไป" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Створити код автентифікації" + "value" : "Загальні" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Tạo mã xác thực" + "value" : "Chung" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "创建认证码" + "value" : "常规" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "創建認證碼" + "value" : "一般" } } } }, - "Auth code login" : { + "Get connected" : { + "comment" : "Call to action", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "تسجيل الدخول برمز المصادقة" + "value" : "اتصل الآن" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Přihlášení pomocí ověřovacího kódu" + "value" : "Připojit se" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Login mit Auth-Code" + "value" : "Verbinden" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Σύνδεση με κωδικό ταυτοποίησης" + "value" : "Συνδεθείτε" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Auth code login" + "value" : "Get connected" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Inicio de sesión con código de autenticación" + "value" : "Conectarse" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Inicio de sesión con código de autenticación" + "value" : "Conéctate" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Conectarse" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Connexion par code d’authentification" + "value" : "Se connecter" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "התחברות עם קוד אימות" + "value" : "התחבר" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "ऑथ कोड लॉगिन" + "value" : "कनेक्ट करें" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Login dengan kode autentikasi" + "value" : "Hubungkan" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Accesso con codice di autenticazione" + "value" : "Connettiti" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "認証コードログイン" + "value" : "接続する" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "인증 코드 로그인" + "value" : "연결하기" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Inloggen met auth-code" + "value" : "Verbinden" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Logowanie kodem autoryzacyjnym" + "value" : "Połącz się" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ligar-se" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Login com código de autenticação" + "value" : "Conectar-se" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Login com código de autenticação" + "value" : "Ligar-se" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Вход по коду авторизации" + "value" : "Подключиться" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Inloggning med autentiseringskod" + "value" : "Anslut" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "เข้าสู่ระบบด้วยรหัสยืนยัน" + "value" : "เชื่อมต่อ" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Вхід за кодом авторизації" + "value" : "Підключитися" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Đăng nhập bằng mã xác thực" + "value" : "Kết nối" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "认证码登录" + "value" : "连接" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "認證碼登入" + "value" : "連接" } } } }, - "Authentication code" : { + "Get free access to the community edition." : { + "comment" : "Call to action", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "رمز المصادقة" + "value" : "شارك في الشبكة واحصل على وصول مجاني إلى إصدار المجتمع." } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Ověřovací kód" + "value" : "Zapojte se do sítě a získejte bezplatný přístup k komunitní edici." } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Authentifizierungscode" + "value" : "Nimm am Netzwerk teil und erhalte kostenlosen Zugang zur Community Edition." } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Κωδικός ταυτοποίησης" + "value" : "Συμμετέχετε στο δίκτυο και αποκτήστε δωρεάν πρόσβαση στην έκδοση κοινότητας." } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Authentication code" + "value" : "Get free access to the community edition." } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Código de autenticación" + "value" : "Participa en la red y obtén acceso gratuito a la edición comunitaria." } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Código de autenticación" + "value" : "Obtén acceso gratuito a la edición comunitaria." + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Participa en la red y obtén acceso gratuito a la edición comunitaria." } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Code d’authentification" + "value" : "Participez au réseau et obtenez un accès gratuit à l’édition communautaire." } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "קוד אימות" + "value" : "קבל גישה חינם למהדורת הקהילה." } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "ऑथेंटिकेशन कोड" + "value" : "नेटवर्क में भाग लें और कम्युनिटी एडिशन तक मुफ्त पहुंच प्राप्त करें।" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Kode autentikasi" + "value" : "Dapatkan akses gratis ke edisi komunitas." } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Codice di autenticazione" + "value" : "Ottieni l'accesso gratuito all'edizione community." } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "認証コード" + "value" : "ネットワークに参加して、コミュニティ版に無料でアクセスしましょう。" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "인증 코드" + "value" : "네트워크에 참여하고 커뮤니티 에디션을 무료로 이용하세요." } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Authenticatiecode" + "value" : "Neem deel aan het netwerk en krijg gratis toegang tot de community-editie." } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Kod autoryzacyjny" + "value" : "Dołącz do sieci i uzyskaj bezpłatny dostęp do wersji społecznościowej." + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Participe na rede e tenha acesso gratuito à edição comunitária." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Código de autenticação" + "value" : "Participe da rede e tenha acesso gratuito à edição comunitária." } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Código de autenticação" + "value" : "Tenha acesso gratuito à edição comunitária." } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Код авторизации" + "value" : "Участвуйте в сети и получите бесплатный доступ к версии для сообщества." } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Autentiseringskod" + "value" : "Få gratis tillgång till community-utgåvan." } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "รหัสยืนยัน" + "value" : "เข้าร่วมเครือข่ายและรับสิทธิ์เข้าถึงฉบับชุมชนฟรี" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Код авторизації" + "value" : "Отримайте безкоштовний доступ до спільнотної версії." } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Mã xác thực" + "value" : "Nhận quyền truy cập miễn phí vào phiên bản cộng đồng." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "认证码" + "value" : "参与网络并免费获得社区版的访问权限。" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "認證碼" + "value" : "免費獲得社群版存取權限。" } } } }, - "Auto" : { + "Get in touch" : { + "comment" : "Feedback Screen", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "تلقائي" + "value" : "تواصل معنا" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Automaticky" + "value" : "Spojte se s námi" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Automatisch" + "value" : "Kontakt aufnehmen" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Αυτόματο" + "value" : "Επικοινωνήστε" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Auto" + "value" : "Get in touch" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Automático" + "value" : "Ponte en contacto" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Automático" + "value" : "Ponte en contacto" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ponte en contacto" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Auto" + "value" : "Entrer en contact" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "אוטומטי" + "value" : "צור קשר" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "स्वचालित" + "value" : "संपर्क करें" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Otomatis" + "value" : "Hubungi kami" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Automatico" + "value" : "Contattaci" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "自動" + "value" : "お問い合わせ" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "자동" + "value" : "연락하기" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Auto" + "value" : "Neem contact op" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Automatyczny" + "value" : "Skontaktuj się" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Entrar em contato" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Automático" + "value" : "Entrar em contato" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Automático" + "value" : "Entre em contato" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Авто" + "value" : "Свяжитесь с нами" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Auto" + "value" : "Kontakta oss" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "อัตโนมัติ" + "value" : "ติดต่อเรา" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Авто" + "value" : "Зв'яжіться з нами" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Tự động" + "value" : "Liên hệ với chúng tôi" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "自动" + "value" : "保持联系" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "自動" + "value" : "聯繫我們" } } } }, - "Available providers" : { + "Get Pro" : { + "comment" : "Call to action", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "مزودي الخدمة المتاحين" + "value" : "احصل على Pro" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Dostupní poskytovatelé" + "value" : "Získat Pro" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Verfügbare Anbieter" + "value" : "Pro holen" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Διαθέσιμοι πάροχοι" + "value" : "Απόκτησε το Pro" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Get Pro" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Proveedores disponibles" + "value" : "Obtener Pro" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Proveedores disponibles" + "value" : "Obtener Pro" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Obtener Pro" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Fournisseurs disponibles" + "value" : "Passer à Pro" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "ספקים זמינים" + "value" : "קבל Pro" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "उपलब्ध प्रदाता" + "value" : "प्रो प्राप्त करें" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Penyedia yang tersedia" + "value" : "Dapatkan Pro" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Provider disponibili" + "value" : "Ottieni Pro" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "利用可能なプロバイダー" + "value" : "Proを取得" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "사용 가능한 제공업체" + "value" : "Pro로 업그레이드" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Beschikbare providers" + "value" : "Word Pro" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Dostępni dostawcy" + "value" : "Zdobądź Pro" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Obter Pro" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Provedores disponíveis" + "value" : "Obter Pro" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Fornecedores disponíveis" + "value" : "Obter Pro" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Доступные провайдеры" + "value" : "Получить Pro" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Tillgängliga leverantörer" + "value" : "Skaffa Pro" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "ผู้ให้บริการที่มีให้เลือก" + "value" : "รับ Pro" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Доступні провайдери" + "value" : "Отримати Pro" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Nhà cung cấp khả dụng" + "value" : "Nhận Pro" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "可用供应商" + "value" : "升级为 Pro" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "可用供應商" + "value" : "獲取 Pro" } } } }, - "Average reliability" : { + "Get started" : { + "comment" : "Button text for beginning the login or create network process used on the Initial Auth screen.", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "متوسط الموثوقية" + "value" : "البدء" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Průměrná spolehlivost" + "value" : "Začněte" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Durchschnittliche Zuverlässigkeit" + "value" : "Loslegen" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Μέση αξιοπιστία" + "value" : "Ξεκίνα" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Average reliability" + "value" : "Get started" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Fiabilidad media" + "value" : "Empezar" } }, - "es-419" : { + "es-MX" : { "stringUnit" : { "state" : "translated", - "value" : "Fiabilidad media" + "value" : "Empezar" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Fiabilité moyenne" + "value" : "Commencer" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "אמינות ממוצעת" + "value" : "התחל" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "औसत विश्वसनीयता" - } - }, - "id" : { - "stringUnit" : { - "state" : "translated", - "value" : "Rata-rata keandalan" - } - }, - "it" : { - "stringUnit" : { - "state" : "translated", - "value" : "Affidabilità media" + "value" : "आरंभ करें" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "平均信頼性" + "value" : "始める" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "평균 신뢰성" + "value" : "시작하기" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Gemiddelde betrouwbaarheid" + "value" : "Aan de slag" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Średnia niezawodność" + "value" : "Rozpocznij" } }, - "pt-BR" : { + "pt" : { "stringUnit" : { "state" : "translated", - "value" : "Confiabilidade média" + "value" : "Começar" } }, - "pt-PT" : { + "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Confiabilidade média" + "value" : "Começar" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Средняя надежность" - } - }, - "sv" : { - "stringUnit" : { - "state" : "translated", - "value" : "Genomsnittlig tillförlitlighet" + "value" : "Начать" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "ความน่าเชื่อถือเฉลี่ย" + "value" : "เริ่มต้นใช้งาน" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Середня надійність" + "value" : "Почати" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Độ tin cậy trung bình" + "value" : "Bắt đầu" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "平均可靠性" - } - }, - "zh-HK" : { - "stringUnit" : { - "state" : "translated", - "value" : "平均可靠性" + "value" : "开始使用" } } } }, - "Average Reliability" : { + "Got it" : { + "comment" : "Used in Shared/Views/UpgradeSubscriptionSheet.swift.", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "متوسط الموثوقية" + "value" : "فهمت" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Průměrná spolehlivost" + "value" : "Rozumím" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Durchschnittliche Zuverlässigkeit" + "value" : "Verstanden" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Μέση αξιοπιστία" + "value" : "Κατάλαβα" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Got it" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Confiabilidad promedio" + "value" : "Entendido" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Confiabilidad promedio" + "value" : "Entendido" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Fiabilité moyenne" + "value" : "Compris" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "אמינות ממוצעת" + "value" : "הבנתי" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "औसत विश्वसनीयता" + "value" : "समझ गया" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Keandalan rata-rata" + "value" : "Mengerti" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Affidabilità media" + "value" : "Ho capito" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "平均信頼性" + "value" : "了解" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "평균 신뢰성" + "value" : "확인" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Gemiddelde betrouwbaarheid" + "value" : "Begrepen" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Średnia niezawodność" + "value" : "Rozumiem" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Confiabilidade média" + "value" : "Entendi" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Confiabilidade média" + "value" : "Entendido" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Средняя надежность" + "value" : "Понятно" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Genomsnittlig tillförlitlighet" + "value" : "Uppfattat" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "ความน่าเชื่อถือเฉลี่ย" + "value" : "รับทราบ" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Середня надійність" + "value" : "Зрозуміло" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Độ tin cậy trung bình" + "value" : "Đã hiểu" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "平均可靠性" + "value" : "知道了" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "平均可靠性" + "value" : "知道了" } } } }, - "Balance code" : { + "Guest" : { + "comment" : "Account Screen", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "رمز الرصيد" + "value" : "ضيف" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Kód na zůstatek" + "value" : "Host" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Guthabencode" + "value" : "Gast" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Κωδικός υπολοίπου" + "value" : "Επισκέπτης" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Balance code" + "value" : "Guest" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Código de saldo" + "value" : "Invitado" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Código de saldo" + "value" : "Invitado" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Invitado" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Code de solde" + "value" : "Invité" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "קוד יתרה" + "value" : "אורח" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "बैलेंस कोड" + "value" : "अतिथि" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Kode saldo" + "value" : "Tamu" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Codice saldo" + "value" : "Ospite" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "残高コード" + "value" : "ゲスト" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "잔액 코드" + "value" : "게스트" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Saldo code" + "value" : "Gast" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Kod salda" + "value" : "Gość" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Convidado" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Código de saldo" + "value" : "Convidado" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Código de saldo" + "value" : "Convidado" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Код баланса" + "value" : "Гость" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Saldo-kod" + "value" : "Gäst" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "รหัสยอดเงิน" + "value" : "ผู้เยี่ยมชม" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Код балансу" + "value" : "Гість" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Mã số dư" + "value" : "Khách" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "余额码" + "value" : "访客" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "餘額碼" + "value" : "訪客" } } } }, - "Balance Codes" : { + "Hide Provider List" : { + "comment" : "Used in Main/Connect/ConnectView-macOS.swift.", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "رموز الرصيد" + "value" : "إخفاء قائمة المزودين" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Kódy na zůstatek" + "value" : "Skrýt seznam poskytovatelů" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Guthabencodes" + "value" : "Anbieter-Liste ausblenden" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Κωδικοί υπολοίπου" + "value" : "Απόκρυψη λίστας παρόχων" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Balance Codes" + "value" : "Hide Provider List" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Códigos de saldo" + "value" : "Ocultar lista de proveedores" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Códigos de saldo" + "value" : "Ocultar lista de proveedores" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Codes de solde" + "value" : "Masquer la liste des fournisseurs" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "קודי יתרה" + "value" : "הסתר רשימת ספקים" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "बैलेंस कोड्स" + "value" : "प्रदाता सूची छिपाएं" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Kode saldo" + "value" : "Sembunyikan Daftar Penyedia" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Codici saldo" + "value" : "Nascondi elenco provider" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "残高コード" + "value" : "プロバイダーリストを非表示" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "잔액 코드" + "value" : "제공업체 목록 숨기기" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Saldo codes" + "value" : "Verberg Provider Lijst" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Kody salda" + "value" : "Ukryj listę dostawców" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Códigos de saldo" + "value" : "Ocultar lista de provedores" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Códigos de saldo" + "value" : "Ocultar lista de fornecedores" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Коды баланса" + "value" : "Скрыть список провайдеров" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Saldo-koder" + "value" : "Dölj leverantörslista" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "รหัสยอดเงิน" + "value" : "ซ่อนรายชื่อผู้ให้บริการ" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Коди балансу" + "value" : "Приховати список провайдерів" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Các mã số dư" + "value" : "Ẩn danh sách nhà cung cấp" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "余额码" + "value" : "隐藏供应商列表" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "餘額碼" + "value" : "隱藏供應商列表" } } } }, - "Become a" : { + "How are we doing?" : { + "comment" : "Call to action", "localizations" : { - "cs" : { + "ar" : { "stringUnit" : { "state" : "translated", - "value" : "Staň se" + "value" : "كيف حالنا؟" } }, - "nl" : { + "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Word een " + "value" : "Jak se nám daří?" } - } - } - }, - "Best available provider" : { - "localizations" : { - "ar" : { + }, + "de" : { "stringUnit" : { "state" : "translated", - "value" : "أفضل مزود متاح" + "value" : "Wie geht es uns?" } }, - "cs" : { + "el" : { "stringUnit" : { "state" : "translated", - "value" : "Nejlepší dostupný poskytovatel" + "value" : "Πώς τα πάμε;" } }, - "de" : { + "en" : { "stringUnit" : { "state" : "translated", - "value" : "Bester verfügbarer Anbieter" + "value" : "How are we doing?" } }, - "el" : { + "es" : { "stringUnit" : { "state" : "translated", - "value" : "Καλύτερος διαθέσιμος πάροχος" + "value" : "¿Cómo lo estamos haciendo?" } }, - "es" : { + "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Mejor proveedor disponible" + "value" : "¿Cómo lo estamos haciendo?" } }, - "es-419" : { + "es-MX" : { "stringUnit" : { "state" : "translated", - "value" : "Mejor proveedor disponible" + "value" : "¿Cómo lo estamos haciendo?" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Meilleur fournisseur disponible" + "value" : "Comment allons-nous ?" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "הספק הזמין הטוב ביותר" + "value" : "איך אנחנו עושים את זה?" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "सर्वश्रेष्ठ उपलब्ध प्रदाता" + "value" : "हम कैसा कर रहे हैं?" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Penyedia terbaik yang tersedia" + "value" : "Bagaimana kinerja kami?" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Miglior provider disponibile" + "value" : "Come stiamo andando?" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "最適なプロバイダー" + "value" : "私たちの調子はどうですか?" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "최적의 제공업체" + "value" : "우리는 어떻게 하고 있나요?" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Beste beschikbare aanbieder" + "value" : "Hoe gaat het met ons?" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Najlepszy dostępny dostawca" + "value" : "Jak nam idzie?" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Como estamos a fazer?" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Melhor provedor disponível" + "value" : "Como estamos indo?" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Melhor fornecedor disponível" + "value" : "Como estamos a ir?" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Лучший доступный провайдер" + "value" : "Как наши успехи?" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Bästa tillgängliga leverantör" + "value" : "Hur går det för oss?" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "ผู้ให้บริการที่ดีที่สุด" + "value" : "ใช้งานเป็นยังไงบ้าง?" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Найкращий доступний провайдер" + "value" : "Як ми справляємось?" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Nhà cung cấp tốt nhất hiện có" + "value" : "Chúng tôi đang làm thế nào?" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "最佳可用供应商" + "value" : "我们做得怎么样?" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "最佳可用供應商" + "value" : "我們做得如何?" } } } }, - "Blocked" : { - "comment" : "Label for the blocked traffic relayed for remote clients.", - "isCommentAutoGenerated" : true - }, - "Blocked location could not be added. Please try again later." : { - - }, - "Blocked location could not be removed. Please try again later." : { - - }, - "Blocked locations" : { - - }, - "Blocked Locations" : { - - }, - "Bonus referral code" : { + "I agree to URnetwork's [Terms and Services](https://ur.io/terms) and [Privacy Policy](https://ur.io/privacy)" : { + "comment" : "Used in Authenticate/CreateNetwork/CreateNetworkView.swift.", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "رمز الإحالة المكافأة" + "value" : "أوافق على [شروط خدمة] URnetwork (https://ur.io/terms) و [سياسة الخصوصية] (https://ur.io/privacy)" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Bonusový referální kód" + "value" : "Souhlasím s [Podmínkami a službami](https://ur.io/terms) a [Zásadami ochrany osobních údajů](https://ur.io/privacy) společnosti URnetwork." } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Bonus-Empfehlungscode" + "value" : "Ich stimme den [Nutzungsbedingungen](https://ur.io/terms) und der [Datenschutzrichtlinie](https://ur.io/privacy) von URnetwork zu." } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Κωδικός παραπομπής μπόνους" + "value" : "Συμφωνώ με τους [Όρους και τις Υπηρεσίες](https://ur.io/terms) και την [Πολιτική Απορρήτου](https://ur.io/privacy) της URnetwork" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Bonus referral code" + "value" : "I agree to URnetwork's [Terms and Services](https://ur.io/terms) and [Privacy Policy](https://ur.io/privacy)" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Código de referencia de bonificación" + "value" : "Acepto los [Términos y servicios](https://ur.io/terms) y la [Política de privacidad](https://ur.io/privacy) de URnetwork" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Código de referencia de bonificación" + "value" : "Acepto los [Términos y servicios](https://ur.io/terms) y la [Política de privacidad](https://ur.io/privacy) de URnetwork" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Code de parrainage bonus" + "value" : "J'accepte les [Conditions d'utilisation](https://ur.io/terms) et la [Politique de confidentialité](https://ur.io/privacy) d'URnetwork" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "קוד הפניה בונוס" + "value" : "אני מסכים ל[תנאים והשירותים](https://ur.io/terms) ול[מדיניות הפרטיות](https://ur.io/privacy) של URnetwork" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "बोनस रेफरल कोड" + "value" : "मैं URnetwork के [नियम और सेवाएँ](https://ur.io/terms) और [गोपनीयता नीति](https://ur.io/privacy) से सहमत हूँ" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Kode referensi bonus" + "value" : "Saya menyetujui [Persyaratan dan Layanan](https://ur.io/terms) dan [Kebijakan Privasi](https://ur.io/privacy) URnetwork" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Codice di riferimento bonus" + "value" : "Accetto i [Termini di servizio](https://ur.io/terms) e l'[Informativa sulla privacy](https://ur.io/privacy) di URnetwork" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "ボーナス紹介コード" + "value" : "URnetworkの[利用規約](https://ur.io/terms)および[プライバシーポリシー](https://ur.io/privacy)に同意します" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "보너스 추천 코드" + "value" : "URnetwork의 [이용약관](https://ur.io/terms) 및 [개인정보 보호정책](https://ur.io/privacy)에 동의합니다." } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Bonus referentiecode" + "value" : "Ik ga akkoord met de [Algemene Voorwaarden](https://ur.io/terms) en het [Privacybeleid](https://ur.io/privacy) van URnetwork" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Kod polecający bonus" + "value" : "Zgadzam się na [Warunki świadczenia usług](https://ur.io/terms) i [Politykę prywatności](https://ur.io/privacy) URnetwork" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Código de referência bônus" + "value" : "Eu concordo com os [Termos e Serviços](https://ur.io/terms) e a [Política de Privacidade](https://ur.io/privacy) do URnetwork" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Código de referência bônus" + "value" : "Concordo com os [Termos e Serviços](https://ur.io/terms) e a [Política de Privacidade](https://ur.io/privacy) da URnetwork" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Бонусный реферальный код" + "value" : "Я согласен с [Условиями обслуживания](https://ur.io/terms) и [Политикой конфиденциальности](https://ur.io/privacy) URnetwork" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Bonusreferenskod" + "value" : "Jag godkänner URnetworks [Användarvillkor](https://ur.io/terms) och [Integritetspolicy](https://ur.io/privacy)" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "รหัสอ้างอิงโบนัส" + "value" : "ฉันยอมรับ[ข้อกำหนดในการให้บริการ](https://ur.io/terms) และ[นโยบายความเป็นส่วนตัว](https://ur.io/privacy) ของ URnetwork" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Бонусний реферальний код" + "value" : "Я погоджуюся з [Умовами надання послуг](https://ur.io/terms) та [Політикою конфіденційності](https://ur.io/privacy) URnetwork" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Mã giới thiệu thưởng" + "value" : "Tôi đồng ý với [Điều khoản và Dịch vụ](https://ur.io/terms) và [Chính sách Bảo mật](https://ur.io/privacy) của URnetwork" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "奖励推荐码" + "value" : "我同意 URnetwork 的[条款和服务](https://ur.io/terms)和[隐私政策](https://ur.io/privacy)" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "獎勵推薦碼" + "value" : "我同意 URnetwork 的[條款和服務](https://ur.io/terms)和[隱私政策](https://ur.io/privacy)" } } } }, - "Boost Your Bandwidth" : { - "comment" : "A title for the introductory usage bar.", - "isCommentAutoGenerated" : true - }, - "Boost Your Bandwidth - Earn More Every Day" : { - "extractionState" : "stale", + "If the problem persists, contact us at [support@ur.io](mailto:support@ur.io) or [join our Discord](https://discord.com/invite/RUNZXMwPRK) for direct support." : { + "comment" : "Used in Main/MainView.swift.", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "عزّز عرض النطاق الترددي لديك - اربح المزيد كل يوم" + "value" : "إذا استمرت المشكلة، تواصل معنا عبر [support@ur.io](mailto:support@ur.io) أو [انضم إلى Discord الخاص بنا](https://discord.com/invite/RUNZXMwPRK) للحصول على دعم مباشر." } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Zvyšte svou šířku pásma – vydělávejte více každý den" + "value" : "Pokud problém přetrvává, kontaktujte nás na [support@ur.io](mailto:support@ur.io) nebo [přidejte se na náš Discord](https://discord.com/invite/RUNZXMwPRK) pro přímou podporu." } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Steigern Sie Ihre Bandbreite – verdienen Sie jeden Tag mehr" + "value" : "Wenn das Problem weiterhin besteht, kontaktieren Sie uns unter [support@ur.io](mailto:support@ur.io) oder [treten Sie unserem Discord bei](https://discord.com/invite/RUNZXMwPRK) für direkten Support." } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Ενισχύστε το εύρος ζώνης σας - Κερδίστε περισσότερα κάθε μέρα" + "value" : "Αν το πρόβλημα επιμένει, επικοινωνήστε μαζί μας στο [support@ur.io](mailto:support@ur.io) ή [μπείτε στο Discord μας](https://discord.com/invite/RUNZXMwPRK) για άμεση υποστήριξη." } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Boost Your Bandwidth - Earn More Every Day" + "value" : "If the problem persists, contact us at [support@ur.io](mailto:support@ur.io) or [join our Discord](https://discord.com/invite/RUNZXMwPRK) for direct support." } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Aumenta tu ancho de banda - Gana más cada día" + "value" : "Si el problema persiste, contáctanos en [support@ur.io](mailto:support@ur.io) o [únete a nuestro Discord](https://discord.com/invite/RUNZXMwPRK) para soporte directo." } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Aumenta tu ancho de banda - Gana más cada día" + "value" : "Si el problema persiste, contáctanos en [support@ur.io](mailto:support@ur.io) o [únete a nuestro Discord](https://discord.com/invite/RUNZXMwPRK) para soporte directo." } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Boostez votre bande passante - Gagnez plus chaque jour" + "value" : "Si le problème persiste, contactez-nous à [support@ur.io](mailto:support@ur.io) ou [rejoignez notre Discord](https://discord.com/invite/RUNZXMwPRK) pour une assistance directe." } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "הגבר את רוחב הפס שלך - תרוויח יותר כל יום" + "value" : "אם הבעיה נמשכת, פנו אלינו בכתובת [support@ur.io](mailto:support@ur.io) או [הצטרפו ל-Discord שלנו](https://discord.com/invite/RUNZXMwPRK) לקבלת תמיכה ישירה." } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "अपना बैंडविड्थ बढ़ाएं - हर दिन अधिक कमाएँ" + "value" : "यदि समस्या बनी रहती है, तो [support@ur.io](mailto:support@ur.io) पर हमसे संपर्क करें या सीधे सहायता के लिए [हमारे Discord से जुड़ें](https://discord.com/invite/RUNZXMwPRK)।" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Tingkatkan bandwidth Anda - Dapatkan lebih banyak setiap hari" + "value" : "Jika masalah berlanjut, hubungi kami di [support@ur.io](mailto:support@ur.io) atau [bergabung dengan Discord kami](https://discord.com/invite/RUNZXMwPRK) untuk dukungan langsung." } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Aumenta la tua larghezza di banda - Guadagna di più ogni giorno" + "value" : "Se il problema persiste, contattaci a [support@ur.io](mailto:support@ur.io) oppure [unisciti al nostro Discord](https://discord.com/invite/RUNZXMwPRK) per assistenza diretta." } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "帯域幅を増やして毎日もっと稼ごう" + "value" : "問題が解決しない場合は、[support@ur.io](mailto:support@ur.io) までご連絡いただくか、直接サポートを受けるために[Discordに参加](https://discord.com/invite/RUNZXMwPRK)してください。" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "대역폭을 높이고 매일 더 많이 벌기" + "value" : "문제가 지속되면 [support@ur.io](mailto:support@ur.io)로 연락하시거나 직접 지원을 위해 [Discord에 가입](https://discord.com/invite/RUNZXMwPRK)해 주세요." } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Vergroot je bandbreedte - Verdien elke dag meer" + "value" : "Als het probleem aanhoudt, neem contact met ons op via [support@ur.io](mailto:support@ur.io) of [word lid van onze Discord](https://discord.com/invite/RUNZXMwPRK) voor directe ondersteuning." } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Zwiększ swoje pasmo – zarabiaj więcej każdego dnia" + "value" : "Jeśli problem nadal występuje, skontaktuj się z nami pod adresem [support@ur.io](mailto:support@ur.io) lub [dołącz do naszego Discorda](https://discord.com/invite/RUNZXMwPRK), aby uzyskać bezpośrednie wsparcie." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Aumente sua largura de banda - Ganhe mais todo dia" + "value" : "Se o problema persistir, entre em contato pelo [support@ur.io](mailto:support@ur.io) ou [entre no nosso Discord](https://discord.com/invite/RUNZXMwPRK) para suporte direto." } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Aumente a sua largura de banda - Ganhe mais todos os dias" + "value" : "Se o problema persistir, contacte-nos através de [support@ur.io](mailto:support@ur.io) ou [junte-se ao nosso Discord](https://discord.com/invite/RUNZXMwPRK) para suporte direto." } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Увеличьте свою пропускную способность — зарабатывайте больше каждый день" + "value" : "Если проблема сохраняется, свяжитесь с нами по адресу [support@ur.io](mailto:support@ur.io) или [присоединяйтесь к нашему Discord](https://discord.com/invite/RUNZXMwPRK) для прямой поддержки." } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Öka din bandbredd – tjäna mer varje dag" + "value" : "Om problemet kvarstår, kontakta oss på [support@ur.io](mailto:support@ur.io) eller [gå med i vår Discord](https://discord.com/invite/RUNZXMwPRK) för direkt support." } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "เพิ่มแบนด์วิดท์ของคุณ - รับรายได้มากขึ้นทุกวัน" + "value" : "หากปัญหายังคงอยู่ โปรดติดต่อเราได้ที่ [support@ur.io](mailto:support@ur.io) หรือ [เข้าร่วม Discord ของเรา](https://discord.com/invite/RUNZXMwPRK) เพื่อรับการสนับสนุนโดยตรง" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Збільшіть свою пропускну здатність — заробляйте більше щодня" + "value" : "Якщо проблема не зникає, зв’яжіться з нами на [support@ur.io](mailto:support@ur.io) або [приєднайтеся до нашого Discord](https://discord.com/invite/RUNZXMwPRK), щоб отримати пряму підтримку." } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Tăng băng thông của bạn - Kiếm nhiều hơn mỗi ngày" + "value" : "Nếu vấn đề vẫn tiếp diễn, hãy liên hệ với chúng tôi tại [support@ur.io](mailto:support@ur.io) hoặc [tham gia Discord của chúng tôi](https://discord.com/invite/RUNZXMwPRK) để được hỗ trợ trực tiếp." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "提升你的带宽——每天赚更多" + "value" : "如果问题仍然存在,请通过 [support@ur.io](mailto:support@ur.io) 联系我们,或[加入我们的 Discord](https://discord.com/invite/RUNZXMwPRK) 获取直接支持。" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "提升你的頻寬 - 每天賺取更多" + "value" : "若問題持續,請透過 [support@ur.io](mailto:support@ur.io) 與我們聯絡,或[加入我們的 Discord](https://discord.com/invite/RUNZXMwPRK) 以獲得直接支援。" } } } }, - "By default you get 10 GiB/month. Here's how you can earn extra bandwidth:" : { + "Includes 2 week free trial" : { + "comment" : "Used in Shared/Views/ProductOptionCard.swift.", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "بشكل افتراضي، تحصل على 10 غيغابايت/شهر. إليك كيف يمكنك كسب عرض نطاق إضافي:" + "value" : "يتضمن تجربة مجانية لمدة أسبوعين" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Ve výchozím nastavení získáte 10 GiB/měsíc. Zde je návod, jak můžete získat další šířku pásma:" + "value" : "Zahrnuje dvoutýdenní zkušební verzi zdarma" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Standardmäßig erhalten Sie 10 GiB/Monat. So können Sie zusätzliche Bandbreite verdienen:" + "value" : "Enthält zweiwöchige kostenlose Testphase" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Από προεπιλογή λαμβάνετε 10 GiB/μήνα. Δείτε πώς μπορείτε να κερδίσετε επιπλέον εύρος ζώνης:" + "value" : "Περιλαμβάνει δωρεάν δοκιμή δύο εβδομάδων" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "By default you get 10 GiB/month. Here's how you can earn extra bandwidth:" + "value" : "Includes 2 week free trial" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Por defecto recibes 10 GiB/mes. Así puedes ganar ancho de banda extra:" + "value" : "Incluye prueba gratuita de 2 semanas" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Por defecto recibes 10 GiB/mes. Así puedes ganar ancho de banda extra:" + "value" : "Incluye prueba gratuita de 2 semanas" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Par défaut, vous obtenez 10 Go/mois. Voici comment gagner de la bande passante supplémentaire :" + "value" : "Comprend un essai gratuit de 2 semaines" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "כברירת מחדל תקבל 10 GiB/חודש. כך תוכל להרוויח רוחב פס נוסף:" + "value" : "כולל ניסיון חינם לשבועיים" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "डिफ़ॉल्ट रूप से आपको 10 GiB/माह मिलता है। ऐसे आप अतिरिक्त बैंडविड्थ कमा सकते हैं:" + "value" : "2 सप्ताह की मुफ्त ट्रायल शामिल है" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Secara default Anda mendapatkan 10 GiB/bulan. Berikut cara mendapatkan bandwidth tambahan:" + "value" : "Termasuk uji coba gratis 2 minggu" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Di default ricevi 10 GiB/mese. Ecco come puoi guadagnare larghezza di banda extra:" + "value" : "Include prova gratuita di 2 settimane" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "デフォルトで毎月10 GiBが付与されます。追加の帯域幅を獲得する方法はこちら:" + "value" : "2週間の無料トライアル付き" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "기본적으로 10 GiB/월을 받습니다. 추가 대역폭을 얻는 방법은 다음과 같습니다:" + "value" : "2주 무료 체험 포함" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Standaard krijg je 10 GiB/maand. Zo kun je extra bandbreedte verdienen:" + "value" : "Inclusief 2 weken gratis proefperiode" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Domyślnie otrzymujesz 10 GiB/miesiąc. Oto jak możesz zdobyć dodatkowe pasmo:" + "value" : "Zawiera 2-tygodniowy bezpłatny okres próbny" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Por padrão, você recebe 10 GiB/mês. Veja como ganhar largura de banda extra:" + "value" : "Inclui teste gratuito de 2 semanas" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Por defeito, recebe 10 GiB/mês. Veja como pode ganhar largura de banda extra:" + "value" : "Inclui teste gratuito de 2 semanas" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "По умолчанию вы получаете 10 ГиБ/месяц. Вот как можно заработать дополнительную пропускную способность:" + "value" : "Включает двухнедельный бесплатный пробный период" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Som standard får du 10 GiB/månad. Så här kan du tjäna extra bandbredd:" + "value" : "Inkluderar 2 veckors gratis provperiod" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "โดยปกติคุณจะได้รับ 10 GiB/เดือน นี่คือวิธีรับแบนด์วิดท์เพิ่มเติม:" + "value" : "รวมทดลองใช้งานฟรี 2 สัปดาห์" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "За замовчуванням ви отримуєте 10 ГіБ/місяць. Ось як можна отримати додаткову пропускну здатність:" + "value" : "Включає двотижневий безкоштовний пробний період" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Mặc định bạn nhận được 10 GiB/tháng. Đây là cách bạn có thể kiếm thêm băng thông:" + "value" : "Bao gồm dùng thử miễn phí 2 tuần" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "默认每月获得10 GiB。以下是赚取额外带宽的方法:" + "value" : "包含2周免费试用" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "預設每月獲得10 GiB。以下是賺取額外頻寬的方法:" + "value" : "包括2星期免費試用" } } } }, - "By participating in the network and providing bandwidth you can earn rewards." : { - "extractionState" : "stale", + "Insufficient balance" : { + "comment" : "Call to action", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "من خلال المشاركة في الشبكة وتوفير عرض النطاق الترددي، يمكنك كسب المكافآت." + "value" : "رصيد غير كافٍ" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Účastí v síti a poskytováním šířky pásma můžete získat odměny." + "value" : "Nedostatečný zůstatek" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Durch die Teilnahme am Netzwerk und das Bereitstellen von Bandbreite können Sie Belohnungen verdienen." + "value" : "Unzureichendes Guthaben" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Συμμετέχοντας στο δίκτυο και παρέχοντας εύρος ζώνης μπορείτε να κερδίσετε ανταμοιβές." + "value" : "Ανεπαρκές υπόλοιπο" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "By participating in the network and providing bandwidth you can earn rewards." + "value" : "Insufficient balance" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Al participar en la red y proporcionar ancho de banda puedes ganar recompensas." + "value" : "Saldo insuficiente" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Al participar en la red y proporcionar ancho de banda puedes ganar recompensas." + "value" : "Saldo insuficiente" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Saldo insuficiente" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "En participant au réseau et en fournissant de la bande passante, vous pouvez gagner des récompenses." + "value" : "Solde insuffisant" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "בהשתתפות ברשת ובמתן רוחב פס תוכל להרוויח תגמולים." + "value" : "יתרה לא מספקת" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "नेटवर्क में भाग लेकर और बैंडविड्थ प्रदान करके आप पुरस्कार कमा सकते हैं।" + "value" : "अपर्याप्त शेष राशि" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Dengan berpartisipasi di jaringan dan menyediakan bandwidth, Anda dapat memperoleh hadiah." + "value" : "Saldo tidak mencukupi" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Partecipando alla rete e fornendo larghezza di banda puoi guadagnare ricompense." + "value" : "Saldo insufficiente" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "ネットワークに参加して帯域幅を提供することで報酬を獲得できます。" + "value" : "残高不足" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "네트워크에 참여하고 대역폭을 제공하면 보상을 받을 수 있습니다." + "value" : "잔액 부족" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Door deel te nemen aan het netwerk en bandbreedte te leveren kun je beloningen verdienen." + "value" : "Onvoldoende saldo" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Uczestnicząc w sieci i udostępniając przepustowość, możesz zdobywać nagrody." + "value" : "Niewystarczające saldo" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Saldo insuficiente" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Ao participar da rede e fornecer largura de banda, você pode ganhar recompensas." + "value" : "Saldo insuficiente" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Ao participar na rede e fornecer largura de banda, pode ganhar recompensas." + "value" : "Saldo insuficiente" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Участвуя в сети и предоставляя пропускную способность, вы можете получать награды." + "value" : "Недостаточно средств" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Genom att delta i nätverket och tillhandahålla bandbredd kan du tjäna belöningar." + "value" : "Otillräckligt saldo" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "โดยเข้าร่วมเครือข่ายและให้แบนด์วิดท์ คุณสามารถรับรางวัลได้" + "value" : "ยอดเงินไม่เพียงพอ" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Беріть участь у мережі та надавайте пропускну здатність, щоб отримувати винагороди." + "value" : "Недостатньо коштів" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Tham gia mạng lưới và cung cấp băng thông, bạn có thể nhận phần thưởng." + "value" : "Không đủ số dư" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "参与网络并提供带宽即可获得奖励。" + "value" : "余额不足" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "參與網絡並提供頻寬即可獲得獎勵。" + "value" : "餘額不足" } } } }, - "By subscribing, you agree to URnetwork's [Terms and Services](https://ur.io/terms) and [Privacy Policy](https://ur.io/privacy)" : { + "Invalid balance code" : { + "comment" : "Used in Shared/Views/RedeemBalanceCodeSheet/RedeemBalanceCodeSheet.swift.", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "بالاشتراك ، أنت توافق على [شروط خدمة] URnetwork (https://ur.io/terms) و [سياسة الخصوصية] (https://ur.io/privacy)" + "value" : "رمز الرصيد غير صالح" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Přihlášením k odběru souhlasíte s [Podmínkami a službami](https://ur.io/terms) a [Zásadami ochrany osobních údajů](https://ur.io/privacy) společnosti URnetwork." + "value" : "Neplatný kód na zůstatek" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Mit dem Abonnieren stimmen Sie den [Nutzungsbedingungen](https://ur.io/terms) und der [Datenschutzrichtlinie](https://ur.io/privacy) von URnetwork zu." + "value" : "Ungültiger Guthabencode" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Με την εγγραφή σας, συμφωνείτε με τους [Όρους και τις Υπηρεσίες](https://ur.io/terms) και την [Πολιτική Απορρήτου](https://ur.io/privacy) της URnetwork" + "value" : "Μη έγκυρος κωδικός υπολοίπου" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "By subscribing, you agree to URnetwork's [Terms and Services](https://ur.io/terms) and [Privacy Policy](https://ur.io/privacy)" + "value" : "Invalid balance code" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Al suscribirte, aceptas los [Términos y servicios](https://ur.io/terms) y la [Política de privacidad](https://ur.io/privacy) de URnetwork" + "value" : "Código de saldo inválido" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Al suscribirte, aceptas los [Términos y servicios](https://ur.io/terms) y la [Política de privacidad](https://ur.io/privacy) de URnetwork" + "value" : "Código de saldo inválido" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "En vous abonnant, vous acceptez les [Conditions d'utilisation](https://ur.io/terms) et la [Politique de confidentialité](https://ur.io/privacy) d'URnetwork" + "value" : "Code de solde invalide" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "על ידי הרשמה כמנוי, אתה מסכים ל[תנאים והשירותים](https://ur.io/terms) ול[מדיניות הפרטיות](https://ur.io/privacy) של URnetwork" + "value" : "קוד יתרה לא חוקי" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "सदस्यता लेने पर, आप URnetwork के [नियम और सेवाएँ](https://ur.io/terms) और [गोपनीयता नीति](https://ur.io/privacy) से सहमत हैं" + "value" : "अमान्य बैलेंस कोड" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Dengan berlangganan, Anda menyetujui [Persyaratan dan Layanan](https://ur.io/terms) dan [Kebijakan Privasi](https://ur.io/privacy) URnetwork" + "value" : "Kode saldo tidak valid" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Con l'iscrizione, accetti i [Termini di servizio](https://ur.io/terms) e l'[Informativa sulla privacy](https://ur.io/privacy) di URnetwork" + "value" : "Codice saldo non valido" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "サブスクライブすると、URnetworkの[利用規約](https://ur.io/terms)および[プライバシーポリシー](https://ur.io/privacy)に同意したことになります" + "value" : "無効な残高コード" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "구독하면 URnetwork의 [이용약관](https://ur.io/terms) 및 [개인정보 보호정책](https://ur.io/privacy)에 동의하는 것입니다." + "value" : "잘못된 잔액 코드" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Door je te abonneren, ga je akkoord met de [Algemene Voorwaarden](https://ur.io/terms) en [Privacybeleid](https://ur.io/privacy) van URnetwork" + "value" : "Ongeldige saldo code" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Subskrybując, zgadzasz się na [Warunki świadczenia usług](https://ur.io/terms) i [Politykę prywatności](https://ur.io/privacy) URnetwork" + "value" : "Nieprawidłowy kod salda" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Ao se inscrever, você concorda com os [Termos e Serviços](https://ur.io/terms) e a [Política de Privacidade](https://ur.io/privacy) do URnetwork" + "value" : "Código de saldo inválido" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Ao subscrever, concorda com os [Termos e Serviços](https://ur.io/terms) e a [Política de Privacidade](https://ur.io/privacy) da URnetwork" + "value" : "Código de saldo inválido" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Подписываясь, вы соглашаетесь с [Условиями обслуживания](https://ur.io/terms) и [Политикой конфиденциальности](https://ur.io/privacy) URnetwork" + "value" : "Недействительный код баланса" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Genom att prenumerera godkänner du URnetworks [Användarvillkor](https://ur.io/terms) och [Integritetspolicy](https://ur.io/privacy)" + "value" : "Ogiltig saldo-kod" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "ในการสมัครสมาชิก แสดงว่าคุณยอมรับ [ข้อกำหนดในการให้บริการ](https://ur.io/terms) และ [นโยบายความเป็นส่วนตัว](https://ur.io/privacy) ของ URnetwork" + "value" : "รหัสยอดเงินไม่ถูกต้อง" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Підписуючись, ви погоджуєтесь з [Умовами надання послуг](https://ur.io/terms) та [Політикою конфіденційності](https://ur.io/privacy) URnetwork" + "value" : "Недійсний код балансу" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Bằng cách đăng ký, bạn đồng ý với [Điều khoản và Dịch vụ](https://ur.io/terms) và [Chính sách Bảo mật](https://ur.io/privacy) của URnetwork" + "value" : "Mã số dư không hợp lệ" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "订阅即表示您同意 URnetwork 的[条款和服务](https://ur.io/terms)和[隐私政策](https://ur.io/privacy)" + "value" : "余额码无效" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "訂閱即表示您同意 URnetwork 的[條款和服務](https://ur.io/terms)和[隱私政策](https://ur.io/privacy)" + "value" : "餘額碼無效" } } } }, - "Cancel" : { + "Invalid referral code. Please try again." : { + "comment" : "Used in Main/Account/Settings/UpdateReferralNetworkSheet/UpdateReferralNetworkSheetViewModel.swift.", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "إلغاء" + "value" : "رمز الإحالة غير صالح. يرجى المحاولة مرة أخرى." } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Zrušit" + "value" : "Neplatný referální kód. Prosím, zkuste to znovu." } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Abbrechen" + "value" : "Ungültiger Empfehlungscode. Bitte versuchen Sie es erneut." } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Ακύρωση" + "value" : "Μη έγκυρος κωδικός παραπομπής. Παρακαλώ δοκιμάστε ξανά." } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Cancel" + "value" : "Invalid referral code. Please try again." } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Cancelar" + "value" : "Código de referencia no válido. Por favor, inténtalo de nuevo." } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Cancelar" + "value" : "Código de referencia no válido. Por favor, inténtalo de nuevo." } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Annuler" + "value" : "Code de parrainage invalide. Veuillez réessayer." } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "ביטול" + "value" : "קוד הפניה לא חוקי. אנא נסה שוב." } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "रद्द करें" + "value" : "अमान्य रेफ़रल कोड। कृपया पुन: प्रयास करें।" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Batal" + "value" : "Kode referensi tidak valid. Silakan coba lagi." } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Annulla" + "value" : "Codice di riferimento non valido. Per favore riprova." } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "キャンセル" + "value" : "無効な紹介コードです。もう一度お試しください。" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "취소" + "value" : "유효하지 않은 추천 코드입니다. 다시 시도해 주세요." } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Annuleer" + "value" : "Ongeldige referentiecode. Probeer het opnieuw." } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Anuluj" + "value" : "Nieprawidłowy kod polecający. Spróbuj ponownie." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Cancelar" + "value" : "Código de indicação inválido. Por favor, tente novamente." } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Cancelar" + "value" : "Código de referência inválido. Por favor, tente novamente." } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Отмена" + "value" : "Недействительный реферальный код. Пожалуйста, попробуйте еще раз." } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Avbryt" + "value" : "Ogiltig referenskod. Försök igen." } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "ยกเลิก" + "value" : "รหัสผู้อ้างอิงไม่ถูกต้อง กรุณาลองใหม่อีกครั้ง" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Скасувати" + "value" : "Недійсний реферальний код. Будь ласка, спробуйте ще раз." } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Hủy" + "value" : "Mã giới thiệu không hợp lệ. Vui lòng thử lại." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "取消" + "value" : "推荐码无效。请再试一次。" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "取消" + "value" : "推薦碼無效。請再試一次。" } } } }, - "Change" : { + "IPv4" : { + "comment" : "Label for a list of IPv4 addresses.", + "isCommentAutoGenerated" : true, "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "تغيير" + "value" : "IPv4" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Změnit" + "value" : "IPv4" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Ändern" + "value" : "IPv4" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Αλλαγή" + "value" : "IPv4" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Change" + "value" : "IPv4" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Cambiar" + "value" : "IPv4" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Cambiar" + "value" : "IPv4" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "IPv4" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Changer" + "value" : "IPv4" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "שנה" + "value" : "IPv4" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "बदलें" + "value" : "IPv4" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Ubah" + "value" : "IPv4" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Cambia" + "value" : "IPv4" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "変更" + "value" : "IPv4" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "변경" + "value" : "IPv4" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Wijzigen" + "value" : "IPv4" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Zmień" + "value" : "IPv4" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "IPv4" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Alterar" + "value" : "IPv4" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Alterar" + "value" : "IPv4" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Изменить" + "value" : "IPv4" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Ändra" + "value" : "IPv4" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "เปลี่ยน" + "value" : "IPv4" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Змінити" + "value" : "IPv4" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Thay đổi" + "value" : "IPv4" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "更改" + "value" : "IPv4" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "更改" + "value" : "IPv4" } } } }, - "Check my IP" : { + "IPv6" : { + "comment" : "Label for the IPv6 list of DNS servers.", + "isCommentAutoGenerated" : true, "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "تحقق من عنوان IP الخاص بي" + "value" : "IPv6" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Zkontrolovat moji IP" + "value" : "IPv6" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Meine IP prüfen" + "value" : "IPv6" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Έλεγχος της IP μου" + "value" : "IPv6" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Check my IP" + "value" : "IPv6" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Comprobar mi IP" + "value" : "IPv6" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Comprobar mi IP" + "value" : "IPv6" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "IPv6" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Vérifier mon IP" + "value" : "IPv6" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "בדוק את ה-IP שלי" + "value" : "IPv6" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "मेरी IP की जाँच करें" + "value" : "IPv6" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Periksa IP saya" + "value" : "IPv6" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Controlla il mio IP" + "value" : "IPv6" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "IPを確認" + "value" : "IPv6" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "내 IP 확인" + "value" : "IPv6" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Mijn IP controleren" + "value" : "IPv6" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Sprawdź moje IP" + "value" : "IPv6" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "IPv6" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Verificar meu IP" + "value" : "IPv6" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Verificar o meu IP" + "value" : "IPv6" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Проверить мой IP" + "value" : "IPv6" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Kontrollera min IP" + "value" : "IPv6" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "ตรวจสอบ IP ของฉัน" + "value" : "IPv6" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Перевірити мою IP" + "value" : "IPv6" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Kiểm tra IP của tôi" + "value" : "IPv6" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "查看我的 IP" + "value" : "IPv6" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "檢查我的 IP" + "value" : "IPv6" } } } }, - "Check your phone" : { + "It's nice to see you again" : { + "comment" : "\tGreeting text on Enter user auth password screen.", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "تحقق من هاتفك" + "value" : "من الجميل رؤيتك مرة أخرى" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Zkontrolujte telefon" + "value" : "Rád vás opět vidím" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Überprüfe dein Telefon" + "value" : "Es ist schön, dich wiederzusehen" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Έλεγξε το τηλέφωνό σου" + "value" : "Χαίρομαι που σε ξαναβλέπω" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Check your phone" + "value" : "It's nice to see you again" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Revisa tu teléfono" + "value" : "Es agradable verte de nuevo" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Revisa tu teléfono" + "value" : "Es agradable verte de nuevo" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Qué gusto verte de nuevo" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Vérifiez votre téléphone" + "value" : "C'est agréable de te revoir" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "בדוק את הטלפון שלך" + "value" : "נחמד לראות אותך שוב" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "अपना फ़ोन जांचें" + "value" : "आपसे फिर से मिलकर अच्छा लगा" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Periksa ponsel Anda" + "value" : "Senang melihatmu lagi" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Controlla il tuo telefono" + "value" : "È bello rivederti" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "スマートフォンを確認してください" + "value" : "また会えて嬉しいです" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "휴대폰을 확인하세요" + "value" : "다시 만나서 반가워요" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Controleer je telefoon" + "value" : "Fijn je weer te zien" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Sprawdź swój telefon" + "value" : "Miło cię znowu widzieć" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "É bom ver você novamente" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Verifique seu telefone" + "value" : "É bom ver você de novo" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Verifique o seu telemóvel" + "value" : "É bom ver você de novo" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Проверьте свой телефон" + "value" : "Приятно снова тебя видеть" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Kontrollera din telefon" + "value" : "Trevligt att se dig igen" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "ตรวจสอบโทรศัพท์ของคุณ" + "value" : "ดีใจที่ได้เจอคุณอีกครั้ง" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Перевірте свій телефон" + "value" : "Приємно знову тебе бачити" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Kiểm tra điện thoại của bạn" + "value" : "Rất vui được gặp lại bạn" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "查看您的手机" + "value" : "很高兴再次见到你" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "檢查您的手機" + "value" : "很高興再次見到你" } } } }, - "Claim multiplier" : { + "Join me on URnetwork! Get the app and enter my referral code when you sign up." : { + "comment" : "Used in Shared/Views/ReferralShareLink/ReferralShareLink.swift.", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "مضاعف المطالبة" + "value" : "انضم إليّ على URnetwork! حمّل التطبيق وأدخل رمز الإحالة الخاص بي عند التسجيل." } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Vyzvedni si znásobovač" + "value" : "Přidejte se ke mně na URnetwork! Stáhněte si aplikaci a při registraci zadejte můj doporučující kód." } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Anspruchs-Multiplikator" + "value" : "Komm zu mir auf URnetwork! Hol dir die App und gib bei der Anmeldung meinen Empfehlungscode ein." } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Πολλαπλασιαστής Αξίωσης" + "value" : "Έλα μαζί μου στο URnetwork! Κατέβασε την εφαρμογή και βάλε τον κωδικό παραπομπής μου στην εγγραφή σου." } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Claim Multiplier" + "value" : "Join me on URnetwork! Get the app and enter my referral code when you sign up." } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Multiplicador de reclamo" + "value" : "¡Únete a URnetwork conmigo! Descarga la app e introduce mi código de referencia al registrarte." } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Multiplicador de reclamo" + "value" : "¡Únete a URnetwork conmigo! Descarga la app e ingresa mi código de referencia al registrarte." } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Multiplicateur de revendication" + "value" : "Rejoins-moi sur URnetwork ! Télécharge l'application et entre mon code de parrainage lors de ton inscription." } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "מכפיל תביעה" + "value" : "הצטרף אליי ב-URnetwork! הורד את האפליקציה והזן את קוד ההפניה שלי בהרשמה." } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "दावा गुणक" + "value" : "URnetwork पर मेरे साथ जुड़ें! ऐप डाउनलोड करें और साइन अप करते समय मेरा रेफ़रल कोड डालें।" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Pengganda Klaim" + "value" : "Gabung URnetwork bersama saya! Unduh aplikasinya dan masukkan kode referensi saya saat mendaftar." } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Moltiplicatore di richiesta" + "value" : "Unisciti a me su URnetwork! Scarica l'app e inserisci il mio codice di riferimento quando ti registri." } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "請求乗数" + "value" : "一緒にURnetworkを使いましょう!アプリをインストールして、登録時に私の紹介コードを入力してください。" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "클레임 배율" + "value" : "URnetwork에서 함께해요! 앱을 설치하고 가입할 때 제 추천 코드를 입력하세요." } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Multiplier claimen" + "value" : "Doe met me mee op URnetwork! Download de app en voer bij het aanmelden mijn referentiecode in." } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Mnożnik roszczeń" + "value" : "Dołącz do mnie w URnetwork! Pobierz aplikację i wpisz mój kod polecający przy rejestracji." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Multiplicador de reivindicação" + "value" : "Junte-se a mim na URnetwork! Baixe o app e insira meu código de indicação ao se cadastrar." } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Multiplicador de reivindicação" + "value" : "Junte-se a mim na URnetwork! Instale a aplicação e introduza o meu código de referência ao registar-se." } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Множитель заявок" + "value" : "Присоединяйтесь ко мне в URnetwork! Установите приложение и введите мой реферальный код при регистрации." } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Kravmultiplikator" + "value" : "Gå med mig på URnetwork! Skaffa appen och ange min referenskod när du registrerar dig." } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "ตัวคูณการเคลม" + "value" : "มาร่วม URnetwork กับฉันสิ! ดาวน์โหลดแอปแล้วกรอกรหัสแนะนำของฉันตอนสมัคร" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Множник вимог" + "value" : "Приєднуйтесь до мене в URnetwork! Встановіть додаток і введіть мій реферальний код під час реєстрації." } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Hệ số nhân yêu cầu" + "value" : "Tham gia URnetwork cùng mình nhé! Tải ứng dụng và nhập mã giới thiệu của mình khi đăng ký." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "申领倍数" + "value" : "快来 URnetwork 和我一起!下载应用,注册时输入我的推荐码。" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "申領倍數" + "value" : "快來 URnetwork 與我一起!下載應用程式,註冊時輸入我的推薦碼。" } } } }, - "Client contracts" : { - - }, - "Client statistics" : { - "comment" : "Title of a card that shows statistics about the client's network usage.", - "isCommentAutoGenerated" : true - }, - "Clients" : { + "Join me on URnetwork! Get the app and enter referral code %@ when you sign up." : { + "comment" : "Refer overlay", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "العملاء" + "value" : "انضم إليّ على URnetwork! حمّل التطبيق وأدخل رمز الإحالة %@ عند التسجيل." } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Klienti" + "value" : "Přidejte se ke mně na URnetwork! Stáhněte si aplikaci a při registraci zadejte doporučující kód %@." } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Clients" + "value" : "Komm zu mir auf URnetwork! Hol dir die App und gib bei der Anmeldung den Empfehlungscode %@ ein." } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Πελάτες" + "value" : "Έλα μαζί μου στο URnetwork! Κατέβασε την εφαρμογή και βάλε τον κωδικό παραπομπής %@ στην εγγραφή σου." } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Clients" + "value" : "Join me on URnetwork! Get the app and enter referral code %@ when you sign up." } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Clientes" + "value" : "¡Únete a URnetwork conmigo! Descarga la app e introduce el código de referencia %@ al registrarte." } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Clientes" + "value" : "¡Únete a URnetwork conmigo! Descarga la app e ingresa el código de referencia %@ al registrarte." + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "¡Únete a URnetwork conmigo! Descarga la app e ingresa el código de referencia %@ al registrarte." } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Clients" + "value" : "Rejoins-moi sur URnetwork ! Télécharge l'application et entre le code de parrainage %@ lors de ton inscription." } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "לקוחות" + "value" : "הצטרף אליי ב-URnetwork! הורד את האפליקציה והזן את קוד ההפניה %@ בהרשמה." } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "क्लाइंट्स" + "value" : "URnetwork पर मेरे साथ जुड़ें! ऐप डाउनलोड करें और साइन अप करते समय रेफ़रल कोड %@ डालें।" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Klien" + "value" : "Gabung URnetwork bersama saya! Unduh aplikasinya dan masukkan kode referensi %@ saat mendaftar." } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Client" + "value" : "Unisciti a me su URnetwork! Scarica l'app e inserisci il codice di riferimento %@ quando ti registri." } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "クライアント" + "value" : "一緒にURnetworkを使いましょう!アプリをインストールして、登録時に紹介コード %@ を入力してください。" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "클라이언트" + "value" : "URnetwork에서 함께해요! 앱을 설치하고 가입할 때 추천 코드 %@을(를) 입력하세요." } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Clients" + "value" : "Doe met me mee op URnetwork! Download de app en voer bij het aanmelden referentiecode %@ in." } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Klienty" + "value" : "Dołącz do mnie w URnetwork! Pobierz aplikację i wpisz kod polecający %@ przy rejestracji." + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Junte-se a mim na URnetwork! Instale a aplicação e introduza o código de referência %@ ao registar-se." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Clientes" + "value" : "Junte-se a mim na URnetwork! Baixe o app e insira o código de indicação %@ ao se cadastrar." } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Clientes" + "value" : "Junte-se a mim na URnetwork! Instale a aplicação e introduza o código de referência %@ ao registar-se." } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Клиенты" + "value" : "Присоединяйтесь ко мне в URnetwork! Установите приложение и введите реферальный код %@ при регистрации." } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Klienter" + "value" : "Gå med mig på URnetwork! Skaffa appen och ange referenskoden %@ när du registrerar dig." } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "ไคลเอนต์" + "value" : "มาร่วม URnetwork กับฉันสิ! ดาวน์โหลดแอปแล้วกรอกรหัสแนะนำ %@ ตอนสมัคร" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Клієнти" + "value" : "Приєднуйтесь до мене в URnetwork! Встановіть додаток і введіть реферальний код %@ під час реєстрації." } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Máy khách" + "value" : "Tham gia URnetwork cùng mình nhé! Tải ứng dụng và nhập mã giới thiệu %@ khi đăng ký." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "客户端" + "value" : "快来 URnetwork 和我一起!下载应用,注册时输入推荐码 %@。" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "客戶端" + "value" : "快來 URnetwork 與我一起!下載應用程式,註冊時輸入推薦碼 %@。" } } } }, - "Close" : { + "Join the community on [Discord](https://discord.com/invite/RUNZXMwPRK)" : { + "comment" : "Used in Main/Account/Settings/SettingsForm-iOS.swift.", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "إغلاق" + "value" : "انضم إلى المجتمع على [Discord](https://discord.com/invite/RUNZXMwPRK)" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Zavřít" + "value" : "Připojte se ke komunitě na [Discord](https://discord.com/invite/RUNZXMwPRK)" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Schließen" + "value" : "Tritt der Community auf [Discord](https://discord.com/invite/RUNZXMwPRK) bei" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Κλείσιμο" + "value" : "Συμμετοχή στην κοινότητα στο [Discord](https://discord.com/invite/RUNZXMwPRK)" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Close" + "value" : "Join the community on [Discord](https://discord.com/invite/RUNZXMwPRK)" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Cerrar" + "value" : "Únete a la comunidad en [Discord](https://discord.com/invite/RUNZXMwPRK)" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Cerrar" + "value" : "Únete a la comunidad en [Discord](https://discord.com/invite/RUNZXMwPRK)" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Fermer" + "value" : "Rejoignez la communauté sur [Discord](https://discord.com/invite/RUNZXMwPRK)" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "סגור" + "value" : "הצטרף לקהילה ב-[Discord](https://discord.com/invite/RUNZXMwPRK)" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "बंद करें" + "value" : "[Discord](https://discord.com/invite/RUNZXMwPRK) पर समुदाय से जुड़ें" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Tutup" + "value" : "Bergabung dengan komunitas di [Discord](https://discord.com/invite/RUNZXMwPRK)" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Chiudi" + "value" : "Unisciti alla community su [Discord](https://discord.com/invite/RUNZXMwPRK)" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "閉じる" + "value" : "[Discord](https://discord.com/invite/RUNZXMwPRK)でコミュニティに参加" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "닫기" + "value" : "[Discord](https://discord.com/invite/RUNZXMwPRK)에서 커뮤니티에 참여하세요" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Sluiten" + "value" : "Word lid van de community [Discord](https://discord.com/invite/RUNZXMwPRK)" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Zamknij" + "value" : "Dołącz do społeczności na [Discord](https://discord.com/invite/RUNZXMwPRK)" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Fechar" + "value" : "Junte-se à comunidade no [Discord](https://discord.com/invite/RUNZXMwPRK)" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Fechar" + "value" : "Junte-se à comunidade no [Discord](https://discord.com/invite/RUNZXMwPRK)" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Закрыть" + "value" : "Присоединяйтесь к сообществу в [Discord](https://discord.com/invite/RUNZXMwPRK)" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Stäng" + "value" : "Gå med i gemenskapen på [Discord](https://discord.com/invite/RUNZXMwPRK)" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "ปิด" + "value" : "เข้าร่วมชุมชนบน [Discord](https://discord.com/invite/RUNZXMwPRK)" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Закрити" + "value" : "Приєднуйтесь до спільноти у [Discord](https://discord.com/invite/RUNZXMwPRK)" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Đóng" + "value" : "Tham gia cộng đồng trên [Discord](https://discord.com/invite/RUNZXMwPRK)" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "关闭" + "value" : "在 [Discord](https://discord.com/invite/RUNZXMwPRK) 上加入社区" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "關閉" + "value" : "在 [Discord](https://discord.com/invite/RUNZXMwPRK) 上加入社區" } } } }, - "Code" : { + "Join the movement" : { + "comment" : "Upgrade subscription overlay", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "الرمز" + "value" : "انضم إلى الحركة" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Kód" + "value" : "Připojte se k hnutí" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Code" + "value" : "Der Bewegung beitreten" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Κωδικός" + "value" : "Γίνετε μέλος του κινήματος" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Code" + "value" : "Join the movement" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Código" + "value" : "Únete al movimiento" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Código" + "value" : "Únete al movimiento" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Únete al movimiento" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Code" + "value" : "Rejoignez le mouvement" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "קוד" + "value" : "הצטרף לתנועה" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "कोड" + "value" : "आंदोलन में शामिल हों" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Kode" + "value" : "Bergabunglah dengan gerakan" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Codice" + "value" : "Unisciti al movimento" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "コード" + "value" : "ムーブメントに参加する" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "코드" + "value" : "운동에 동참하세요" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Code" + "value" : "Doe mee aan de beweging" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Kod" + "value" : "Dołącz do ruchu" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Junte-se ao movimento" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Código" + "value" : "Junte-se ao movimento" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Código" + "value" : "Junte-se ao movimento" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Код" + "value" : "Присоединяйтесь к движению" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Kod" + "value" : "Gå med i rörelsen" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "รหัส" + "value" : "ร่วมเป็นส่วนหนึ่งกับเราสิ" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Код" + "value" : "Приєднуйтесь до руху" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Mã" + "value" : "Tham gia phong trào" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "代码" + "value" : "加入运动" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "代碼" + "value" : "加入運動" } } } }, - "Commitment issues?" : { + "Join URnetwork" : { + "comment" : "Join URnetwork title on the Create Network screen", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "متردد في الالتزام؟" + "value" : "انضم إلى URnetwork" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Problémy se závazky?" + "value" : "Připojte se k URnetwork" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Noch unentschlossen?" + "value" : "Tritt bei URnetwork" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Διστάζετε να δεσμευτείτε;" + "value" : "Συνδεθείτε στο URnetwork" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Commitment issues?" + "value" : "Join URnetwork" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "¿Miedo al compromiso?" + "value" : "Únete a URnetwork" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "¿Miedo al compromiso?" + "value" : "Únete a URnetwork" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Únete a URnetwork" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Peur de l'engagement ?" + "value" : "Rejoindre URnetwork" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "חושש להתחייב?" + "value" : "הצטרף ל-URnetwork" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "अभी तय नहीं कर पा रहे?" + "value" : "शामिल हों URnetwork" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Masih ragu?" + "value" : "Bergabung dengan URnetwork" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Paura di impegnarsi?" + "value" : "Unisciti a URnetwork" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "まだ迷っていますか?" + "value" : "参加する URnetwork" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "결정하기 어려우신가요?" + "value" : "가입하기 URnetwork" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Problemen met commitment?" + "value" : "Sluit je aan bij URnetwork" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Masz wątpliwości?" + "value" : "Dołącz do URnetwork" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Junte-se à URnetwork" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Medo de compromisso?" + "value" : "Junte-se à URnetwork" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Receio de compromisso?" + "value" : "Junte-se ao URnetwork" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Боитесь обязательств?" + "value" : "Присоединяйтесь к URnetwork" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Svårt att bestämma dig?" + "value" : "Gå med i URnetwork" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "ยังลังเลอยู่?" + "value" : "เข้าร่วมกับ URnetwork" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Вагаєтесь?" + "value" : "Приєднатися до URnetwork" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Còn phân vân?" + "value" : "Tham gia URnetwork" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "还在犹豫?" + "value" : "加入 URnetwork" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "猶豫不決?" + "value" : "加入 URnetwork" } } } }, - "Community Edition" : { + "key" : { + "comment" : "Used in Main/Account/Wallets/NetworkReliability/NetworkReliabilityView.swift.", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "key" + } + } + } + }, + "Kill switch" : { + "comment" : "Call to action", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "إصدار المجتمع" + "value" : "مفتاح القتل" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Komunitní edice" + "value" : "Kill switch" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Community Edition" + "value" : "Kill switch" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Έκδοση κοινότητας" + "value" : "Kill switch" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Community Edition" + "value" : "Kill switch" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Edición comunitaria" + "value" : "Kill switch" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Edición comunitaria" + "value" : "Kill switch" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kill switch" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Édition communautaire" + "value" : "Kill switch" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "מהדורת קהילה" + "value" : "קיל סוויץ'" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "कम्युनिटी संस्करण" + "value" : "किल स्विच" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Edisi Komunitas" + "value" : "Kill switch" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Edizione Community" + "value" : "Kill switch" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "コミュニティ版" + "value" : "キルスイッチ" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "커뮤니티 에디션" + "value" : "킬 스위치" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Community-editie" + "value" : "Kill switch" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Edycja społecznościowa" + "value" : "Kill switch" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kill switch" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Edição Comunitária" + "value" : "Kill switch" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Edição Comunitária" + "value" : "Kill switch" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Версия для сообщества" + "value" : "Kill switch" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Community Edition" + "value" : "Kill switch" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "ฉบับชุมชน" + "value" : "สวิตช์หยุดการเชื่อมต่อ" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Спільнотна версія" + "value" : "Kill switch" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Phiên bản cộng đồng" + "value" : "Kill switch" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "社区版" + "value" : "断网保护" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "社群版" + "value" : "斷線開關 (Kill switch)" } } } }, - "Companion" : { - "comment" : "A label displayed next to a companion contract.", - "isCommentAutoGenerated" : true - }, - "Confirm Seeker Token" : { + "Launch" : { + "comment" : "Button text for entering the one-time authentication code and entering the app", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "تأكيد رمز Seeker" + "value" : "تشغيل" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Potvrďte Seeker Token" + "value" : "Spustit" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Seeker-Token bestätigen" + "value" : "Starten" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Επιβεβαίωση Token Seeker" + "value" : "Εκκίνηση" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Confirm Seeker Token" + "value" : "Launch" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Confirmar Token de Seeker" + "value" : "Iniciar" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Confirmar Token de Seeker" + "value" : "Iniciar" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Iniciar" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Confirmer le jeton Seeker" + "value" : "Lancer" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "אמת אסימון Seeker" + "value" : "הפעל" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "सीकर टोकन की पुष्टि करें" + "value" : "लॉन्च करें" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Konfirmasi Token Seeker" + "value" : "Luncurkan" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Conferma Token Seeker" + "value" : "Avvia" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "シーカートークンを確認" + "value" : "起動" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "Seeker 토큰 확인" + "value" : "실행" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Bevestig Seeker Token" + "value" : "Starten" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Potwierdź token Seeker" + "value" : "Uruchom" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Iniciar" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Confirmar Token Seeker" + "value" : "Iniciar" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Confirmar Token Seeker" + "value" : "Lançar" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Подтвердить токен Seeker" + "value" : "Запустить" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Bekräfta Seeker-token" + "value" : "Starta" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "ยืนยัน Seeker Token" + "value" : "เปิดใช้งาน" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Підтвердити токен Seeker" + "value" : "Запустити" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Xác nhận Token Seeker" + "value" : "Khởi chạy" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "确认 Seeker 令牌" + "value" : "启动" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "確認 Seeker 代幣" + "value" : "啟動" } } } }, - "Connect" : { + "Launch URnetwork on system startup" : { + "comment" : "Used in Main/Account/Settings/SettingsForm-macOS.swift.", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "اتصال" + "value" : "تشغيل URnetwork عند بدء النظام" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Připojit" + "value" : "Spuštění URnetwork při spuštění systému" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Verbinden" + "value" : "URnetwork beim Systemstart starten" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Σύνδεση" + "value" : "Εκκίνηση του URnetwork κατά την εκκίνηση του συστήματος" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Launch URnetwork on system startup" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Conectar" + "value" : "Iniciar URnetwork al arrancar el sistema" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Conectar" + "value" : "Iniciar URnetwork al arrancar el sistema" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Connecter" + "value" : "Lancer URnetwork au démarrage du système" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "התחבר" + "value" : "הפעל את URnetwork בעת הפעלת המערכת" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "कनेक्ट करें" + "value" : "सिस्टम स्टार्टअप पर URnetwork लॉन्च करें" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Hubungkan" + "value" : "Jalankan URnetwork saat sistem startup" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Connetti" + "value" : "Avvia URnetwork all'avvio del sistema" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "接続" + "value" : "システム起動時にURnetworkを起動" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "연결" + "value" : "시스템 시작 시 URnetwork 실행" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Verbind" + "value" : "Start URnetwork automatisch bij het opstarten van het systeem" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Połącz" + "value" : "Uruchom URnetwork przy starcie systemu" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Conectar" + "value" : "Iniciar URnetwork na inicialização do sistema" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Ligar" + "value" : "Iniciar URnetwork no arranque do sistema" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Подключиться" + "value" : "Запускать URnetwork при запуске системы" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Anslut" + "value" : "Starta URnetwork vid systemstart" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "เชื่อมต่อ" + "value" : "เปิด URnetwork เมื่อเริ่มต้นระบบ" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Підключитися" + "value" : "Запускати URnetwork під час запуску системи" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Kết nối" + "value" : "Khởi chạy URnetwork khi khởi động hệ thống" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "连接" + "value" : "系统启动时启动 URnetwork" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "連接" + "value" : "系統啟動時啟動 URnetwork" } } } }, - "Connect a wallet" : { + "Leaderboard" : { + "comment" : "Call to action", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "ربط محفظة" + "value" : "لوحة الصدارة" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Připojit peněženku" + "value" : "Žebříček" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Verbinde eine Wallet" + "value" : "Bestenliste" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Συνδέστε ένα πορτοφόλι" + "value" : "Πίνακας κατάταξης" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Connect a wallet" + "value" : "Leaderboard" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Conecta una billetera" + "value" : "Tabla de clasificación" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Conecta una billetera" + "value" : "Tabla de posiciones" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tabla de clasificación" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Connectez un portefeuille" + "value" : "Classement" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "חבר ארנק" + "value" : "טבלת הישגים" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "एक वॉलेट कनेक्ट करें" + "value" : "लीडरबोर्ड" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Hubungkan dompet" + "value" : "Papan Peringkat" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Collega un wallet" + "value" : "Classifica" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "ウォレットを接続" + "value" : "リーダーボード" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "지갑 연결" + "value" : "리더보드" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Verbind een wallet" + "value" : "Scorebord" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Połącz portfel" + "value" : "Tablica wyników" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Quadro de líderes" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Conecte uma carteira" + "value" : "Placar de Líderes" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Conecte uma carteira" + "value" : "Tabela de classificação" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Подключите кошелек" + "value" : "Таблица лидеров" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Anslut en plånbok" + "value" : "Topplista" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "เชื่อมต่อกระเป๋าเงิน" + "value" : "กระดานจัดอันดับ" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Підключіть гаманець" + "value" : "Таблиця лідерів" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Kết nối ví" + "value" : "Bảng xếp hạng" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "连接钱包" + "value" : "排行榜" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "連接錢包" + "value" : "排行榜" } } } }, - "Connect a wallet with the Seeker Pre-Order Token" : { + "Learn more about the multiplier" : { + "comment" : "Call to action", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "ربط محفظة برمز طلب Seeker المسبق" + "value" : "معرفة المزيد حول المضاعف" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Připojit peněženku s Seeker Pre-Order tokenem" + "value" : "Další informace o násobiteli" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Verbinde eine Wallet mit dem Seeker Pre-Order Token" + "value" : "Mehr über den Multiplikator erfahren" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Συνδέστε ένα πορτοφόλι με το Seeker Pre-Order Token" + "value" : "Μάθετε περισσότερα σχετικά με τον πολλαπλασιαστή" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Connect a wallet with the Seeker Pre-Order Token" + "value" : "Learn more about the multiplier" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Conecta una billetera con el token de pedido anticipado de Seeker" + "value" : "Más información sobre el multiplicador" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Conecta una billetera con el token de pedido anticipado de Seeker" + "value" : "Más información sobre el multiplicador" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Más información sobre el multiplicador" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Connectez un portefeuille avec le jeton de précommande Seeker" + "value" : "En savoir plus sur le multiplicateur" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "חבר ארנק עם אסימון הזמנה מוקדמת של Seeker" + "value" : "למידע נוסף על המכפיל" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "Seeker प्री-ऑर्डर टोकन के साथ एक वॉलेट कनेक्ट करें" + "value" : "गुणक के बारे में और जानें" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Hubungkan dompet dengan Token Pra-Pemesanan Seeker" + "value" : "Pelajari lebih lanjut tentang pengganda" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Collega un wallet con il token di pre-ordine Seeker" + "value" : "Scopri di più sul moltiplicatore" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "Seekerプレオーダートークンでウォレットを接続" + "value" : "倍率について詳しく見る" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "Seeker 선주문 토큰으로 지갑 연결" + "value" : "배수에 대해 자세히 알아보기" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Verbind een wallet me een Seeker Pre-Order Token" + "value" : "Meer informatie over de vermenigvuldiger" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Połącz portfel z tokenem zamówienia przedpremierowego Seeker" + "value" : "Dowiedz się więcej o mnożniku" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Saiba mais sobre o multiplicador" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Conecte uma carteira com o token de pré-venda Seeker" + "value" : "Saiba mais sobre o multiplicador" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Conecte uma carteira com o token de pré-venda Seeker" + "value" : "Mais informações sobre o multiplicador" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Подключите кошелек с токеном предзаказа Seeker" + "value" : "Узнать больше о множителе" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Anslut en plånbok med Seeker Förbeställnings-token" + "value" : "Läs mer om multiplikatorn" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "เชื่อมต่อกระเป๋าเงินกับ Seeker Pre-Order Token" + "value" : "เรียนรู้เพิ่มเติมเกี่ยวกับตัวคูณ" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Підключіть гаманець за допомогою токена попереднього замовлення Seeker" + "value" : "Докладніше про множник" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Kết nối ví với Mã thông báo Đặt hàng trước Seeker" + "value" : "Tìm hiểu thêm về hệ số nhân" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "连接带有 Seeker 预售代币的钱包" + "value" : "了解更多关于倍数的信息" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "連接帶有 Seeker 預售代幣的錢包" + "value" : "了解更多關於乘數的資訊" } } } }, - "Connect external wallet" : { + "Loading..." : { + "comment" : "Used in ContentView.swift.", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "ربط المحفظة" + "value" : "جارٍ التحميل..." } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Připojit externí, či nepodporovanou peněženku" + "value" : "Načítání..." } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Wallet verbinden" + "value" : "Laden..." } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Σύνδεση πορτοφολιού" + "value" : "Φόρτωση..." } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Connect external wallet" + "value" : "Loading..." } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Conectar billetera" + "value" : "Cargando..." } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Conectar billetera" + "value" : "Cargando..." } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Connecter le portefeuille" + "value" : "Chargement..." } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "חבר ארנק" + "value" : "טוען..." } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "वॉलेट कनेक्ट करें" + "value" : "लोड हो रहा है..." } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Hubungkan Dompet" + "value" : "Memuat..." } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Connetti il portafoglio" + "value" : "Caricamento..." } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "ウォレットを接続" + "value" : "読み込み中..." } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "지갑 연결" + "value" : "로딩 중..." } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Verbind externe wallet" + "value" : "Laden..." } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Połącz portfel" + "value" : "Ładowanie..." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Conectar carteira" + "value" : "Carregando..." } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Conectar carteira" + "value" : "Carregando..." } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Подключить кошелек" + "value" : "Загрузка..." } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Anslut plånbok" + "value" : "Laddar..." } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "เชื่อมต่อกระเป๋าเงิน" + "value" : "กำลังโหลด..." } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Підключити гаманець" + "value" : "Завантаження..." } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Kết nối ví" + "value" : "Đang tải..." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "连接钱包" + "value" : "加载中..." } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "連接錢包" - } - } - } - }, - "Connect options" : { - "comment" : "A label displayed above the options for connecting to a tunnel.", - "isCommentAutoGenerated" : true - }, - "Connect URnetwork VPN" : { - "localizations" : { - "cs" : { - "stringUnit" : { - "state" : "translated", - "value" : "Připojit se k URnetwork VPN" - } - }, - "nl" : { - "stringUnit" : { - "state" : "translated", - "value" : "Verbind URnetwork VPN" + "value" : "載入中..." } } } }, - "Connect Wallet" : { + "local" : { + "comment" : "Toggle label for the local DNS over HTTPS switch.", + "isCommentAutoGenerated" : true, "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "ربط المحفظة" + "value" : "محلي" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Připojit peněženku" + "value" : "lokálně" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Wallet verbinden" + "value" : "lokal" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Σύνδεση πορτοφολιού" + "value" : "τοπικό" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Connect Wallet" + "value" : "local" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Conectar billetera" + "value" : "local" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Conectar billetera" + "value" : "local" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "local" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Connecter le portefeuille" + "value" : "local" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "חבר ארנק" + "value" : "מקומי" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "वॉलेट कनेक्ट करें" + "value" : "स्थानीय" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Hubungkan Dompet" + "value" : "lokal" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Connetti il portafoglio" + "value" : "locale" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "ウォレットを接続" + "value" : "ローカル" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "지갑 연결" + "value" : "로컬" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Verbind Wallet" + "value" : "lokaal" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Połącz portfel" + "value" : "lokalnie" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "local" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Conectar carteira" + "value" : "local" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Conectar carteira" + "value" : "local" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Подключить кошелек" + "value" : "локально" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Anslut plånbok" + "value" : "lokal" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "เชื่อมต่อกระเป๋าเงิน" + "value" : "ภายในเครื่อง" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Підключити гаманець" + "value" : "локально" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Kết nối ví" + "value" : "cục bộ" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "连接钱包" + "value" : "本地" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "連接錢包" + "value" : "本地" } } } }, - "Connected" : { + "Local" : { + "comment" : "Label for a chart showing local traffic statistics.", + "isCommentAutoGenerated" : true, "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "متصل" + "value" : "محلي" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Připojeno" + "value" : "Lokálně" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Verbunden" + "value" : "Lokal" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Συνδεδεμένο" + "value" : "Τοπικό" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Local" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Conectado" + "value" : "Local" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Conectado" + "value" : "Local" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Local" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Connecté" + "value" : "Local" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "מחובר" + "value" : "מקומי" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "कनेक्टेड" + "value" : "स्थानीय" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Terhubung" + "value" : "Lokal" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Connesso" + "value" : "Locale" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "接続済み" + "value" : "ローカル" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "연결됨" + "value" : "로컬" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Verbonden" + "value" : "Lokaal" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Połączono" + "value" : "Lokalnie" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Local" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Conectado" + "value" : "Local" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Conectado" + "value" : "Local" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Подключено" + "value" : "Локально" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Ansluten" + "value" : "Lokal" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "เชื่อมต่อแล้ว" + "value" : "ภายในเครื่อง" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Підключено" + "value" : "Локально" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Đã kết nối" + "value" : "Cục bộ" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "已连接" + "value" : "本地" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "已連接" + "value" : "本地" } } } }, - "Connected to %d %@" : { - "comment" : "A message indicating that the device is connected to a certain number of providers. The argument is the number of providers.", + "Local DNS" : { + "comment" : "A label displayed in the custom dns settings section.", "isCommentAutoGenerated" : true, - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "new", - "value" : "Connected to %1$d %2$@" - } - } - } - }, - "Connected to %d providers" : { - "extractionState" : "stale", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "متصل بـ %d من مقدمي الخدمة" + "value" : "DNS محلي" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Připojeno k %d poskytovatelům" + "value" : "Místní DNS" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Verbunden mit %d Anbietern" + "value" : "Lokales DNS" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Συνδέθηκε σε %d παρόχους" + "value" : "Τοπικό DNS" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Connected to %d providers" + "value" : "Local DNS" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Conectado a %d proveedores" + "value" : "DNS local" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Conectado a %d proveedores" + "value" : "DNS local" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "DNS local" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Connecté à %d fournisseurs" + "value" : "DNS local" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "מחובר ל-%d ספקים" + "value" : "DNS מקומי" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "%d प्रदाताओं से कनेक्टेड" + "value" : "स्थानीय DNS" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Terhubung ke %d penyedia" + "value" : "DNS lokal" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Connesso a %d provider" + "value" : "DNS locale" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "%d のプロバイダーに接続" + "value" : "ローカルDNS" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "%d개 제공업체에 연결됨" + "value" : "로컬 DNS" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Verbonden met %d providers" + "value" : "Lokale DNS" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Połączono z %d dostawcami" + "value" : "Lokalny DNS" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "DNS local" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Conectado a %d provedores" + "value" : "DNS local" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Conectado a %d fornecedores" + "value" : "DNS local" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Подключено к %d провайдерам" + "value" : "Локальный DNS" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Ansluten till %d leverantörer" + "value" : "Lokal DNS" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "เชื่อมต่อกับผู้ให้บริการ %d ราย" + "value" : "DNS ภายในเครื่อง" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Підключено до %d провайдерів" + "value" : "Локальний DNS" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Đã kết nối với %d nhà cung cấp" + "value" : "DNS cục bộ" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "已连接到 %d 个供应商" + "value" : "本地 DNS" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "已連接到 %d 個供應商" + "value" : "本地 DNS" } } } }, - "Connecting to providers" : { + "Local DNS fallback" : { + "comment" : "A label displayed in the custom dns settings section.", + "isCommentAutoGenerated" : true, "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "جارٍ الاتصال بمزودي الخدمة" + "value" : "الرجوع إلى DNS المحلي" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Připojování k poskytovatelům" + "value" : "Záložní místní DNS" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Verbinde mit Anbietern" + "value" : "Lokales DNS-Fallback" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Σύνδεση σε παρόχους" + "value" : "Εφεδρικό τοπικό DNS" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Connecting to providers" + "value" : "Local DNS fallback" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Conectando a los proveedores" + "value" : "DNS local de reserva" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Conectando a los proveedores" + "value" : "DNS local de respaldo" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "DNS local de respaldo" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Connexion aux fournisseurs" + "value" : "DNS local de secours" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "מתחבר לספקים" + "value" : "גיבוי DNS מקומי" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "प्रदाताओं से कनेक्ट हो रहा है" + "value" : "स्थानीय DNS फ़ॉलबैक" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Menghubungkan ke penyedia" + "value" : "Cadangan DNS lokal" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Connessione ai provider" + "value" : "Fallback DNS locale" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "プロバイダーに接続中" + "value" : "ローカルDNSフォールバック" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "제공업체에 연결하는 중" + "value" : "로컬 DNS 폴백" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Verbinden met providers" + "value" : "Lokale DNS-fallback" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Łączenie z dostawcami" + "value" : "Awaryjny lokalny DNS" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fallback de DNS local" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Conectando aos provedores" + "value" : "Fallback de DNS local" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "A ligar aos fornecedores" + "value" : "Fallback de DNS local" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Подключение к провайдерам" + "value" : "Резервный локальный DNS" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Ansluter till leverantörer" + "value" : "Lokal DNS-reserv" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "กำลังเชื่อมต่อกับผู้ให้บริการ" + "value" : "DNS สำรองภายในเครื่อง" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Підключення до провайдерів" + "value" : "Резервний локальний DNS" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Đang kết nối với nhà cung cấp" + "value" : "DNS dự phòng cục bộ" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "正在连接到供应商" + "value" : "本地 DNS 回退" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "正在連接到供應商" + "value" : "本地 DNS 後備" } } } }, - "Connection Mode" : { - - }, - "Connections" : { + "Local DNS servers" : { + "comment" : "A label displayed above a list of local DNS servers.", + "isCommentAutoGenerated" : true, "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "الاتصالات" + "value" : "خوادم DNS المحلية" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Připojení" + "value" : "Místní servery DNS" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Verbindungen" + "value" : "Lokale DNS-Server" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Συνδέσεις" + "value" : "Τοπικοί διακομιστές DNS" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Connections" + "value" : "Local DNS servers" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Conexiones" + "value" : "Servidores DNS locales" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Conexiones" + "value" : "Servidores DNS locales" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Servidores DNS locales" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Connexions" + "value" : "Serveurs DNS locaux" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "חיבורים" + "value" : "שרתי DNS מקומיים" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "कनेक्शंस" + "value" : "स्थानीय DNS सर्वर" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Koneksi" + "value" : "Server DNS lokal" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Connessioni" + "value" : "Server DNS locali" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "接続" + "value" : "ローカルDNSサーバー" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "연결" + "value" : "로컬 DNS 서버" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Verbindingen" + "value" : "Lokale DNS-servers" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Połączenia" + "value" : "Lokalne serwery DNS" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Servidores DNS locais" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Conexões" + "value" : "Servidores DNS locais" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Conexões" + "value" : "Servidores DNS locais" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Подключения" + "value" : "Локальные DNS-серверы" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Anslutningar" + "value" : "Lokala DNS-servrar" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "การเชื่อมต่อ" + "value" : "เซิร์ฟเวอร์ DNS ภายในเครื่อง" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "З'єднання" + "value" : "Локальні DNS-сервери" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Kết nối" + "value" : "Máy chủ DNS cục bộ" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "连接" + "value" : "本地 DNS 服务器" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "連接" + "value" : "本地 DNS 伺服器" } } } }, - "Continue" : { + "Local DoH URLs" : { + "comment" : "A heading for a list of local DoH URLs.", + "isCommentAutoGenerated" : true, "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "استمر" + "value" : "عناوين URL لـ DoH المحلي" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Pokračovat" + "value" : "Místní adresy URL DoH" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Fortsetzen" + "value" : "Lokale DoH-URLs" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Συνέχεια" + "value" : "Τοπικά URL DoH" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Continue" + "value" : "Local DoH URLs" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Continuar" + "value" : "URLs DoH locales" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Continuar" + "value" : "URLs DoH locales" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "URLs DoH locales" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Continuer" + "value" : "URL DoH locales" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "המשך" + "value" : "כתובות URL ל-DoH מקומי" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "जारी रखें" + "value" : "स्थानीय DoH URLs" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Lanjutkan" + "value" : "URL DoH lokal" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Continua" + "value" : "URL DoH locali" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "続ける" + "value" : "ローカルDoH URL" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "계속" + "value" : "로컬 DoH URL" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Doorgaan" + "value" : "Lokale DoH-URL's" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Kontynuuj" + "value" : "Lokalne adresy URL DoH" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "URLs DoH locais" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Continuar" + "value" : "URLs DoH locais" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Continuar" + "value" : "URLs DoH locais" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Продолжить" + "value" : "Локальные DoH URL" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Fortsätt" + "value" : "Lokala DoH-URL:er" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "ดำเนินการต่อ" + "value" : "DoH URL ภายในเครื่อง" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Продовжити" + "value" : "Локальні DoH URL" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Tiếp tục" + "value" : "URL DoH cục bộ" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "继续" + "value" : "本地 DoH URLs" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "繼續" + "value" : "本地 DoH URLs" } } } }, - "Contract" : { - "comment" : "A label displayed in the center of a contract circle.", - "isCommentAutoGenerated" : true - }, - "Contracts appear here while connected." : { - "comment" : "A description of the contract details view.", - "isCommentAutoGenerated" : true - }, - "Contracts appear here while providing." : { - "comment" : "A description of the contract details view.", - "isCommentAutoGenerated" : true - }, - "Contribute Bandwidth" : { - - }, - "Copy Auth Code" : { + "Local statistics" : { + "comment" : "Title of a card that shows the local traffic statistics.", + "isCommentAutoGenerated" : true, "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "نسخ رمز المصادقة" + "value" : "الإحصائيات المحلية" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Kopírovat autorizační kód" + "value" : "Místní statistiky" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Auth-Code kopieren" + "value" : "Lokale Statistiken" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Αντιγραφή κωδικού ταυτοποίησης" + "value" : "Τοπικά στατιστικά" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Copy Auth Code" + "value" : "Local statistics" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Copiar código de autenticación" + "value" : "Estadísticas locales" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Copiar código de autenticación" + "value" : "Estadísticas locales" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Estadísticas locales" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Copier le code d'authentification" + "value" : "Statistiques locales" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "העתק קוד אימות" + "value" : "סטטיסטיקה מקומית" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "प्रमाणीकरण कोड कॉपी करें" + "value" : "स्थानीय आँकड़े" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Salin kode otentikasi" + "value" : "Statistik lokal" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Copia codice di autenticazione" + "value" : "Statistiche locali" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "認証コードをコピー" + "value" : "ローカル統計" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "인증 코드 복사" + "value" : "로컬 통계" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Authenticatiecode kopiëren" + "value" : "Lokale statistieken" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Skopiuj kod uwierzytelniający" + "value" : "Statystyki lokalne" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Estatísticas locais" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Copiar código de autenticação" + "value" : "Estatísticas locais" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Copiar código de autenticação" + "value" : "Estatísticas locais" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Скопировать код авторизации" + "value" : "Локальная статистика" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Kopiera autentiseringskod" + "value" : "Lokal statistik" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "คัดลอกรหัสยืนยันตัวตน" + "value" : "สถิติภายในเครื่อง" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Скопіювати код автентифікації" + "value" : "Локальна статистика" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Sao chép mã xác thực" + "value" : "Thống kê cục bộ" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "复制认证码" + "value" : "本地统计" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "複製認證碼" + "value" : "本地統計" } } } }, - "Copy client ID" : { - "comment" : "A label for a context menu item that copies a client ID to the clipboard.", - "isCommentAutoGenerated" : true - }, - "Country" : { + "Log in with Auth Code" : { + "comment" : "Guidance for someone who has no code yet. Deliberately PLAIN TEXT with no link and no button: app store rules forbid steering a user to an external purchase, but nothing stops us saying where codes come from. Without this, the sheet asks for a code and gives no way to obtain one.", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "البلد" + "value" : "تسجيل الدخول برمز المصادقة" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Země" + "value" : "Přihlásit se ověřovacím kódem" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Land" + "value" : "Mit Authentifizierungscode anmelden" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Χώρα" + "value" : "Σύνδεση με κωδικό ταυτοποίησης" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Log in with Auth Code" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "País" + "value" : "Iniciar sesión con código de autenticación" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "País" + "value" : "Iniciar sesión con código de autenticación" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Iniciar sesión con código de autenticación" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Pays" + "value" : "Se connecter avec le code d’authentification" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "מדינה" + "value" : "התחבר עם קוד אימות" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "देश" + "value" : "ऑथ कोड से लॉगिन करें" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Negara" + "value" : "Masuk dengan kode autentikasi" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Paese" + "value" : "Accedi con codice di autenticazione" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "国" + "value" : "認証コードでログイン" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "국가" + "value" : "인증 코드로 로그인" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Land" + "value" : "Inloggen met authenticatiecode" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Kraj" + "value" : "Zaloguj się kodem uwierzytelniającym" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Iniciar sessão com código de autenticação" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "País" + "value" : "Entrar com código de autenticação" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "País" + "value" : "Entrar com código de autenticação" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Страна" + "value" : "Войти с кодом аутентификации" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Land" + "value" : "Logga in med autentiseringskod" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "ประเทศ" + "value" : "เข้าสู่ระบบด้วยรหัสยืนยันตัวตน" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Країна" + "value" : "Увійти за кодом авторизації" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Quốc gia" + "value" : "Đăng nhập bằng mã xác thực" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "国家" + "value" : "使用验证码登录" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "國家" + "value" : "用認證碼登入" } } } }, - "Country multipliers" : { + "Lowest user/IP ratio to access content" : { + "comment" : "Call to action", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "مضاعفات البلد" + "value" : "أقل نسبة مستخدم/عنوان IP للوصول إلى المحتوى" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Násobitele zemí" + "value" : "Nejnižší poměr uživatelů na IP pro přístup k obsahu" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Landesmultiplikatoren" + "value" : "Niedrigstes Nutzer/IP-Verhältnis für den Zugriff auf Inhalte" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Πολλαπλασιαστές χώρας" + "value" : "Η χαμηλότερη αναλογία χρηστών/IP για πρόσβαση σε περιεχόμενο" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lowest user/IP ratio to access content" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Multiplicadores por país" + "value" : "La menor proporción de usuarios por IP para acceder a contenido" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Multiplicadores por país" + "value" : "La menor proporción de usuarios/IP para acceder a contenido" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "La menor proporción de usuarios por IP para acceder a contenido" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Multiplicateurs par pays" + "value" : "Le ratio utilisateur/IP le plus bas pour accéder au contenu" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "מכפילים לפי מדינה" + "value" : "יחס המשתמשים/כתובת IP הנמוך ביותר לגישה לתוכן" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "देश गुणक" + "value" : "कंटेंट एक्सेस के लिए सबसे कम यूज़र/IP अनुपात" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Pengali negara" + "value" : "Rasio pengguna/IP terendah untuk mengakses konten" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Moltiplicatori per paese" + "value" : "Il più basso rapporto utenti/IP per accedere ai contenuti" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "国ごとの乗数" + "value" : "コンテンツにアクセスするための最も低いユーザー/IP比率" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "국가 승수" + "value" : "콘텐츠 접근 시 최저 사용자/IP 비율" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Landenvermenigvuldigers" + "value" : "Laagste gebruiker/IP-verhouding voor toegang tot content" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Mnożniki krajów" + "value" : "Najniższy stosunek użytkowników do IP przy dostępie do treści" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Menor proporção de utilizadores por IP para aceder a conteúdo" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Multiplicadores por país" + "value" : "Menor proporção de usuários por IP para acessar conteúdo" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Multiplicadores por país" + "value" : "Menor proporção de utilizadores/IP para aceder a conteúdo" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Страновые множители" + "value" : "Самое низкое соотношение пользователей к IP для доступа к контенту" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Landsmultiplikatorer" + "value" : "Lägsta användare/IP-kvot för att komma åt innehåll" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "ตัวคูณตามประเทศ" + "value" : "อัตราส่วนผู้ใช้/IP ต่ำที่สุดสำหรับการเข้าถึงเนื้อหา" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Країнові множники" + "value" : "Найнижче співвідношення користувачів до IP для доступу до контенту" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Hệ số quốc gia" + "value" : "Tỷ lệ người dùng/IP thấp nhất để truy cập nội dung" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "国家乘数" + "value" : "访问内容时用户/IP比例最低" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "國家乘數" + "value" : "最低用戶/IP比率以存取內容" } } } }, - "Create" : { + "Make default" : { + "comment" : "Wallet Screen", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "إنشاء" + "value" : "جعل الافتراضي" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Vytvořit" + "value" : "Udělat výchozí" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Erstellen" + "value" : "Als Standard festlegen" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Δημιουργία" + "value" : "Ορισμός ως προεπιλογή" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Create" + "value" : "Make default" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Crear" + "value" : "Establecer como predeterminada" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Crear" + "value" : "Hacer predeterminado" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Establecer como predeterminada" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Créer" + "value" : "Définir par défaut" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "צור" + "value" : "הפוך לברירת מחדל" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "बनाएँ" + "value" : "डिफ़ॉल्ट बनाएं" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Buat" + "value" : "Jadikan default" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Crea" + "value" : "Imposta come predefinito" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "作成" + "value" : "デフォルトに設定" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "생성" + "value" : "기본값으로 설정" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Aanmaken" + "value" : "Als standaard instellen" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Utwórz" + "value" : "Ustaw jako domyślny" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tornar padrão" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Criar" + "value" : "Tornar padrão" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Criar" + "value" : "Tornar padrão" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Создать" + "value" : "Сделать по умолчанию" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Skapa" + "value" : "Gör till standard" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "สร้าง" + "value" : "ตั้งเป็นกระเป๋ารับยอด" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Створити" + "value" : "Зробити за замовчуванням" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Tạo" + "value" : "Đặt làm mặc định" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "创建" + "value" : "设为默认" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "創建" + "value" : "設為默認" } } } }, - "Create account" : { + "Most Popular" : { + "comment" : "Used in Shared/Views/ProductOptionCard.swift.", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "إنشاء حساب" + "value" : "الأكثر شعبية" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Vytvořit si účet" + "value" : "Nejoblíbenější" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Ein Konto erstellen" + "value" : "Am beliebtesten" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Δημιουργία λογαριασμού" + "value" : "Πιο δημοφιλές" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Create account" + "value" : "Most Popular" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Crear una cuenta" + "value" : "Más popular" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Crear una cuenta" + "value" : "Más popular" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Créer un compte" + "value" : "Le plus populaire" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "צור חשבון" + "value" : "הפופולרי ביותר" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "खाता बनाएं" + "value" : "सबसे लोकप्रिय" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Buat akun" + "value" : "Paling Populer" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Crea un account" + "value" : "Più popolare" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "アカウントを作成" + "value" : "最も人気" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "계정 만들기" + "value" : "가장 인기 있음" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Account maken" + "value" : "Meest populair" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Utwórz konto" + "value" : "Najpopularniejszy" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Criar uma conta" + "value" : "Mais popular" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Criar uma conta" + "value" : "Mais popular" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Создать аккаунт" + "value" : "Самый популярный" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Skapa ett konto" + "value" : "Mest populär" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "สร้างบัญชี" + "value" : "ได้รับความนิยมสูงสุด" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Створити обліковий запис" + "value" : "Найпопулярніший" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Tạo tài khoản" + "value" : "Phổ biến nhất" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "创建账户" + "value" : "最受欢迎" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "創建帳戶" + "value" : "最受歡迎" } } } }, - "Create Account" : { + "Multiplier" : { + "comment" : "Call to action", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "إنشاء حساب" + "value" : "المضاعف" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Vytvořit účet" + "value" : "Násobitel" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Ein Konto erstellen" + "value" : "Multiplikator" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Δημιουργία λογαριασμού" + "value" : "Πολλαπλασιαστής" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Create Account" + "value" : "Multiplier" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Crear una cuenta" + "value" : "Multiplicador" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Crear una cuenta" + "value" : "Multiplicador" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Multiplicador" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Créer un compte" + "value" : "Multiplicateur" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "צור חשבון" + "value" : "מכפיל" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "खाता बनाएं" + "value" : "गुणक" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Buat akun" + "value" : "Pengali" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Crea un account" + "value" : "Moltiplicatore" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "アカウントを作成" + "value" : "倍率" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "계정 만들기" + "value" : "배수" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Account aanmaken" + "value" : "Vermenigvuldiger" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Utwórz konto" + "value" : "Mnożnik" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Multiplicador" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Criar uma conta" + "value" : "Multiplicador" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Criar uma conta" + "value" : "Multiplicador" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Создать аккаунт" + "value" : "Множитель" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Skapa ett konto" + "value" : "Multiplikator" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "สร้างบัญชี" + "value" : "ตัวคูณ" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Створити обліковий запис" + "value" : "Множник" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Tạo tài khoản" + "value" : "Hệ số nhân" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "创建账户" + "value" : "倍增器" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "創建帳戶" + "value" : "乘數" } } } }, - "Create an account" : { + "Name" : { + "comment" : "Create Network Form", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "إنشاء حساب" + "value" : "الاسم" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Vytvořte si účet" + "value" : "Jméno" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Ein Konto erstellen" + "value" : "Name" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Δημιουργία λογαριασμού" + "value" : "Όνομα" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Create an account" + "value" : "Name" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Crear una cuenta" + "value" : "Nombre" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Crear una cuenta" + "value" : "Nombre" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nombre" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Créer un compte" + "value" : "Nom" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "צור חשבון" + "value" : "שם" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "खाता बनाएं" + "value" : "नाम" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Buat akun" + "value" : "Nama" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Crea un account" + "value" : "Nome" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "アカウントを作成" + "value" : "名前" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "계정 만들기" + "value" : "이름" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Account aanmaken" + "value" : "Naam" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Utwórz konto" + "value" : "Imię" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nome" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Criar uma conta" + "value" : "Nome" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Criar uma conta" + "value" : "Nome" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Создать аккаунт" + "value" : "Имя" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Skapa ett konto" + "value" : "Namn" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "สร้างบัญชี" + "value" : "ชื่อ" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Створити обліковий запис" + "value" : "Ім'я" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Tạo tài khoản" + "value" : "Tên" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "创建账户" + "value" : "姓名" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "創建帳戶" + "value" : "名稱" } } } }, - "Created auth codes expire after 5 minutes" : { + "Net points earned" : { + "comment" : "Used in Shared/Views/AccountPointsBreakdown.swift.", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "تنتهي صلاحية رموز المصادقة التي تم إنشاؤها بعد 5 دقائق" + "value" : "صافي النقاط المكتسبة" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Vytvořené autorizační kódy vyprší po 5 minutách" + "value" : "Získané \"net\" body" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Erstellte Auth-Codes verfallen nach 5 Minuten" + "value" : "Netto verdiente Punkte" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Οι δημιουργημένοι κωδικοί ταυτοποίησης λήγουν μετά από 5 λεπτά" + "value" : "Καθαροί πόντοι που κερδήθηκαν" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Created auth codes expire after 5 minutes" + "value" : "Net points earned" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Los códigos de autenticación creados caducan después de 5 minutos" + "value" : "Puntos netos ganados" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Los códigos de autenticación creados caducan después de 5 minutos" + "value" : "Puntos netos ganados" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Les codes d'authentification créés expirent après 5 minutes" + "value" : "Points nets gagnés" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "קודי אימות שנוצרו פגים לאחר 5 דקות" + "value" : "נקודות נטו שהושגו" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "निर्मित प्रमाणीकरण कोड 5 मिनट बाद समाप्त हो जाते हैं" + "value" : "कुल अर्जित अंक" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Kode otentikasi yang dibuat akan kedaluwarsa setelah 5 menit" + "value" : "Poin bersih yang diperoleh" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "I codici di autenticazione creati scadono dopo 5 minuti" + "value" : "Punti netti guadagnati" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "作成された認証コードは5分後に失効します" + "value" : "獲得した純ポイント" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "생성된 인증 코드는 5분 후 만료됩니다" + "value" : "순획득 포인트" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Aangemaakte authenticatiecodes verlopen na 5 minuten" + "value" : "Totaal verdiende punten" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Utworzone kody uwierzytelniające wygasają po 5 minutach" + "value" : "Uzyskane punkty netto" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Códigos de autenticação criados expiram após 5 minutos" + "value" : "Pontos líquidos ganhos" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Códigos de autenticação criados expiram após 5 minutos" + "value" : "Pontos líquidos ganhos" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Созданные коды авторизации истекают через 5 минут" + "value" : "Заработанные чистые баллы" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Skapade autentiseringskoder upphör efter 5 minuter" + "value" : "Nettopoäng intjänade" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "รหัสยืนยันตัวตนที่สร้างขึ้นจะหมดอายุหลังจาก 5 นาที" + "value" : "แต้มสุทธิที่ได้รับ" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Створені коди автентифікації закінчуються через 5 хвилин" + "value" : "Чисті зароблені бали" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Mã xác thực được tạo sẽ hết hạn sau 5 phút" + "value" : "Điểm ròng đã kiếm được" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "创建的认证码将在5分钟后失效" + "value" : "净赚积分" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "創建的認證碼將於5分鐘後失效" + "value" : "淨賺積分" } } } }, - "Current Ranking" : { + "Net Provided" : { + "comment" : "Call to action", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "الترتيب الحالي" + "value" : "البيانات المقدمة" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Aktuální ranking" + "value" : "Poskytnutá čistá hodnota" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Aktuelle Platzierung" + "value" : "Bereitgestelltes Netz" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Τρέχουσα κατάταξη" + "value" : "Παρεχόμενο Δίκτυο" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Net Provided" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Clasificación actual" + "value" : "Red Proporcionada" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Clasificación actual" + "value" : "Red proporcionada" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Red Proporcionada" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Classement actuel" + "value" : "Données Fournies" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "דירוג נוכחי" + "value" : "סופק נטו" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "वर्तमान रैंकिंग" + "value" : "प्रदान किया गया नेट" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Peringkat Saat Ini" + "value" : "Bersih Disediakan" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Classifica attuale" + "value" : "Fornito netto" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "現在のランキング" + "value" : "提供済みネット" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "현재 순위" + "value" : "제공된 넷" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Huidige positie" + "value" : "Geleverd netwerk" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Aktualna pozycja" + "value" : "Dostarczona sieć" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rede Fornecida" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Classificação atual" + "value" : "Rede Fornecida" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Classificação atual" + "value" : "Fornecido líquido" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Текущий рейтинг" + "value" : "Предоставлено (нетто)" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Aktuell rankning" + "value" : "Nettotillhandahållen" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "อันดับปัจจุบัน" + "value" : "แบนด์วิธที่ให้บริการแล้ว" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Поточний рейтинг" + "value" : "Надано мережею (нетто)" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Xếp hạng hiện tại" + "value" : "Mạng cung cấp (ròng)" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "当前排名" + "value" : "已提供网络量" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "目前排名" + "value" : "淨提供" } } } }, - "Current referral network" : { + "Network earnings" : { + "comment" : "Used in Main/Account/AccountRootView.swift.", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "شبكة الإحالة الحالية" + "value" : "أرباح الشبكة" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Aktuální referální sít" + "value" : "Výnosy ze sítě" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Aktuelles Empfehlungsnetzwerk" + "value" : "Netzwerk-Einnahmen" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Τρέχον δίκτυο παραπομπών" + "value" : "Κέρδη δικτύου" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Current referral network" + "value" : "Network earnings" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Red de referidos actual" + "value" : "Ganancias de la red" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Red de referidos actual" + "value" : "Ganancias de la red" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Réseau de parrainage actuel" + "value" : "Gains du réseau" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "רשת ההפניות הנוכחית" + "value" : "רווחי רשת" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "वर्तमान रेफ़रल नेटवर्क" + "value" : "नेटवर्क आय" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Jaringan referensi saat ini" + "value" : "Penghasilan Jaringan" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Rete di riferimento attuale" + "value" : "Guadagni della rete" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "現在のリファラルネットワーク" + "value" : "ネットワーク収益" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "현재 추천 네트워크" + "value" : "네트워크 수익" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Huidig referentienetwerk" + "value" : "Netwerkinkomsten" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Obecna sieć poleceń" + "value" : "Zarobki sieci" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Rede de indicações atual" + "value" : "Ganhos da rede" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Rede de recomendações atual" + "value" : "Ganhos da rede" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Текущая реферальная сеть" + "value" : "Доход сети" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Aktuellt referensnätverk" + "value" : "Nätverksintäkter" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "เครือข่ายผู้อ้างอิงปัจจุบัน" + "value" : "รายได้ของเครือข่าย" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Поточна реферальна мережа" + "value" : "Заробіток мережі" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Mạng giới thiệu hiện tại" + "value" : "Thu nhập mạng" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "当前推荐网络" + "value" : "网络收益" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "當前推薦網絡" + "value" : "網絡收益" } } } }, - "Custom DNS" : { - "comment" : "Title of a section in the connect drawer that shows the custom dns settings.", - "isCommentAutoGenerated" : true - }, - "Daily Data Balance:" : { + "Network name" : { + "comment" : "Label for the network name input field", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "رصيد البيانات اليومي:" + "value" : "اسم الشبكة" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Denní datový zůstatek:" + "value" : "Název sítě" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Tägliches Datenvolumen:" + "value" : "Netzwerkname" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Ημερήσιο υπόλοιπο δεδομένων:" + "value" : "Όνομα δικτύου" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Daily Data Balance:" + "value" : "Network name" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Saldo de datos diario:" + "value" : "Nombre de la red" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Saldo de datos diario:" + "value" : "Nombre de la red" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nombre de la red" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Solde de données quotidien :" + "value" : "Nom du réseau" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "יתרת נתונים יומית:" + "value" : "שם הרשת" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "दैनिक डेटा बैलेंस:" + "value" : "नेटवर्क का नाम" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Saldo Data Harian:" + "value" : "Nama jaringan" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Saldo dati giornaliero:" + "value" : "Nome della rete" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "日次データ残高:" + "value" : "ネットワーク名" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "일일 데이터 잔액:" + "value" : "네트워크 이름" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Dagelijkse databalance:" + "value" : "Netwerk naam" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Dzienne saldo danych:" + "value" : "Nazwa sieci" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nome da rede" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Saldo diário de dados:" + "value" : "Nome da rede" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Saldo diário de dados:" + "value" : "Nome da rede" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Дневной баланс данных:" + "value" : "Имя сети" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Daglig databalans:" + "value" : "Nätverksnamn" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "ยอดคงเหลือข้อมูลรายวัน:" + "value" : "ชื่อเครือข่าย" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Щоденний баланс даних:" + "value" : "Назва мережі" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Số dư dữ liệu hàng ngày:" + "value" : "Tên mạng" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "每日数据余额:" + "value" : "网络名称" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "每日數據餘額:" + "value" : "網絡名稱" } } } }, - "Danger" : { + "Network name is too short. Try one with at least 6 characters" : { + "comment" : "Supporting text for a text field that is invalid.", + "isCommentAutoGenerated" : true, "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "خطر" + "value" : "اسم الشبكة قصير جدًا. جرّب اسمًا من 6 أحرف على الأقل" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Název sítě je příliš krátký. Zkuste název s alespoň 6 znaky" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Gefahr" + "value" : "Netzwerkname ist zu kurz. Probiere einen mit mindestens 6 Zeichen" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Κίνδυνος" + "value" : "Το όνομα δικτύου είναι πολύ σύντομο. Δοκιμάστε ένα με τουλάχιστον 6 χαρακτήρες" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Danger" + "value" : "Network name is too short. Try one with at least 6 characters" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Peligro" + "value" : "El nombre de la red es demasiado corto. Prueba con uno de al menos 6 caracteres" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Peligro" + "value" : "El nombre de la red es demasiado corto. Prueba con uno de al menos 6 caracteres" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Danger" + "value" : "Le nom du réseau est trop court. Essayez-en un d'au moins 6 caractères" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "סכנה" + "value" : "שם הרשת קצר מדי. נסה שם באורך 6 תווים לפחות" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "खतरा" + "value" : "नेटवर्क का नाम बहुत छोटा है। कम से कम 6 अक्षरों वाला नाम आज़माएँ" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Bahaya" + "value" : "Nama jaringan terlalu pendek. Coba yang minimal 6 karakter" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Pericolo" + "value" : "Il nome della rete è troppo corto. Provane uno di almeno 6 caratteri" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "危険" + "value" : "ネットワーク名が短すぎます。6文字以上でお試しください" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "위험" + "value" : "네트워크 이름이 너무 짧습니다. 6자 이상으로 입력하세요" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Gevaar" + "value" : "Netwerknaam is te kort. Kies er een met minstens 6 tekens" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Niebezpieczeństwo" + "value" : "Nazwa sieci jest za krótka. Musi mieć co najmniej 6 znaków" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Perigo" + "value" : "O nome da rede é muito curto. Tente um com pelo menos 6 caracteres" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Perigo" + "value" : "O nome da rede é demasiado curto. Experimente um com pelo menos 6 caracteres" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Опасность" + "value" : "Имя сети слишком короткое. Нужно не менее 6 символов" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Fara" + "value" : "Nätverksnamnet är för kort. Prova ett med minst 6 tecken" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "อันตราย" + "value" : "ชื่อเครือข่ายสั้นเกินไป ลองใช้ชื่อที่มีอย่างน้อย 6 ตัวอักษร" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Небезпека" + "value" : "Назва мережі закоротка. Потрібно щонайменше 6 символів" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Nguy hiểm" + "value" : "Tên mạng quá ngắn. Hãy thử tên có ít nhất 6 ký tự" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "危险" + "value" : "网络名称太短。请使用至少 6 个字符的名称" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "危險" + "value" : "網絡名稱太短。請使用至少 6 個字符的名稱" } } } }, - "Data" : { - - }, - "DEFAULT" : { + "Network names must be 6 characters or more" : { + "comment" : "Hint text indicating the required length for a network name input.", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "افتراضي" + "value" : "يجب أن تتكون أسماء الشبكات من 6 أحرف أو أكثر" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "" + "value" : "Jména sítí musí mít 6 a více znaků" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "STANDARD" + "value" : "Netzwerknamen müssen mindestens 6 Zeichen lang sein." } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "ΠΡΟΕΠΙΛΟΓΗ" + "value" : "Τα ονόματα δικτύου πρέπει να έχουν τουλάχιστον 6 χαρακτήρες." } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "DEFAULT" + "value" : "Network names must be 6 characters or more" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "POR DEFECTO" + "value" : "Los nombres de red deben tener al menos 6 caracteres." } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "POR DEFECTO" + "value" : "Los nombres de red deben tener 6 caracteres o más" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Los nombres de red deben tener al menos 6 caracteres." } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "PAR DÉFAUT" + "value" : "Les noms de réseau doivent comporter au moins 6 caractères." } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "בְּרִירַת מֶחדָל" + "value" : "שמות רשת חייבים להיות באורך של 6 תווים או יותר" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "डिफ़ॉल्ट" + "value" : "नेटवर्क नाम कम से कम 6 अक्षरों का होना चाहिए" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "DEFAULT" + "value" : "Nama jaringan harus terdiri dari 6 karakter atau lebih" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "DEFAULT" + "value" : "I nomi di rete devono essere di almeno 6 caratteri" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "デフォルト" + "value" : "ネットワーク名は6文字以上である必要があります" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "기본값" + "value" : "네트워크 이름은 6자 이상이어야 합니다" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Standaard" + "value" : "Netwerknamen moeten uit 6 of meer tekens bestaan" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "DOMYŚLNY" + "value" : "Nazwy sieci muszą mieć co najmniej 6 znaków." + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Os nomes de rede devem ter pelo menos 6 caracteres." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "PADRÃO" + "value" : "Os nomes de rede devem ter pelo menos 6 caracteres." } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "PADRÃO" + "value" : "Os nomes de rede devem ter 6 caracteres ou mais" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "ПО УМОЛЧАНИЮ" + "value" : "Название сети должно содержать не менее 6 символов" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "STANDARD" + "value" : "Nätverksnamn måste vara minst 6 tecken långa" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "ค่าเริ่มต้น" + "value" : "ชื่อเครือข่ายของคุณต้องมีตัวอักษรมากกว่า 6 ตัวขึ้นไป" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "ЗА ЗАМОВЧУВАННЯМ" + "value" : "Імена мереж повинні містити щонайменше 6 символів" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "MẶC ĐỊNH" + "value" : "Tên mạng phải dài ít nhất 6 ký tự" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "默认" + "value" : "网络名称必须至少包含 6 个字符。" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "默認" + "value" : "網絡名稱必須為6個字符或以上" } } } }, - "Delete" : { - - }, - "Delete account" : { + "Network peers" : { + "comment" : "A label displayed above the list of connected peers.", + "isCommentAutoGenerated" : true, "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "حذف الحساب" + "value" : "أقران الشبكة" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Vymazat účet" + "value" : "Síťové uzly" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Konto löschen" + "value" : "Netzwerk-Peers" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Διαγραφή λογαριασμού" + "value" : "Κόμβοι δικτύου" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Delete account" + "value" : "Network peers" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Eliminar cuenta" + "value" : "Pares de la red" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Eliminar cuenta" + "value" : "Pares de la red" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pares de la red" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Supprimer le compte" + "value" : "Pairs du réseau" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "מחק חשבון" + "value" : "עמיתי רשת" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "खाता हटाएं" + "value" : "नेटवर्क पीयर" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Hapus akun" + "value" : "Rekan jaringan" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Elimina account" + "value" : "Peer di rete" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "アカウントを削除" + "value" : "ネットワークピア" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "계정 삭제" + "value" : "네트워크 피어" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Verwijder account" + "value" : "Netwerkpeers" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Usuń konto" + "value" : "Węzły sieci" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pares da rede" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Excluir conta" + "value" : "Pares da rede" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Eliminar conta" + "value" : "Pares da rede" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Удалить аккаунт" + "value" : "Сетевые узлы" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Radera konto" + "value" : "Nätverksnoder" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "ลบบัญชี" + "value" : "เพียร์ในเครือข่าย" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Видалити обліковий запис" + "value" : "Мережеві вузли" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Xóa tài khoản" + "value" : "Nút mạng ngang hàng" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "删除账户" + "value" : "网络对等节点" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "刪除帳戶" + "value" : "網絡對等節點" } } } }, - "Device" : { - - }, - "Device name" : { - - }, - "Disconnect" : { + "Never" : { + "comment" : "Call to action", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "قطع الاتصال" + "value" : "أبدًا" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Odpojit" + "value" : "Nikdy" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Trennen" + "value" : "Nie" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Αποσύνδεση" + "value" : "Ποτέ" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Never" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Desconectar" + "value" : "Nunca" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Desconectar" + "value" : "Nunca" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nunca" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Déconnecter" + "value" : "Jamais" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "התנתק" + "value" : "אף פעם" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "डिसकनेक्ट करें" + "value" : "कभी नहीं" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Putuskan" + "value" : "Tidak pernah" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Disconnetti" + "value" : "Mai" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "切断" + "value" : "決して" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "연결 해제" + "value" : "절대" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Verbinding verbreken" + "value" : "Nooit" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Rozłącz" + "value" : "Nigdy" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nunca" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Desconectar" + "value" : "Nunca" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Desligar" + "value" : "Nunca" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Отключиться" + "value" : "Никогда" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Koppla från" + "value" : "Aldrig" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "ยกเลิกการเชื่อมต่อ" + "value" : "ไม่เลย" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Відключитися" + "value" : "Ніколи" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Ngắt kết nối" + "value" : "Không bao giờ" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "断开连接" + "value" : "从不" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "斷開連接" + "value" : "從不" } } } }, - "Disconnect URnetwork VPN" : { + "New split rule" : { + "comment" : "A label displayed above the form for creating a new split rule.", + "isCommentAutoGenerated" : true, "localizations" : { - "cs" : { + "ar" : { "stringUnit" : { "state" : "translated", - "value" : "Odpojit se od URnetwork VPN" + "value" : "قاعدة تقسيم جديدة" } }, - "nl" : { + "cs" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork VPM verbinding verbreken" + "value" : "Nové pravidlo rozdělení" } - } - } - }, - "Display network on leaderboard" : { - "localizations" : { - "ar" : { + }, + "de" : { "stringUnit" : { "state" : "translated", - "value" : "عرض الشبكة على لوحة المتصدرين" + "value" : "Neue Split-Regel" } }, - "cs" : { + "el" : { "stringUnit" : { "state" : "translated", - "value" : "Zobrazit síť na žebříčku" + "value" : "Νέος κανόνας διαχωρισμού" } }, - "de" : { + "en" : { "stringUnit" : { "state" : "translated", - "value" : "Netzwerk in Bestenliste anzeigen" + "value" : "New split rule" } }, - "el" : { + "es" : { "stringUnit" : { "state" : "translated", - "value" : "Εμφάνιση δικτύου στον πίνακα κατάταξης" + "value" : "Nueva regla de división" } }, - "es" : { + "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Mostrar red en la clasificación" + "value" : "Nueva regla de división" } }, - "es-419" : { + "es-MX" : { "stringUnit" : { "state" : "translated", - "value" : "Mostrar red en la tabla de posiciones" + "value" : "Nueva regla de división" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Afficher le réseau dans le classement" + "value" : "Nouvelle règle de séparation" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "הצג רשת בטבלת המובילים" + "value" : "כלל פיצול חדש" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "लीडरबोर्ड पर नेटवर्क दिखाएं" + "value" : "नया स्प्लिट नियम" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Tampilkan jaringan di papan peringkat" + "value" : "Aturan pemisahan baru" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Mostra rete nella classifica" + "value" : "Nuova regola di suddivisione" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "ランキングにネットワークを表示" + "value" : "新しい分割ルール" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "리더보드에 네트워크 표시" + "value" : "새 분할 규칙" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Toon netwerk op ranglijst" + "value" : "Nieuwe splitsregel" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Wyświetlaj sieć w tabeli wyników" + "value" : "Nowa reguła podziału" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nova regra de divisão" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Exibir rede no placar" + "value" : "Nova regra de divisão" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Mostrar rede na tabela de classificação" + "value" : "Nova regra de divisão" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Показывать сеть в таблице лидеров" + "value" : "Новое правило разделения" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Visa nätverk på topplistan" + "value" : "Ny delningsregel" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "แสดงเครือข่ายบนกระดานผู้นำ" + "value" : "กฎแยกเส้นทางใหม่" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Показувати мережу в таблиці лідерів" + "value" : "Нове правило розділення" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Hiển thị mạng trên bảng xếp hạng" + "value" : "Quy tắc tách luồng mới" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "在排行榜上显示网络" + "value" : "新建分流规则" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "在排行榜上顯示網絡" + "value" : "新增分流規則" } } } }, - "DNS over HTTPS" : { - "comment" : "A label displayed in a row of a custom dns settings.", - "isCommentAutoGenerated" : true - }, - "DNS settings unavailable" : { - "comment" : "A message displayed when the dns settings are unavailable.", - "isCommentAutoGenerated" : true - }, - "Don't see it? " : { - "comment" : "Referring to the OTP code", + "Nice! This network name is available" : { + "comment" : "Used in Authenticate/CreateNetwork/CreateNetworkViewModel.swift.", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "لا تراه؟" + "value" : "رائع! اسم الشبكة هذا متاح" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Nevidíte to?" + "value" : "Pěkně! Todle jméno sítě je dostupné." } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Nicht gesehen?" + "value" : "Schön! Dieser Netzwerkname ist verfügbar" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Δεν το βλέπετε;" + "value" : "Ωραία! Αυτό το όνομα δικτύου είναι διαθέσιμο" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Don't see it?" + "value" : "Nice! This network name is available" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "¿No lo ves?" + "value" : "¡Genial! Este nombre de red está disponible" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "¿No lo ves?" + "value" : "¡Genial! Este nombre de red está disponible" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Vous ne le voyez pas ?" + "value" : "Super ! Ce nom de réseau est disponible" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "לא רואה את זה?" + "value" : "נחמד! שם הרשת הזה זמין" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "यह नहीं दिख रहा?" + "value" : "अच्छा! यह नेटवर्क नाम उपलब्ध है" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Tidak melihatnya?" + "value" : "Bagus! Nama jaringan ini tersedia" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Non lo vedi?" + "value" : "Bene! Questo nome di rete è disponibile" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "見えませんか?" + "value" : "いいですね!このネットワーク名は使用可能です" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "보이지 않나요?" + "value" : "좋아요! 이 네트워크 이름을 사용할 수 있습니다" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Zie je het niet?" + "value" : "Mooi! Deze netwerknaam is beschikbaar" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Nie widzisz tego?" + "value" : "Świetnie! Ta nazwa sieci jest dostępna" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Não está vendo?" + "value" : "Legal! Este nome de rede está disponível" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Não está vendo?" + "value" : "Legal! Este nome de rede está disponível" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Не видите?" + "value" : "Отлично! Это имя сети доступно" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Ser du det inte?" + "value" : "Bra! Detta nätverksnamn är tillgängligt" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "ไม่เห็นหรือ?" + "value" : "ดี! ชื่อเครือข่ายนี้สามารถใช้ได้" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Не бачите?" + "value" : "Чудово! Це ім'я мережі доступне" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Không thấy?" + "value" : "Tuyệt! Tên mạng này có sẵn" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "看不到吗?" + "value" : "很好!此网络名称可用" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "看不到嗎?" + "value" : "很好!此網絡名稱可用" } } } }, - "Earning multipliers" : { + "Nicely done." : { + "comment" : "Used in Main/MainView.swift.", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "مضاعفات الأرباح" + "value" : "عمل جيد." } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Násobiče výdělků" + "value" : "Pěkně uděláno." } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Verdienstmultiplikatoren" + "value" : "Gut gemacht." } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Πολλαπλασιαστές κερδών" + "value" : "Μπράβο." + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nicely done." } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Multiplicadores de ganancias" + "value" : "Bien hecho." } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Multiplicadores de ganancias" + "value" : "Bien hecho." } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Multiplicateurs de gains" + "value" : "Bien joué." } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "מכפילי רווחים" + "value" : "כל הכבוד." } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "अर्जन गुणक" + "value" : "अच्छा किया।" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Pengganda penghasilan" + "value" : "Kerja bagus." } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Moltiplicatori di guadagno" + "value" : "Ben fatto." } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "収益倍率" + "value" : "よくできました。" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "수익 승수" + "value" : "잘했어요." } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Verdienvermeningvuldigers" + "value" : "Goed gedaan!" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Mnożniki zarobków" + "value" : "Dobra robota." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Multiplicadores de ganhos" + "value" : "Bem feito." } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Multiplicadores de ganhos" + "value" : "Bem feito." } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Множители заработка" + "value" : "Отлично сделано." } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Intäktsmultiplikatorer" + "value" : "Bra gjort." } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "ตัวคูณรายได้" + "value" : "ทำได้ดีมาก." } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Множники заробітку" + "value" : "Добре зроблено." } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Hệ số nhân thu nhập" + "value" : "Làm tốt lắm." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "收益倍增器" + "value" : "做得好。" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "收益倍增器" + "value" : "做得好。" } } } }, - "Earnings" : { + "No balance codes found" : { + "comment" : "Used in Main/Account/TransferBalanceCodes/TransferBalanceCodesView.swift.", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "الأرباح" + "value" : "لم يتم العثور على رموز رصيد" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Výdělky" + "value" : "Nebyly nalezeny žádné kódy na zůstatek" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Einnahmen" + "value" : "Keine Guthabencodes gefunden" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Κέρδη" + "value" : "Δεν βρέθηκαν κωδικοί υπολοίπου" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Earnings" + "value" : "No balance codes found" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Ganancias" + "value" : "No se encontraron códigos de saldo" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Ganancias" + "value" : "No se encontraron códigos de saldo" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Gains" + "value" : "Aucun code de solde trouvé" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "רווחים" + "value" : "לא נמצאו קודי יתרה" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "कमाई" + "value" : "कोई बैलेंस कोड नहीं मिला" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Pendapatan" + "value" : "Tidak ditemukan kode saldo" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Guadagni" + "value" : "Nessun codice saldo trovato" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "収益" + "value" : "残高コードが見つかりません" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "수익" + "value" : "잔액 코드가 없습니다" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Verdiensten" + "value" : "Geen saldo codes gevonden" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Zarobki" + "value" : "Nie znaleziono kodów salda" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Ganhos" + "value" : "Nenhum código de saldo encontrado" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Ganhos" + "value" : "Nenhum código de saldo encontrado" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Заработок" + "value" : "Коды баланса не найдены" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Intäkter" + "value" : "Inga saldo-koder hittades" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "รายได้" + "value" : "ไม่พบรหัสยอดเงิน" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Заробіток" + "value" : "Кодів балансу не знайдено" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Thu nhập" + "value" : "Không tìm thấy mã số dư nào" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "收益" + "value" : "未找到余额码" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "收益" + "value" : "找不到餘額碼" } } } }, - "Edit referral code" : { + "No blocked locations" : { + "comment" : "Call to action", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "تحرير رمز الإحالة" + "value" : "لا توجد مواقع محجوبة" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Upravit referální kód" + "value" : "Žádné blokované lokality" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Empfehlungscode bearbeiten" + "value" : "Keine blockierten Standorte" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Επεξεργασία κωδικού παραπομπής" + "value" : "Δεν υπάρχουν αποκλεισμένες τοποθεσίες" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Edit referral code" + "value" : "No blocked locations" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Editar código de referencia" + "value" : "No hay ubicaciones bloqueadas" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Editar código de referencia" + "value" : "No hay ubicaciones bloqueadas" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "No hay ubicaciones bloqueadas" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Modifier le code de parrainage" + "value" : "Aucun emplacement bloqué" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "ערוך קוד הפניה" + "value" : "אין מיקומים חסומים" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "रेफरल कोड संपादित करें" + "value" : "कोई अवरुद्ध स्थान नहीं" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Edit kode referensi" + "value" : "Tidak ada lokasi yang diblokir" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Modifica codice di riferimento" + "value" : "Nessuna posizione bloccata" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "紹介コードを編集" + "value" : "ブロックされた場所はありません" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "추천 코드 수정" + "value" : "차단된 위치 없음" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Referentiecode bewerken" + "value" : "Geen geblokkeerde locaties" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Edytuj kod polecający" + "value" : "Brak zablokowanych lokalizacji" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nenhuma localização bloqueada" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Editar código de referência" + "value" : "Nenhuma localização bloqueada" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Editar código de referência" + "value" : "Nenhuma localização bloqueada" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Редактировать реферальный код" + "value" : "Нет заблокированных локаций" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Redigera referenskod" + "value" : "Inga blockerade platser" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "แก้ไขรหัสอ้างอิง" + "value" : "ไม่มีตำแหน่งที่ถูกบล็อก" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Редагувати реферальний код" + "value" : "Немає заблокованих місць" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Chỉnh sửa mã giới thiệu" + "value" : "Không có vị trí bị chặn" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "编辑推荐码" + "value" : "没有被屏蔽的位置" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "編輯推薦碼" + "value" : "沒有被屏蔽的位置" } } } }, - "Edit split rule" : { - "comment" : "A label displayed above the split rule editor.", - "isCommentAutoGenerated" : true - }, - "Email or phone number" : { + "No open contracts" : { + "comment" : "A label displayed when there are no open contracts.", + "isCommentAutoGenerated" : true, "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "البريد الإلكتروني أو رقم الهاتف" + "value" : "لا توجد عقود مفتوحة" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Email nebo telefonní číslo" + "value" : "Žádné otevřené kontrakty" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "E-Mail oder Telefonnummer" + "value" : "Keine offenen Verträge" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Email ή αριθμός τηλεφώνου" + "value" : "Δεν υπάρχουν ανοιχτά συμβόλαια" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "No open contracts" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Correo electrónico o número de teléfono" + "value" : "No hay contratos abiertos" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Correo electrónico o número de teléfono" + "value" : "No hay contratos abiertos" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "No hay contratos abiertos" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "E-mail ou numéro de téléphone" + "value" : "Aucun contrat ouvert" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "דוא״ל או מספר טלפון" + "value" : "אין חוזים פתוחים" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "ईमेल या फ़ोन नंबर" + "value" : "कोई खुला कॉन्ट्रैक्ट नहीं" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Email atau nomor telepon" + "value" : "Tidak ada kontrak terbuka" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Email o numero di telefono" + "value" : "Nessun contratto aperto" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "メールアドレスまたは電話番号" + "value" : "有効な契約はありません" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "이메일 또는 전화번호" + "value" : "진행 중인 계약 없음" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "E-mail of telefonnummer" + "value" : "Geen open contracten" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "E-mail lub numer telefonu" + "value" : "Brak otwartych kontraktów" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nenhum contrato aberto" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "E-mail ou número de telefone" + "value" : "Nenhum contrato aberto" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "E-mail ou número de telemóvel" + "value" : "Nenhum contrato aberto" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Электронная почта или номер телефона" + "value" : "Нет открытых контрактов" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "E-post eller telefonnummer" + "value" : "Inga öppna kontrakt" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "อีเมลหรือหมายเลขโทรศัพท์" + "value" : "ไม่มีสัญญาที่เปิดอยู่" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Електронна пошта або номер телефону" + "value" : "Немає відкритих контрактів" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Email hoặc số điện thoại" + "value" : "Không có hợp đồng đang mở" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "邮箱或手机号码" + "value" : "没有进行中的合约" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "電郵或電話號碼" + "value" : "沒有進行中的合約" } } } }, - "Enter" : { + "Notifications" : { + "comment" : "Settings", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "أدخل" + "value" : "الإشعارات" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Enter" + "value" : "Oznámení" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Eintreten" + "value" : "Benachrichtigungen" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Εισαγωγή" + "value" : "Ειδοποιήσεις" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Enter" + "value" : "Notifications" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Entrar" + "value" : "Notificaciones" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Entrar" + "value" : "Notificaciones" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Notificaciones" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Entrer" + "value" : "Notifications" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "היכנס" + "value" : "התראות" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "प्रवेश करें" + "value" : "सूचनाएं" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Masuk" + "value" : "Notifikasi" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Entra" + "value" : "Notifiche" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "入る" + "value" : "通知" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "들어가기" + "value" : "알림" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Invoeren" + "value" : "Notificaties" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Wejdź" + "value" : "Powiadomienia" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Notificações" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Entrar" + "value" : "Notificações" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Entrar" + "value" : "Notificações" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Войти" + "value" : "Уведомления" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Gå in" + "value" : "Aviseringar" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "เข้าสู่" + "value" : "การแจ้งเตือน" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Увійти" + "value" : "Сповіщення" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Vào" + "value" : "Thông báo" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "进入" + "value" : "通知" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "進入" + "value" : "通知" } } } }, - "Enter a bonus referral code" : { + "of %@" : { + "comment" : "A label that shows the total size of the contract.", + "isCommentAutoGenerated" : true, "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "أدخل رمز الإحالة المكافأة" + "value" : "من %@" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Vložte bonusový referální kód" + "value" : "z %@" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Geben Sie einen Bonus-Empfehlungscode ein" + "value" : "von %@" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Εισαγάγετε έναν κωδικό παραπομπής μπόνους" + "value" : "από %@" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Enter a bonus referral code" + "value" : "of %@" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Introduce un código de referencia de bonificación" + "value" : "de %@" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Ingresa un código de referencia de bonificación" + "value" : "de %@" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "de %@" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Entrez un code de parrainage bonus" + "value" : "sur %@" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "הזן קוד הפניה בונוס" + "value" : "מתוך %@" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "बोनस रेफरल कोड दर्ज करें" + "value" : "%@ में से" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Masukkan kode referensi bonus" + "value" : "dari %@" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Inserisci un codice di riferimento bonus" + "value" : "su %@" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "ボーナス紹介コードを入力" + "value" : "/ %@" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "보너스 추천 코드를 입력하세요" + "value" : "/ %@" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Voer een bonusreferentiecode in" + "value" : "van %@" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Wprowadź kod polecający bonus" + "value" : "z %@" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "de %@" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Digite um código de referência bônus" + "value" : "de %@" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Digite um código de referência bônus" + "value" : "de %@" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Введите бонусный реферальный код" + "value" : "из %@" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Ange en bonusreferenskod" + "value" : "av %@" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "ป้อนรหัสอ้างอิงโบนัส" + "value" : "จาก %@" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Введіть бонусний реферальний код" + "value" : "з %@" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Nhập mã giới thiệu thưởng" + "value" : "trên %@" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "输入奖励推荐码" + "value" : "/ %@" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "輸入獎勵推薦碼" + "value" : "/ %@" } } } }, - "Enter a name for your network" : { + "Off" : { + "comment" : "A label for a DNS setting that is currently off.", + "isCommentAutoGenerated" : true, "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "أدخل اسمًا لشبكتك" + "value" : "إيقاف" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Vložte jméno pro vaší sít" + "value" : "Vypnuto" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Geben Sie einen Namen für Ihr Netzwerk ein" + "value" : "Aus" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Εισάγετε ένα όνομα για το δίκτυό σας" + "value" : "Ανενεργό" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Enter a name for your network" + "value" : "Off" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Introduce un nombre para tu red" + "value" : "Desactivado" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Ingresa un nombre para tu red" + "value" : "Desactivado" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Desactivado" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Entrez un nom pour votre réseau" + "value" : "Désactivé" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "הזן שם לרשת שלך" + "value" : "כבוי" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "अपने नेटवर्क के लिए एक नाम दर्ज करें" + "value" : "बंद" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Masukkan nama untuk jaringan Anda" + "value" : "Nonaktif" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Inserisci un nome per la tua rete" + "value" : "Non attivo" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "ネットワーク名を入力" + "value" : "オフ" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "네트워크 이름을 입력하세요" + "value" : "꺼짐" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Voer een naam in voor uw netwerk" + "value" : "Uit" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Wprowadź nazwę swojej sieci" + "value" : "Wyłączone" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Desativado" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Digite um nome para sua rede" + "value" : "Desativado" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Introduza um nome para a sua rede" + "value" : "Desativado" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Введите название вашей сети" + "value" : "Выкл." } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Ange ett namn för ditt nätverk" + "value" : "Av" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "ป้อนชื่อสำหรับเครือข่ายของคุณ" + "value" : "ปิด" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Введіть назву вашої мережі" + "value" : "Вимк." } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Nhập tên cho mạng của bạn" + "value" : "Tắt" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "输入您的网络名称" + "value" : "关" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "輸入你的網絡名稱" + "value" : "關" } } } }, - "Enter a Solana USDC wallet address" : { + "On" : { + "comment" : "A label for a DNS setting that is enabled.", + "isCommentAutoGenerated" : true, "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "أدخل عنوان محفظة USDC على سولانا" + "value" : "تشغيل" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Zadejte adresu peněženky USDC na Solaně" + "value" : "Zapnuto" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Geben Sie eine Solana USDC-Wallet-Adresse ein" + "value" : "An" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Εισαγάγετε μια διεύθυνση πορτοφολιού USDC στο Solana" + "value" : "Ενεργό" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Enter a Solana USDC wallet address" + "value" : "On" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Introduce una dirección de monedero USDC en Solana" + "value" : "Activado" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Ingresa una dirección de billetera USDC en Solana" + "value" : "Activado" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Activado" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Saisissez une adresse de portefeuille USDC sur Solana" + "value" : "Activé" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "הזן כתובת ארנק USDC ב-Solana" + "value" : "מופעל" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "Solana USDC वॉलेट पता दर्ज करें" + "value" : "चालू" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Masukkan alamat dompet USDC Solana" + "value" : "Aktif" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Inserisci un indirizzo wallet USDC su Solana" + "value" : "Attivo" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "Solana USDCウォレットアドレスを入力してください" + "value" : "オン" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "Solana USDC 지갑 주소를 입력하세요" + "value" : "켜짐" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Voer een Solana USDC-walletadres in" + "value" : "Aan" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Wprowadź adres portfela USDC na Solanie" + "value" : "Włączone" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ativado" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Digite um endereço de carteira USDC na Solana" + "value" : "Ativado" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Introduza um endereço de carteira USDC na Solana" + "value" : "Ativado" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Введите адрес кошелька USDC в сети Solana" + "value" : "Вкл." } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Ange en Solana USDC-plånboksadress" + "value" : "På" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "ป้อนที่อยู่กระเป๋าเงิน USDC บน Solana" + "value" : "เปิด" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Введіть адресу гаманця USDC на Solana" + "value" : "Увімк." } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Nhập địa chỉ ví USDC trên Solana" + "value" : "Bật" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "请输入 Solana USDC 钱包地址" + "value" : "开" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "請輸入 Solana USDC 錢包地址" + "value" : "開" } } } }, - "Enter balance code" : { + "or" : { + "comment" : "Referring to the two options 'Get started' *or* 'Login with Apple'", "localizations" : { - "ar" : { - "stringUnit" : { - "state" : "translated", - "value" : "أدخل رمز الرصيد" - } - }, - "cs" : { - "stringUnit" : { - "state" : "translated", - "value" : "Zadejte kód na zůstatek" - } - }, - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Guthabencode eingeben" - } - }, - "el" : { - "stringUnit" : { - "state" : "translated", - "value" : "Εισαγάγετε κωδικό υπολοίπου" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Enter balance code" - } - }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Ingresa el código de saldo" - } - }, - "es-419" : { + "ar" : { "stringUnit" : { "state" : "translated", - "value" : "Ingresa el código de saldo" + "value" : "أو" } }, - "fr" : { + "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Entrez le code de solde" + "value" : "nebo" } }, - "he" : { + "de" : { "stringUnit" : { "state" : "translated", - "value" : "הזן קוד יתרה" + "value" : "oder" } }, - "hi" : { + "el" : { "stringUnit" : { "state" : "translated", - "value" : "बैलेंस कोड दर्ज करें" + "value" : "ή" } }, - "id" : { + "en" : { "stringUnit" : { "state" : "translated", - "value" : "Masukkan kode saldo" + "value" : "or" } }, - "it" : { + "es" : { "stringUnit" : { "state" : "translated", - "value" : "Inserisci il codice saldo" + "value" : "o" } }, - "ja" : { + "es-MX" : { "stringUnit" : { "state" : "translated", - "value" : "残高コードを入力" + "value" : "o" } }, - "ko" : { + "fr" : { "stringUnit" : { "state" : "translated", - "value" : "잔액 코드를 입력하세요" + "value" : "ou" } }, - "nl" : { + "hi" : { "stringUnit" : { "state" : "translated", - "value" : "Voer saldo code in" + "value" : "या" } }, - "pl" : { + "ja" : { "stringUnit" : { "state" : "translated", - "value" : "Wprowadź kod salda" + "value" : "または" } }, - "pt-BR" : { + "ko" : { "stringUnit" : { "state" : "translated", - "value" : "Digite o código de saldo" + "value" : "또는" } }, - "pt-PT" : { + "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Introduza o código de saldo" + "value" : "of" } }, - "ru" : { + "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Введите код баланса" + "value" : "lub" } }, - "sv" : { + "pt" : { "stringUnit" : { "state" : "translated", - "value" : "Ange saldo-kod" + "value" : "ou" } }, - "th" : { + "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "กรอกรหัสยอดเงิน" + "value" : "ou" } }, - "uk" : { + "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Введіть код балансу" + "value" : "или" } }, - "vi" : { + "th" : { "stringUnit" : { "state" : "translated", - "value" : "Nhập mã số dư" + "value" : "หรือ" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "输入余额码" - } - }, - "zh-HK" : { - "stringUnit" : { - "state" : "translated", - "value" : "輸入餘額碼" + "value" : "或" } } } }, - "Enter network referral code" : { + "Other" : { + "comment" : "Used in Main/Account/Wallets/ConnectWalletSheet/ConnectWalletSheetView.swift.", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "أدخل رمز الإحالة للشبكة" + "value" : "آخر" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Zadejte síťový referenční kód" + "value" : "Jiné" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Netzwerk-Empfehlungscode eingeben" + "value" : "Andere" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Εισαγάγετε τον κωδικό παραπομπής δικτύου" + "value" : "Άλλο" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Enter network referral code" + "value" : "Other" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Ingrese el código de referencia de la red" + "value" : "Otro" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Ingrese el código de referencia de la red" + "value" : "Otro" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Entrez le code de parrainage du réseau" + "value" : "Autre" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "הזן קוד הפניה של הרשת" + "value" : "אחר" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "नेटवर्क रेफ़रल कोड दर्ज करें" + "value" : "अन्य" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Masukkan kode referensi jaringan" + "value" : "Lainnya" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Inserisci il codice di riferimento della rete" + "value" : "Altro" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "ネットワーク紹介コードを入力" + "value" : "その他" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "네트워크 추천 코드를 입력하세요" + "value" : "기타" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Voer een verwijzingscode in" + "value" : "Andere" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Wprowadź kod polecenia sieci" + "value" : "Inne" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Insira o código de indicação da rede" + "value" : "Outro" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Introduza o código de referência da rede" + "value" : "Outro" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Введите реферальный код сети" + "value" : "Другое" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Ange nätverksreferenskod" + "value" : "Annat" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "ป้อนรหัสผู้อ้างอิงของเครือข่าย" + "value" : "อื่นๆ" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Введіть реферальний код мережі" + "value" : "Інше" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Nhập mã giới thiệu mạng" + "value" : "Khác" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "输入网络推荐码" + "value" : "其他" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "輸入網絡推薦碼" + "value" : "其他" } } } }, - "Enter URnetwork" : { + "Participate in the network and get free access to the community edition." : { + "comment" : "Call to action", + "extractionState" : "stale", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "أدخل URnetwork" + "value" : "شارك في الشبكة واحصل على وصول مجاني إلى إصدار المجتمع." } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Vkročte do URnetwork" + "value" : "Zapojte se do sítě a získejte bezplatný přístup k komunitní edici." } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork betreten" + "value" : "Nimm am Netzwerk teil und erhalte kostenlosen Zugang zur Community Edition." } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Εισαγωγή στο URnetwork" + "value" : "Συμμετέχετε στο δίκτυο και αποκτήστε δωρεάν πρόσβαση στην έκδοση κοινότητας." } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Enter URnetwork" + "value" : "Participate in the network and get free access to the community edition." } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Entrar en URnetwork" + "value" : "Participa en la red y obtén acceso gratuito a la edición comunitaria." } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Entrar en URnetwork" + "value" : "Participa en la red y obtén acceso gratuito a la edición comunitaria." + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Participa en la red y obtén acceso gratuito a la edición comunitaria." } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Entrer dans URnetwork" + "value" : "Participez au réseau et obtenez un accès gratuit à l’édition communautaire." } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "היכנס ל-URnetwork" + "value" : "השתתף ברשת וקבל גישה חינם למהדורת הקהילה." } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork में प्रवेश करें" + "value" : "नेटवर्क में भाग लें और कम्युनिटी एडिशन तक मुफ्त पहुंच प्राप्त करें।" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Masuk URnetwork" + "value" : "Berpartisipasi di jaringan dan dapatkan akses gratis ke edisi komunitas." } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Entra in URnetwork" + "value" : "Partecipa alla rete e ottieni l’accesso gratuito all’edizione community." } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "URnetworkに入る" + "value" : "ネットワークに参加して、コミュニティ版に無料でアクセスしましょう。" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork에 들어가기" + "value" : "네트워크에 참여하고 커뮤니티 에디션을 무료로 이용하세요." } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Voer URnetwork in" + "value" : "Neem deel aan het netwerk en krijg gratis toegang tot de community-editie." } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Wejdź do URnetwork" + "value" : "Dołącz do sieci i uzyskaj bezpłatny dostęp do wersji społecznościowej." + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Participe na rede e tenha acesso gratuito à edição comunitária." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Entrar no URnetwork" + "value" : "Participe da rede e tenha acesso gratuito à edição comunitária." } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Entrar no URnetwork" + "value" : "Participe na rede e tenha acesso gratuito à edição comunitária." } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Войти в URnetwork" + "value" : "Участвуйте в сети и получите бесплатный доступ к версии для сообщества." } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Gå in i URnetwork" + "value" : "Delta i nätverket och få gratis tillgång till community-utgåvan." } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "เข้าสู่ URnetwork" + "value" : "เข้าร่วมเครือข่ายและรับสิทธิ์เข้าถึงฉบับชุมชนฟรี" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Увійти в URnetwork" + "value" : "Беріть участь у мережі та отримайте безкоштовний доступ до спільнотної версії." } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Vào URnetwork" + "value" : "Tham gia mạng lưới và nhận quyền truy cập miễn phí vào phiên bản cộng đồng." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "进入 URnetwork" + "value" : "参与网络并免费获得社区版的访问权限。" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "進入 URnetwork" + "value" : "參與網絡,免費獲得社群版存取權限。" } } } }, - "Enter wallet address" : { + "Password" : { + "comment" : "Label for the password input field.", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "أدخل عنوان المحفظة" + "value" : "كلمة المرور" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Vložte adresu peněženky" + "value" : "Heslo" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Geben Sie die Wallet-Adresse ein" + "value" : "Passwort" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Εισαγάγετε τη διεύθυνση πορτοφολιού" + "value" : "Κωδικός πρόσβασης" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Enter wallet address" + "value" : "Password" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Ingrese la dirección de la billetera" + "value" : "Contraseña" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Ingrese la dirección de la billetera" + "value" : "Contraseña" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Contraseña" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Entrez l'adresse du portefeuille" + "value" : "Mot de passe" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "הזן כתובת ארנק" + "value" : "סיסמה" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "वॉलेट पता दर्ज करें" + "value" : "पासवर्ड" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Masukkan alamat dompet" + "value" : "Kata sandi" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Inserisci l'indirizzo del portafoglio" + "value" : "Password" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "ウォレットアドレスを入力" + "value" : "パスワード" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "지갑 주소 입력" + "value" : "비밀번호" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Voer walletadres in" + "value" : "Wachtwoord" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Wprowadź adres portfela" + "value" : "Hasło" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Senha" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Digite o endereço da carteira" + "value" : "Senha" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Digite o endereço da carteira" + "value" : "Senha" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Введите адрес кошелька" + "value" : "Пароль" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Ange plånboksadress" + "value" : "Lösenord" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "ป้อนที่อยู่กระเป๋าเงิน" + "value" : "รหัสผ่าน" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Введіть адресу гаманця" + "value" : "Пароль" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Nhập địa chỉ ví" + "value" : "Mật khẩu" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "输入钱包地址" + "value" : "密码" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "輸入錢包地址" + "value" : "密碼" } } } }, - "Enter your one-time auth code" : { + "Password must be at least 12 characters long" : { + "comment" : "Used in Authenticate/CreateNetwork/CreateNetworkView.swift.", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "أدخل رمز المصادقة لمرة واحدة" + "value" : "يجب أن تكون كلمة المرور مكونة من 12 حرفًا على الأقل" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Zadejte jednorázový ověřovací kód" + "value" : "Heslo musí být minimálně 12 znaků dlouhé" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Geben Sie Ihren einmaligen Auth-Code ein" + "value" : "Das Passwort muss mindestens 12 Zeichen lang sein" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Εισάγετε τον μοναδικό κωδικό ταυτοποίησης" + "value" : "Ο κωδικός πρόσβασης πρέπει να έχει τουλάχιστον 12 χαρακτήρες" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Enter your one-time auth code" + "value" : "Password must be at least 12 characters long" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Ingresa tu código de autenticación de una sola vez" + "value" : "La contraseña debe tener al menos 12 caracteres" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Ingresa tu código de autenticación de una sola vez" + "value" : "La contraseña debe tener al menos 12 caracteres" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Entrez votre code d’authentification à usage unique" + "value" : "Le mot de passe doit comporter au moins 12 caractères" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "הזן את קוד האימות החד-פעמי שלך" + "value" : "הסיסמה חייבת להיות באורך של לפחות 12 תווים" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "अपना एक बार का ऑथ कोड दर्ज करें" + "value" : "पासवर्ड कम से कम 12 अक्षरों का होना चाहिए" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Masukkan kode autentikasi satu kali Anda" + "value" : "Kata sandi harus terdiri dari minimal 12 karakter" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Inserisci il tuo codice di autenticazione monouso" + "value" : "La password deve essere lunga almeno 12 caratteri" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "ワンタイム認証コードを入力してください" + "value" : "パスワードは12文字以上である必要があります" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "일회용 인증 코드를 입력하세요" + "value" : "비밀번호는 최소 12자 이상이어야 합니다" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Voer uw eenmalige auth-code in" + "value" : "Wachtwoord moet minstens 12 tekens lang zijn" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Wprowadź swój jednorazowy kod autoryzacyjny" + "value" : "Hasło musi mieć co najmniej 12 znaków" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Digite seu código de autenticação único" + "value" : "A senha deve ter pelo menos 12 caracteres" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Introduza o seu código de autenticação único" + "value" : "A senha deve ter pelo menos 12 caracteres" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Введите ваш одноразовый код авторизации" + "value" : "Пароль должен содержать не менее 12 символов" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Ange din engångsautentiseringskod" + "value" : "Lösenordet måste vara minst 12 tecken långt" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "ป้อนรหัสยืนยันแบบใช้ครั้งเดียวของคุณ" + "value" : "รหัสผ่านต้องมีความยาวอย่างน้อย 12 ตัวอักษร" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Введіть ваш одноразовий код авторизації" + "value" : "Пароль повинен містити щонайменше 12 символів" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Nhập mã xác thực một lần của bạn" + "value" : "Mật khẩu phải dài ít nhất 12 ký tự" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "输入您的一次性认证码" + "value" : "密码必须至少12个字符长" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "輸入你的一次性認證碼" + "value" : "密碼必須至少12個字符長" } } } }, - "Enter your phone number or email" : { + "Password reset link sent to %@." : { + "comment" : "Snackbar confirming the password reset link was sent. The argument is the user's email or phone. Used in Authenticate/ResetPassword/ResetPasswordView.swift and Main/Account/Profile/ProfileView.swift.", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "أدخل رقم هاتفك أو بريدك الإلكتروني" + "value" : "تم إرسال رابط إعادة تعيين كلمة المرور إلى %@." } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Vložte vaše telefonní číslo nebo email" + "value" : "Odkaz pro obnovení hesla byl odeslán na %@." } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Telefonnummer oder E-Mail eingeben" + "value" : "Link zum Zurücksetzen des Passworts an %@ gesendet." } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Εισάγετε τον αριθμό τηλεφώνου ή email" + "value" : "Ο σύνδεσμος επαναφοράς κωδικού πρόσβασης στάλθηκε στο %@." } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Enter your phone number or email" + "value" : "Password reset link sent to %@." } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Introduce tu número de teléfono o email" + "value" : "Enlace para restablecer la contraseña enviado a %@." } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Ingresa tu número de teléfono o correo" + "value" : "Enlace para restablecer la contraseña enviado a %@." + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enlace para restablecer la contraseña enviado a %@." } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Entrez votre numéro de téléphone ou e-mail" + "value" : "Lien de réinitialisation du mot de passe envoyé à %@." } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "הזן את מספר הטלפון או האימייל שלך" + "value" : "קישור לאיפוס הסיסמה נשלח אל %@." } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "अपना फ़ोन नंबर या ईमेल दर्ज करें" + "value" : "पासवर्ड रीसेट लिंक %@ पर भेजा गया।" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Masukkan nomor telepon atau email Anda" + "value" : "Tautan atur ulang kata sandi telah dikirim ke %@." } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Inserisci il tuo numero di telefono o email" + "value" : "Link per reimpostare la password inviato a %@." } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "電話番号またはメールアドレスを入力" + "value" : "パスワードリセット用リンクを%@に送信しました。" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "전화번호 또는 이메일을 입력하세요" + "value" : "비밀번호 재설정 링크를 %@(으)로 보냈습니다." } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Voer je telefoonnummer of e-mailadres in" + "value" : "Link voor het opnieuw instellen van je wachtwoord verzonden naar %@." } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Wprowadź numer telefonu lub email" + "value" : "Link do zresetowania hasła został wysłany na %@." + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ligação para repor a palavra-passe enviada para %@." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Digite seu número de telefone ou email" + "value" : "Link de redefinição de senha enviado para %@." } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Introduza o seu número de telefone ou email" + "value" : "Ligação para repor a palavra-passe enviada para %@." } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Введите номер телефона или email" + "value" : "Ссылка для сброса пароля отправлена на %@." } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Ange ditt telefonnummer eller e-post" + "value" : "Länk för lösenordsåterställning skickad till %@." } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "ป้อนหมายเลขโทรศัพท์หรืออีเมลของคุณ" + "value" : "ส่งลิงก์รีเซ็ตรหัสผ่านไปยัง %@ แล้ว" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Введіть номер телефону або email" + "value" : "Посилання для скидання пароля надіслано на %@." } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Nhập số điện thoại hoặc email của bạn" + "value" : "Đã gửi liên kết đặt lại mật khẩu đến %@." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "输入您的手机号码或邮箱" + "value" : "密码重置链接已发送至 %@。" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "輸入你的電話號碼或電郵" - } - } - } - }, - "Error connecting to URnetwork" : { - "extractionState" : "stale", - "localizations" : { - "cs" : { - "stringUnit" : { - "state" : "translated", - "value" : "Chyba při připojování k síti URnetwork" - } - }, - "nl" : { - "stringUnit" : { - "state" : "translated", - "value" : "Fout bij verbinden met URnetwork" + "value" : "密碼重設連結已發送至 %@。" } } } }, - "Error loading your account plan." : { + "Payment Pending" : { + "comment" : "Used in Main/Account/PayoutItem/PayoutItemView.swift.", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "خطأ في تحميل خطة حسابك." + "value" : "الدفع معلق" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Chyba při načítání vašeho tarifu." + "value" : "Čekající platba" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Fehler beim Laden Ihres Tarifplans." + "value" : "Zahlung ausstehend" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Σφάλμα κατά τη φόρτωση του πλάνου λογαριασμού σας." + "value" : "Εκκρεμεί πληρωμή" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Error loading your account plan." + "value" : "Payment Pending" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Error al cargar tu plan de cuenta." + "value" : "Pago pendiente" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Error al cargar tu plan de cuenta." + "value" : "Pago pendiente" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Erreur lors du chargement de votre forfait." + "value" : "Paiement en attente" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "שגיאה בטעינת תוכנית החשבון שלך." + "value" : "התשלום ממתין" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "आपकी अकाउंट योजना लोड करने में त्रुटि।" + "value" : "भुगतान लंबित है" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Terjadi kesalahan saat memuat paket akun Anda." + "value" : "Pembayaran tertunda" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Errore nel caricamento del tuo piano account." + "value" : "Pagamento in sospeso" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "アカウントプランの読み込みエラー。" + "value" : "支払い保留中" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "계정 플랜을 불러오는 중 오류가 발생했습니다." + "value" : "결제 대기 중" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Fout bij het laden van je abonnementsplan." + "value" : "Betaling in behandeling" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Błąd podczas wczytywania Twojego planu konta." + "value" : "Płatność oczekuje" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Erro ao carregar seu plano de conta." + "value" : "Pagamento pendente" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Erro ao carregar o seu plano de conta." + "value" : "Pagamento pendente" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Ошибка загрузки вашего тарифного плана." + "value" : "Ожидание оплаты" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Fel vid inläsning av din kontoplan." + "value" : "Betalning väntar" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "เกิดข้อผิดพลาดในการโหลดแผนบัญชีของคุณ" + "value" : "การชำระเงินรอดำเนินการ" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Помилка завантаження вашого тарифного плану." + "value" : "Очікується оплата" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Lỗi khi tải gói tài khoản của bạn." + "value" : "Thanh toán đang chờ xử lý" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "加载你的账户套餐时出错。" + "value" : "付款待处理" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "載入你的帳戶方案時發生錯誤。" + "value" : "付款待處理" } } } }, - "Exclusions apply to the whole co-associated network cluster, so related traffic is caught together." : { - - }, - "Expires" : { + "Payout" : { + "comment" : "Used in Main/Account/AccountNavStackView.swift.", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "تنتهي" + "value" : "دفع" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Vyprší" + "value" : "Výplata" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Ablauf" + "value" : "Auszahlung" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Λήγει" + "value" : "Πληρωμή" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Expires" + "value" : "Payout" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Expira" + "value" : "Pago" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Expira" + "value" : "Pago" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Expire" + "value" : "Paiement" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "פג תוקף" + "value" : "תשלום" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "समाप्ति" + "value" : "भुगतान" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Kedaluwarsa" + "value" : "Pembayaran" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Scade" + "value" : "Pagamento" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "有効期限" + "value" : "支払い" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "만료" + "value" : "지급" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Verloopt" + "value" : "Uitbetaling" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Wygasa" + "value" : "Wypłata" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Expira" + "value" : "Pagamento" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Expira" + "value" : "Pagamento" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Истекает" + "value" : "Выплата" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Utgår" + "value" : "Utbetalning" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "หมดอายุ" + "value" : "การจ่ายเงิน" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Закінчується" + "value" : "Виплата" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Hết hạn" + "value" : "Thanh toán" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "到期" + "value" : "支付" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "到期" + "value" : "支付" } } } }, - "Export Logs" : { + "Payout wallet updated" : { + "comment" : "Snackbar shown after the payout wallet is changed. Used in Main/Account/Wallet/WalletView.swift.", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "تصدير السجلات" + "value" : "تم تحديث محفظة الدفع" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Exportovat logy" + "value" : "Výplatní peněženka byla aktualizována" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Logs exportieren" + "value" : "Auszahlungs-Wallet aktualisiert" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Εξαγωγή αρχείων καταγραφής" + "value" : "Το πορτοφόλι πληρωμής ενημερώθηκε" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Export Logs" + "value" : "Payout wallet updated" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Exportar registros" + "value" : "Billetera de pago actualizada" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Exportar registros" + "value" : "Cartera de pago actualizada" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cartera de pago actualizada" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Exporter les journaux" + "value" : "Portefeuille de paiement mis à jour" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "ייצא לוגים" + "value" : "ארנק התשלום עודכן" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "लॉग्स एक्सपोर्ट करें" + "value" : "पेआउट वॉलेट अपडेट किया गया" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Ekspor log" + "value" : "Dompet pembayaran diperbarui" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Esporta log" + "value" : "Portafoglio di pagamento aggiornato" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "ログをエクスポート" + "value" : "支払いウォレットを更新しました" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "로그 내보내기" + "value" : "지급 지갑이 업데이트되었습니다" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Logbestanden exporteren" + "value" : "Uitbetalingswallet bijgewerkt" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Eksportuj logi" + "value" : "Zaktualizowano portfel wypłat" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Carteira de pagamento atualizada" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Exportar logs" + "value" : "Carteira de pagamento atualizada" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Exportar logs" + "value" : "Carteira de pagamento atualizada" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Экспорт логов" + "value" : "Кошелек для выплат обновлен" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Exportera loggar" + "value" : "Utbetalningsplånbok uppdaterad" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "ส่งออกล็อก" + "value" : "อัปเดตกระเป๋ารับยอดจ่ายแล้ว" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Експортувати логи" + "value" : "Гаманець для виплат оновлено" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Xuất nhật ký" + "value" : "Đã cập nhật ví thanh toán" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "导出日志" + "value" : "支付钱包已更新" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "匯出日誌" - } - } - } - }, - "Failed to connect" : { - "localizations" : { - "cs" : { - "stringUnit" : { - "state" : "translated", - "value" : "Nepodařilo se připojit" - } - }, - "nl" : { - "stringUnit" : { - "state" : "translated", - "value" : "Fout tijdens verbinden" - } - } - } - }, - "Failed to connect URnetwork" : { - "localizations" : { - "cs" : { - "stringUnit" : { - "state" : "translated", - "value" : "Nepodařilo se připojit síť URnetwork" - } - }, - "nl" : { - "stringUnit" : { - "state" : "translated", - "value" : "Verbinding met URnetwork mislukt" + "value" : "支付錢包已更新" } } } }, - "Failed to disconnect URnetwork VPN" : { - - }, - "Failed to start URnetwork VPN" : { - - }, - "Feedback" : { + "Payout Wallets" : { + "comment" : "Wallet Screen", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "ملاحظات" + "value" : "محافظ الدفع" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Odezva" + "value" : "Výplatní peněženky" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Feedback" + "value" : "Auszahlungs-Wallets" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Ανατροφοδότηση" + "value" : "Πορτοφόλια πληρωμής" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Feedback" + "value" : "Payout Wallets" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Retroalimentación" + "value" : "Billeteras de pago" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Retroalimentación" + "value" : "Carteras de pago" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Carteras de pago" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Retour d'information" + "value" : "Portefeuilles de paiement" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "משוב" + "value" : "ארנקי תשלום" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "प्रतिक्रिया" + "value" : "पेआउट वॉलेट" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Umpan balik" + "value" : "Dompet Pembayaran" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Feedback" + "value" : "Portafogli di pagamento" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "フィードバック" + "value" : "支払いウォレット" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "피드백" + "value" : "지급 지갑" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Feedback" + "value" : "Uitbetalingswallets" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Portfele wypłat" } }, - "pl" : { + "pt" : { "stringUnit" : { "state" : "translated", - "value" : "Informacja zwrotna" + "value" : "Carteiras de pagamento" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Feedback" + "value" : "Carteiras de pagamento" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Feedback" + "value" : "Carteiras de pagamento" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Обратная связь" + "value" : "Кошельки для выплат" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Feedback" + "value" : "Utbetalningsplånböcker" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "ข้อเสนอแนะ" + "value" : "กระเป๋ารับยอดจ่าย" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Зворотній зв'язок" + "value" : "Гаманці для виплат" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Phản hồi" + "value" : "Ví thanh toán" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "反馈" + "value" : "支付钱包" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "反饋" + "value" : "支付錢包" } } } }, - "Fixed IP" : { - "comment" : "A toggle that allows users to enable or disable a fixed IP for their connection.", - "isCommentAutoGenerated" : true - }, - "Forgot your password?" : { + "Payouts occur every Sunday at 00:00 UTC, and require meeting a minimum USDC threshold." : { + "comment" : "Wallet Screen", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "هل نسيت كلمة المرور؟" + "value" : "تحدث الدفعات كل يوم أحد في الساعة 00:00 بتوقيت UTC، ويتطلب ذلك تحقيق حد أدنى من USDC." } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Zapomněli jste své heslo?" + "value" : "Výplaty probíhají každou neděli v 00:00 UTC a vyžadují dosažení minimální hranice USDC." } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Passwort vergessen?" + "value" : "Auszahlungen erfolgen jeden Sonntag um 00:00 UTC und erfordern das Erreichen eines Mindest-USDC-Schwellenwerts." } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Ξεχάσατε τον κωδικό σας;" + "value" : "Οι πληρωμές πραγματοποιούνται κάθε Κυριακή στις 00:00 UTC και απαιτούν την επίτευξη ενός ελάχιστου ορίου USDC." } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Forgot your password?" + "value" : "Payouts occur every Sunday at 00:00 UTC, and require meeting a minimum USDC threshold." } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "¿Olvidaste tu contraseña?" + "value" : "Los pagos se realizan cada domingo a las 00:00 UTC y requieren cumplir con un umbral mínimo de USDC." } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "¿Olvidaste tu contraseña?" + "value" : "Los pagos se realizan cada domingo a las 00:00 UTC y requieren cumplir con un umbral mínimo de USDC." + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Los pagos se realizan cada domingo a las 00:00 UTC y requieren cumplir con un umbral mínimo de USDC." } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Mot de passe oublié ?" + "value" : "Les paiements ont lieu chaque dimanche à 00h00 UTC et nécessitent d’atteindre un seuil minimum de USDC." } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "שכחת את הסיסמה?" + "value" : "התשלומים מתבצעים בכל יום ראשון בשעה 00:00 UTC ודורשים עמידה בסף מינימלי של USDC." } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "क्या आप पासवर्ड भूल गए?" + "value" : "भुगतान हर रविवार 00:00 UTC पर होते हैं, और न्यूनतम USDC सीमा पूरी करनी होती है।" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Lupa kata sandi Anda?" + "value" : "Pembayaran dilakukan setiap hari Minggu pukul 00:00 UTC dan memerlukan pencapaian ambang minimum USDC." } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Hai dimenticato la password?" + "value" : "I pagamenti avvengono ogni domenica alle 00:00 UTC e richiedono il raggiungimento di una soglia minima di USDC." } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "パスワードをお忘れですか?" + "value" : "支払いは毎週日曜日の00:00 UTCに行われ、最低USDC基準を満たす必要があります。" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "비밀번호를 잊으셨나요?" + "value" : "지급은 매주 일요일 00:00 UTC에 이루어지며, 최소 USDC 기준을 충족해야 합니다." } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Wachtwoord vergeten?" + "value" : "Uitbetalingen vinden elke zondag om 00:00 UTC plaats en vereisen het behalen van een minimale USDC-drempel." } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Zapomniałeś hasła?" + "value" : "Wypłaty odbywają się w każdą niedzielę o 00:00 UTC i wymagają spełnienia minimalnego progu USDC." + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Os pagamentos ocorrem todos os domingos às 00:00 UTC e exigem atingir um limite mínimo de USDC." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Esqueceu sua senha?" + "value" : "Os pagamentos ocorrem todo domingo às 00:00 UTC e exigem atingir um limite mínimo de USDC." } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Esqueceu sua senha?" + "value" : "Os pagamentos ocorrem todos os domingos às 00:00 UTC e exigem atingir um limite mínimo de USDC." } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Забыли пароль?" + "value" : "Выплаты происходят каждое воскресенье в 00:00 UTC и требуют достижения минимального порога USDC." } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Glömt ditt lösenord?" + "value" : "Utbetalningar sker varje söndag kl. 00:00 UTC och kräver att en minsta USDC-gräns uppnås." } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "ลืมรหัสผ่าน?" + "value" : "การจ่ายเงินจะเกิดขึ้นทุกวันอาทิตย์ เวลา 00:00 UTC และต้องถึงขั้นต่ำขั้นต่ำของ USDC" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Забули пароль?" + "value" : "Виплати відбуваються щонеділі о 00:00 UTC і вимагають досягнення мінімального порогу USDC." } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Quên mật khẩu?" + "value" : "Thanh toán diễn ra vào mỗi Chủ Nhật lúc 00:00 UTC và yêu cầu đạt ngưỡng USDC tối thiểu." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "忘记密码?" + "value" : "支付在每周日 00:00 UTC 进行,并且需要达到最低 USDC 门槛。" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "忘記密碼?" + "value" : "每週日 00:00 UTC 進行支付,並且需要達到最低 USDC 門檻。" } } } }, - "Free" : { + "Pending: %@ MB provided" : { + "comment" : "Used in Shared/Views/PaymentsList.swift.", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "مجاني" + "value" : "قيد الانتظار: تم توفير %@ ميغابايت" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Zdarma" + "value" : "Čekající: %@ MB poskytnuto" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Kostenlos" + "value" : "Ausstehend: %@ MB bereitgestellt" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Δωρεάν" + "value" : "Σε εκκρεμότητα: παρέχονται %@ MB" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Free" + "value" : "Pending: %@ MB provided" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Gratis" + "value" : "Pendiente: %@ MB proporcionados" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Gratis" + "value" : "Pendiente: %@ MB proporcionados" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Gratuit" + "value" : "En attente : %@ MB fournis" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "חינם" + "value" : "ממתין: סופקו %@ מגה-בייט" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "नि: शुल्क" + "value" : "लंबित: %@ एमबी प्रदान किए गए" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Gratis" + "value" : "Tertunda: %@ MB disediakan" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Gratuito" + "value" : "In sospeso: %@ MB forniti" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "無料" + "value" : "保留中: %@ MB 提供済み" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "무료" + "value" : "보류 중: %@ MB 제공됨" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Gratis" + "value" : "In behandeling: %@ geleverd" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Darmowy" + "value" : "Oczekujące: dostarczono %@ MB" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Grátis" + "value" : "Pendente: %@ MB fornecidos" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Grátis" + "value" : "Pendente: %@ MB fornecidos" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Бесплатно" + "value" : "В ожидании: предоставлено %@ МБ" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Gratis" + "value" : "Väntar: %@ MB tillhandahållna" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "ฟรี" + "value" : "รอดำเนินการ: ให้บริการ %@ MB" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Безкоштовно" + "value" : "Очікується: надано %@ МБ" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Miễn phí" + "value" : "Đang chờ xử lý: đã cung cấp %@ MB" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "免费" + "value" : "待处理:提供了 %@ MB" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "免費" + "value" : "待處理:提供了 %@ MB" } } } }, - "General" : { + "Phantom" : { + "comment" : "Used in Main/Account/Wallets/ConnectWalletSheet/ConnectWalletSheetView.swift.", + "shouldTranslate" : false + }, + "Placeholder" : { + "comment" : "Placeholder text for an empty text field.", + "isCommentAutoGenerated" : true, + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Placeholder" + } + } + } + }, + "Plan" : { + "comment" : "Upgrade subscription overlay", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "عام" + "value" : "خطة" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Plán" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Allgemein" + "value" : "Planen" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Γενικά" + "value" : "Πρόγραμμα" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "General" + "value" : "Plan" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "General" + "value" : "Plan" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "General" + "value" : "Plan" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Plan" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Général" + "value" : "Plan" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "כללי" + "value" : "תוכנית" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "सामान्य" + "value" : "योजना" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Umum" + "value" : "Paket" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Generale" + "value" : "Piano" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "一般" + "value" : "プラン" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "일반" + "value" : "플랜" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Algemeen" + "value" : "Plan" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Ogólne" + "value" : "Plan" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Plano" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Geral" + "value" : "Plano" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Geral" + "value" : "Plano" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Общие" + "value" : "Тариф" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Allmänt" + "value" : "Plan" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "ทั่วไป" + "value" : "แพ็กเกจ" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Загальні" + "value" : "План" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Chung" + "value" : "Kế hoạch" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "常规" + "value" : "计划" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "一般" + "value" : "計劃" } } } }, - "Get connected" : { + "Please install Phantom or Solflare to use this feature" : { + "comment" : "Used in Shared/Views/SolanaSignMessageSheet.swift.", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "اتصل الآن" + "value" : "يرجى تثبيت Phantom أو Solflare لاستخدام هذه الميزة" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Připojte se" + "value" : "Prosím nainstalujte peněženku/y Phantom či Solflare pro použité této featury." } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Verbinden" + "value" : "Bitte installiere Phantom oder Solflare, um diese Funktion zu nutzen" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Συνδεθείτε" + "value" : "Παρακαλώ εγκαταστήστε το Phantom ή το Solflare για να χρησιμοποιήσετε αυτήν τη λειτουργία" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Get connected" + "value" : "Please install Phantom or Solflare to use this feature" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Conéctate" + "value" : "Por favor, instala Phantom o Solflare para usar esta función" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Conéctate" + "value" : "Por favor, instala Phantom o Solflare para usar esta función" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Connectez-vous" + "value" : "Veuillez installer Phantom ou Solflare pour utiliser cette fonctionnalité" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "התחבר" + "value" : "אנא התקן את Phantom או Solflare כדי להשתמש בתכונה זו" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "कनेक्ट करें" + "value" : "कृपया इस सुविधा का उपयोग करने के लिए Phantom या Solflare इंस्टॉल करें" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Hubungkan" + "value" : "Silakan instal Phantom atau Solflare untuk menggunakan fitur ini" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Connettiti" + "value" : "Per favore, installa Phantom o Solflare per utilizzare questa funzionalità" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "接続する" + "value" : "この機能を使用するにはPhantomまたはSolflareをインストールしてください" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "연결하기" + "value" : "이 기능을 사용하려면 Phantom 또는 Solflare를 설치하십시오" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Verbinden" + "value" : "Installeer Phantom of Solflare om deze functie te gebruiken" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Połącz się" + "value" : "Zainstaluj Phantom lub Solflare, aby korzystać z tej funkcji" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Conectar" + "value" : "Instale o Phantom ou Solflare para usar este recurso" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Ligar-se" + "value" : "Instale o Phantom ou Solflare para usar esta funcionalidade" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Подключиться" + "value" : "Пожалуйста, установите Phantom или Solflare, чтобы использовать эту функцию" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Anslut" + "value" : "Vänligen installera Phantom eller Solflare för att använda denna funktion" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "เชื่อมต่อ" + "value" : "โปรดติดตั้ง Phantom หรือ Solflare เพื่อใช้คุณสมบัตินี้" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Підключитися" + "value" : "Будь ласка, встановіть Phantom або Solflare, щоб використовувати цю функцію" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Kết nối" + "value" : "Vui lòng cài đặt Phantom hoặc Solflare để sử dụng tính năng này" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "连接" + "value" : "请安装 Phantom 或 Solflare 以使用此功能" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "連接" + "value" : "請安裝 Phantom 或 Solflare 以使用此功能" } } } }, - "Get free access to the community edition." : { - - }, - "Get in touch" : { + "Please login to URnetwork" : { + "comment" : "Used in Shared/Intents/ConnectIntent.swift.", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "ابقى على تواصل" + "value" : "يرجى تسجيل الدخول إلى URnetwork" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Spojte se s námi" + "value" : "Prosím přihlašte se do URnetwork" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Kontakt aufnehmen" + "value" : "Bitte melde dich bei URnetwork an" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Επικοινωνήστε μαζί μας" + "value" : "Παρακαλώ συνδεθείτε στο URnetwork" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Get in touch" + "value" : "Please login to URnetwork" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Ponte en contacto" + "value" : "Por favor, inicia sesión en URnetwork" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Ponte en contacto" + "value" : "Por favor, inicia sesión en URnetwork" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Contactez-nous" + "value" : "Veuillez vous connecter à URnetwork" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "צור קשר" + "value" : "אנא התחבר ל-URnetwork" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "संपर्क करें" + "value" : "कृपया URnetwork में लॉग इन करें" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Hubungi kami" + "value" : "Silakan masuk ke URnetwork" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Contattaci" + "value" : "Per favore, accedi a URnetwork" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "お問い合わせ" + "value" : "URnetworkにログインしてください" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "문의하기" + "value" : "URnetwork에 로그인하십시오" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Neem contact op" + "value" : "Log in op URnetwork" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Skontaktuj się z nami" + "value" : "Zaloguj się do URnetwork" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Entre em contato" + "value" : "Faça login no URnetwork" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Entre em contato" + "value" : "Faça login no URnetwork" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Связаться с нами" + "value" : "Пожалуйста, войдите в URnetwork" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Kontakta oss" + "value" : "Vänligen logga in på URnetwork" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "ติดต่อเรา" + "value" : "โปรดเข้าสู่ระบบ URnetwork" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Зв'яжіться з нами" + "value" : "Будь ласка, увійдіть до URnetwork" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Liên hệ với chúng tôi" + "value" : "Vui lòng đăng nhập vào URnetwork" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "联系我们" + "value" : "请登录到 URnetwork" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "聯繫我們" + "value" : "請登錄到 URnetwork" } } } }, - "Get Pro" : { + "Please login to URnetwork to connect" : { + "comment" : "Used in Shared/Intents/ConnectIntent.swift.", + "localizations" : { + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pro připojení se přihlaste do URnetwork" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Please login to URnetwork to connect" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Log in op URnetwork om te verbinden" + } + } + } + }, + "Please login with one of: %@." : { + "comment" : "Call to action", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "احصل على برو" + "value" : "يرجى تسجيل الدخول باستخدام أحد: %@." } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Získat Pro" + "value" : "Přihlaste se prosím pomocí jednoho z těchto údajů: %@." } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Pro erhalten" + "value" : "Bitte melde dich mit einem der folgenden an: %@." } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Αποκτήστε Pro" + "value" : "Συνδεθείτε με ένα από τα εξής: %@." } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Get Pro" + "value" : "Please login with one of: %@." } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Obtener Pro" + "value" : "Inicia sesión con una de las siguientes opciones: %@." } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Obtener Pro" + "value" : "Inicia sesión con una de las siguientes opciones: %@." + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Inicia sesión con una de las siguientes opciones: %@." } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Obtenir Pro" + "value" : "Veuillez vous connecter avec l'un des éléments suivants : %@." } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "קבל Pro" + "value" : "אנא התחבר באמצעות אחת מהאפשרויות הבאות: %@." } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "प्रो प्राप्त करें" + "value" : "कृपया इनमें से किसी एक के साथ लॉगिन करें: %@." } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Dapatkan Pro" + "value" : "Silakan masuk dengan salah satu dari: %@." } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Ottieni Pro" + "value" : "Accedi con una delle seguenti opzioni: %@." } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "Proを取得" + "value" : "以下のいずれかでログインしてください:%@" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "Pro 받기" + "value" : "다음 중 하나로 로그인해 주세요: %@." } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Pro krijgen" + "value" : "Log in met één van de volgende opties: %@." } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Uzyskaj Pro" + "value" : "Zaloguj się za pomocą jednego z: %@." + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Faça login com uma das seguintes opções: %@." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Obter Pro" + "value" : "Faça login com uma das seguintes opções: %@." } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Obter Pro" + "value" : "Inicie sessão com uma das seguintes opções: %@." } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Получить Pro" + "value" : "Пожалуйста, войдите, используя один из следующих способов: %@." } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Skaffa Pro" + "value" : "Logga in med något av följande: %@." } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "รับ Pro" + "value" : "โปรดเข้าสู่ระบบด้วยหนึ่งใน %@" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Отримати Pro" + "value" : "Будь ласка, увійдіть, використовуючи один із таких способів: %@." } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Nhận Pro" + "value" : "Vui lòng đăng nhập bằng một trong các cách sau: %@." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "获取 Pro" + "value" : "请使用以下任一方式登录:%@。" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "獲取 Pro" - } - } - } - }, - "Get started" : { - "localizations" : { - "cs" : { - "stringUnit" : { - "state" : "translated", - "value" : "Začněte" - } - }, - "nl" : { - "stringUnit" : { - "state" : "translated", - "value" : "Aan de slag" + "value" : "請使用以下任一方式登入:%@。" } } } }, - "Guest" : { + "Please try again." : { + "comment" : "Call to action", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "ضيف" + "value" : "يرجى المحاولة مرة أخرى." } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Host" + "value" : "Prosím, zkuste to znovu." } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Gast" + "value" : "Bitte versuchen Sie es erneut." } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Επισκέπτης" + "value" : "Παρακαλώ δοκιμάστε ξανά." } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Guest" + "value" : "Please try again." } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Invitado" + "value" : "Por favor, inténtalo de nuevo." } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Invitado" + "value" : "Por favor, inténtalo de nuevo." + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Por favor, inténtalo de nuevo." } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Invité" + "value" : "Veuillez réessayer." } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "אורח" + "value" : "אנא נסה שוב." } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "अतिथि" + "value" : "कृपया पुनः प्रयास करें।" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Tamu" + "value" : "Silakan coba lagi." } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Ospite" + "value" : "Per favore riprova." } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "ゲスト" + "value" : "もう一度お試しください。" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "손님" + "value" : "다시 시도해 주세요." } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Gast" + "value" : "Probeer het alstublieft opnieuw." } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Gość" + "value" : "Proszę spróbować ponownie." + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Por favor, tente novamente." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Convidado" + "value" : "Por favor, tente novamente." } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Convidado" + "value" : "Por favor, tente novamente." } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Гость" + "value" : "Пожалуйста, попробуйте снова." } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Gäst" + "value" : "Försök igen, tack." } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "แขก" + "value" : "กรุณาลองใหม่อีกครั้ง" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Гість" + "value" : "Будь ласка, спробуйте ще раз." } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Khách" + "value" : "Vui lòng thử lại." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "访客" + "value" : "请再试一次。" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "訪客" + "value" : "請再試一次。" } } } }, - "Hide Provider List" : { + "Points breakdown" : { + "comment" : "Call to action", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "إخفاء قائمة المزودين" + "value" : "تفصيل النقاط" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Skrýt seznam poskytovatelů" + "value" : "Rozdělení bodů" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Anbieter-Liste ausblenden" + "value" : "Punkteübersicht" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Απόκρυψη λίστας παρόχων" + "value" : "Ανάλυση πόντων" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Hide Provider List" + "value" : "Points breakdown" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Ocultar lista de proveedores" + "value" : "Desglose de puntos" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Ocultar lista de proveedores" + "value" : "Desglose de puntos" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Desglose de puntos" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Masquer la liste des fournisseurs" + "value" : "Répartition des points" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "הסתר רשימת ספקים" + "value" : "פירוט נקודות" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "प्रदाता सूची छिपाएं" + "value" : "अंक विवरण" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Sembunyikan Daftar Penyedia" + "value" : "Rincian poin" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Nascondi elenco provider" + "value" : "Dettaglio punti" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "プロバイダーリストを非表示" + "value" : "ポイントの内訳" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "제공업체 목록 숨기기" + "value" : "포인트 내역" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Verberg Provider Lijst" + "value" : "Puntenoverzicht" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Ukryj listę dostawców" + "value" : "Szczegółowy podział punktów" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Detalhamento dos pontos" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Ocultar lista de provedores" + "value" : "Detalhamento dos pontos" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Ocultar lista de fornecedores" + "value" : "Detalhe dos pontos" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Скрыть список провайдеров" + "value" : "Разбивка очков" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Dölj leverantörslista" + "value" : "Poängfördelning" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "ซ่อนรายชื่อผู้ให้บริการ" + "value" : "รายละเอียดคะแนน" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Приховати список провайдерів" + "value" : "Розподіл балів" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Ẩn danh sách nhà cung cấp" + "value" : "Phân tích điểm" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "隐藏供应商列表" + "value" : "积分明细" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "隱藏供應商列表" + "value" : "積分明細" } } } }, - "How are we doing?" : { + "Private Network" : { + "comment" : "Network name when privacy is enabled", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "كيف نؤدي عملنا؟" + "value" : "شبكة خاصة" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Jak se nám daří?" + "value" : "Soukromá síť" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Wie machen wir uns?" + "value" : "Privates Netzwerk" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Πώς τα πάμε;" + "value" : "Ιδιωτικό δίκτυο" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "How are we doing?" + "value" : "Private Network" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "¿Qué tal lo estamos haciendo?" + "value" : "Red privada" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "¿Cómo lo estamos haciendo?" + "value" : "Red privada" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Red privada" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Comment nous débrouillons-nous ?" + "value" : "Réseau privé" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "איך אנחנו עושים את זה?" + "value" : "רשת פרטית" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "हम कैसा कर रहे हैं?" + "value" : "निजी नेटवर्क" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Bagaimana kinerja kami?" + "value" : "Jaringan Pribadi" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Come stiamo andando?" + "value" : "Rete privata" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "私たちの取り組みはいかがですか?" + "value" : "プライベートネットワーク" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "저희 서비스는 어떤가요?" + "value" : "프라이빗 네트워크" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Hoe gaat het met ons?" + "value" : "Privénetwerk" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Jak sobie radzimy?" + "value" : "Sieć prywatna" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rede privada" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Como estamos nos saindo?" + "value" : "Rede privada" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Como estamos a ir?" + "value" : "Rede privada" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Как мы справляемся?" + "value" : "Частная сеть" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Hur går det för oss?" + "value" : "Privat nätverk" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "เราทำงานได้ดีแค่ไหน?" + "value" : "เครือข่ายส่วนตัว" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Як ми справляємось?" + "value" : "Приватна мережа" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Chúng tôi đang làm thế nào?" + "value" : "Mạng riêng tư" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "我们做得怎么样?" + "value" : "私有网络" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "我們做得如何?" + "value" : "私人網絡" } } } }, - "I agree to URnetwork's [Terms and Services](https://ur.io/terms) and [Privacy Policy](https://ur.io/privacy)" : { + "Pro" : { + "comment" : "Display name for the paid plan. The WIRE value stays \"supporter\" (subscription_type / current_subscription.plan) because already-shipped app versions parse that string; only what the user reads changes.", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "أوافق على [شروط خدمة] URnetwork (https://ur.io/terms) و [سياسة الخصوصية] (https://ur.io/privacy)" + "value" : "Pro" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Souhlasím s [Podmínkami a službami](https://ur.io/terms) a [Zásadami ochrany osobních údajů](https://ur.io/privacy) společnosti URnetwork." + "value" : "Pro" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Ich stimme den [Nutzungsbedingungen](https://ur.io/terms) und der [Datenschutzrichtlinie](https://ur.io/privacy) von URnetwork zu." + "value" : "Pro" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Συμφωνώ με τους [Όρους και τις Υπηρεσίες](https://ur.io/terms) και την [Πολιτική Απορρήτου](https://ur.io/privacy) της URnetwork" + "value" : "Pro" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "I agree to URnetwork's [Terms and Services](https://ur.io/terms) and [Privacy Policy](https://ur.io/privacy)" + "value" : "Pro" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Acepto los [Términos y servicios](https://ur.io/terms) y la [Política de privacidad](https://ur.io/privacy) de URnetwork" + "value" : "Pro" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Acepto los [Términos y servicios](https://ur.io/terms) y la [Política de privacidad](https://ur.io/privacy) de URnetwork" + "value" : "Pro" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pro" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "J'accepte les [Conditions d'utilisation](https://ur.io/terms) et la [Politique de confidentialité](https://ur.io/privacy) d'URnetwork" + "value" : "Pro" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "אני מסכים ל[תנאים והשירותים](https://ur.io/terms) ול[מדיניות הפרטיות](https://ur.io/privacy) של URnetwork" + "value" : "Pro" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "मैं URnetwork के [नियम और सेवाएँ](https://ur.io/terms) और [गोपनीयता नीति](https://ur.io/privacy) से सहमत हूँ" + "value" : "Pro" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Saya menyetujui [Persyaratan dan Layanan](https://ur.io/terms) dan [Kebijakan Privasi](https://ur.io/privacy) URnetwork" + "value" : "Pro" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Accetto i [Termini di servizio](https://ur.io/terms) e l'[Informativa sulla privacy](https://ur.io/privacy) di URnetwork" + "value" : "Pro" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "URnetworkの[利用規約](https://ur.io/terms)および[プライバシーポリシー](https://ur.io/privacy)に同意します" + "value" : "Pro" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork의 [이용약관](https://ur.io/terms) 및 [개인정보 보호정책](https://ur.io/privacy)에 동의합니다." + "value" : "Pro" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Ik ga akkoord met de [Algemene Voorwaarden](https://ur.io/terms) en het [Privacybeleid](https://ur.io/privacy) van URnetwork" + "value" : "Pro" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Zgadzam się na [Warunki świadczenia usług](https://ur.io/terms) i [Politykę prywatności](https://ur.io/privacy) URnetwork" + "value" : "Pro" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pro" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Eu concordo com os [Termos e Serviços](https://ur.io/terms) e a [Política de Privacidade](https://ur.io/privacy) do URnetwork" + "value" : "Pro" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Concordo com os [Termos e Serviços](https://ur.io/terms) e a [Política de Privacidade](https://ur.io/privacy) da URnetwork" + "value" : "Pro" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Я согласен с [Условиями обслуживания](https://ur.io/terms) и [Политикой конфиденциальности](https://ur.io/privacy) URnetwork" + "value" : "Pro" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Jag godkänner URnetworks [Användarvillkor](https://ur.io/terms) och [Integritetspolicy](https://ur.io/privacy)" + "value" : "Pro" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "ฉันยอมรับ[ข้อกำหนดในการให้บริการ](https://ur.io/terms) และ[นโยบายความเป็นส่วนตัว](https://ur.io/privacy) ของ URnetwork" + "value" : "Pro" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Я погоджуюся з [Умовами надання послуг](https://ur.io/terms) та [Політикою конфіденційності](https://ur.io/privacy) URnetwork" + "value" : "Pro" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Tôi đồng ý với [Điều khoản và Dịch vụ](https://ur.io/terms) và [Chính sách Bảo mật](https://ur.io/privacy) của URnetwork" + "value" : "Pro" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "我同意 URnetwork 的[条款和服务](https://ur.io/terms)和[隐私政策](https://ur.io/privacy)" + "value" : "Pro" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "我同意 URnetwork 的[條款和服務](https://ur.io/terms)和[隱私政策](https://ur.io/privacy)" + "value" : "Pro" } } } }, - "If the problem persists, contact us at [support@ur.io](mailto:support@ur.io) or [join our Discord](https://discord.com/invite/RUNZXMwPRK) for direct support." : { + "Processing subscription balance..." : { + "comment" : "Used in Main/Connect/ConnectButton/StatusIndicator/ConnectStatusIndicator.swift.", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "إذا استمرت المشكلة، تواصل معنا عبر [support@ur.io](mailto:support@ur.io) أو [انضم إلى Discord الخاص بنا](https://discord.com/invite/RUNZXMwPRK) للحصول على دعم مباشر." + "value" : "جارٍ معالجة رصيد الاشتراك..." } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Pokud problém přetrvává, kontaktujte nás na [support@ur.io](mailto:support@ur.io) nebo [přidejte se na náš Discord](https://discord.com/invite/RUNZXMwPRK) pro přímou podporu." + "value" : "Zpracování zůstatku předplatného..." } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Wenn das Problem weiterhin besteht, kontaktieren Sie uns unter [support@ur.io](mailto:support@ur.io) oder [treten Sie unserem Discord bei](https://discord.com/invite/RUNZXMwPRK) für direkten Support." + "value" : "Abonnementguthaben wird verarbeitet..." } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Αν το πρόβλημα επιμένει, επικοινωνήστε μαζί μας στο [support@ur.io](mailto:support@ur.io) ή [μπείτε στο Discord μας](https://discord.com/invite/RUNZXMwPRK) για άμεση υποστήριξη." + "value" : "Επεξεργασία υπολοίπου συνδρομής..." } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "If the problem persists, contact us at [support@ur.io](mailto:support@ur.io) or [join our Discord](https://discord.com/invite/RUNZXMwPRK) for direct support." + "value" : "Processing subscription balance..." } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Si el problema persiste, contáctanos en [support@ur.io](mailto:support@ur.io) o [únete a nuestro Discord](https://discord.com/invite/RUNZXMwPRK) para soporte directo." + "value" : "Procesando el saldo de la suscripción..." } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Si el problema persiste, contáctanos en [support@ur.io](mailto:support@ur.io) o [únete a nuestro Discord](https://discord.com/invite/RUNZXMwPRK) para soporte directo." + "value" : "Procesando el saldo de la suscripción..." } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Si le problème persiste, contactez-nous à [support@ur.io](mailto:support@ur.io) ou [rejoignez notre Discord](https://discord.com/invite/RUNZXMwPRK) pour une assistance directe." + "value" : "Traitement du solde de l'abonnement..." } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "אם הבעיה נמשכת, פנו אלינו בכתובת [support@ur.io](mailto:support@ur.io) או [הצטרפו ל-Discord שלנו](https://discord.com/invite/RUNZXMwPRK) לקבלת תמיכה ישירה." + "value" : "מעבד את יתרת המנוי..." } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "यदि समस्या बनी रहती है, तो [support@ur.io](mailto:support@ur.io) पर हमसे संपर्क करें या सीधे सहायता के लिए [हमारे Discord से जुड़ें](https://discord.com/invite/RUNZXMwPRK)।" + "value" : "सदस्यता बैलेंस संसाधित किया जा रहा है..." } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Jika masalah berlanjut, hubungi kami di [support@ur.io](mailto:support@ur.io) atau [bergabung dengan Discord kami](https://discord.com/invite/RUNZXMwPRK) untuk dukungan langsung." + "value" : "Memproses saldo langganan..." } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Se il problema persiste, contattaci a [support@ur.io](mailto:support@ur.io) oppure [unisciti al nostro Discord](https://discord.com/invite/RUNZXMwPRK) per assistenza diretta." + "value" : "Elaborazione del saldo dell'abbonamento..." } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "問題が解決しない場合は、[support@ur.io](mailto:support@ur.io) までご連絡いただくか、直接サポートを受けるために[Discordに参加](https://discord.com/invite/RUNZXMwPRK)してください。" + "value" : "サブスクリプション残高を処理中..." } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "문제가 지속되면 [support@ur.io](mailto:support@ur.io)로 연락하시거나 직접 지원을 위해 [Discord에 가입](https://discord.com/invite/RUNZXMwPRK)해 주세요." + "value" : "구독 잔액 처리 중..." } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Als het probleem aanhoudt, neem contact met ons op via [support@ur.io](mailto:support@ur.io) of [word lid van onze Discord](https://discord.com/invite/RUNZXMwPRK) voor directe ondersteuning." + "value" : "Abonnementsaldo wordt verwerkt..." } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Jeśli problem nadal występuje, skontaktuj się z nami pod adresem [support@ur.io](mailto:support@ur.io) lub [dołącz do naszego Discorda](https://discord.com/invite/RUNZXMwPRK), aby uzyskać bezpośrednie wsparcie." + "value" : "Przetwarzanie salda subskrypcji..." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Se o problema persistir, entre em contato pelo [support@ur.io](mailto:support@ur.io) ou [entre no nosso Discord](https://discord.com/invite/RUNZXMwPRK) para suporte direto." + "value" : "Processando saldo da assinatura..." } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Se o problema persistir, contacte-nos através de [support@ur.io](mailto:support@ur.io) ou [junte-se ao nosso Discord](https://discord.com/invite/RUNZXMwPRK) para suporte direto." + "value" : "A processar saldo da subscrição..." } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Если проблема сохраняется, свяжитесь с нами по адресу [support@ur.io](mailto:support@ur.io) или [присоединяйтесь к нашему Discord](https://discord.com/invite/RUNZXMwPRK) для прямой поддержки." + "value" : "Обработка баланса подписки..." } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Om problemet kvarstår, kontakta oss på [support@ur.io](mailto:support@ur.io) eller [gå med i vår Discord](https://discord.com/invite/RUNZXMwPRK) för direkt support." + "value" : "Bearbetar prenumerationssaldo..." } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "หากปัญหายังคงอยู่ โปรดติดต่อเราได้ที่ [support@ur.io](mailto:support@ur.io) หรือ [เข้าร่วม Discord ของเรา](https://discord.com/invite/RUNZXMwPRK) เพื่อรับการสนับสนุนโดยตรง" + "value" : "กำลังประมวลผลยอดเงินสำหรับการสมัครสมาชิก..." } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Якщо проблема не зникає, зв’яжіться з нами на [support@ur.io](mailto:support@ur.io) або [приєднайтеся до нашого Discord](https://discord.com/invite/RUNZXMwPRK), щоб отримати пряму підтримку." + "value" : "Обробка балансу підписки..." } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Nếu vấn đề vẫn tiếp diễn, hãy liên hệ với chúng tôi tại [support@ur.io](mailto:support@ur.io) hoặc [tham gia Discord của chúng tôi](https://discord.com/invite/RUNZXMwPRK) để được hỗ trợ trực tiếp." + "value" : "Đang xử lý số dư đăng ký..." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "如果问题仍然存在,请通过 [support@ur.io](mailto:support@ur.io) 联系我们,或[加入我们的 Discord](https://discord.com/invite/RUNZXMwPRK) 获取直接支持。" + "value" : "正在处理订阅余额..." } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "若問題持續,請透過 [support@ur.io](mailto:support@ur.io) 與我們聯絡,或[加入我們的 Discord](https://discord.com/invite/RUNZXMwPRK) 以獲得直接支援。" + "value" : "正在處理訂閱餘額..." } } } }, - "Includes 2 week free trial" : { + "Profile" : { + "comment" : "Account Screen", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "يتضمن تجربة مجانية لمدة أسبوعين" + "value" : "الملف الشخصي" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Zahrnuje dvoutýdenní zkušební verzi zdarma" + "value" : "Profil" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Enthält zweiwöchige kostenlose Testphase" + "value" : "Profil" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Περιλαμβάνει δωρεάν δοκιμή δύο εβδομάδων" + "value" : "Προφίλ" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Includes 2 week free trial" + "value" : "Profile" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Incluye prueba gratuita de 2 semanas" + "value" : "Perfil" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Incluye prueba gratuita de 2 semanas" + "value" : "Perfil" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Perfil" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Comprend un essai gratuit de 2 semaines" + "value" : "Profil" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "כולל ניסיון חינם לשבועיים" + "value" : "פרופיל" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "2 सप्ताह की मुफ्त ट्रायल शामिल है" + "value" : "प्रोफ़ाइल" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Termasuk uji coba gratis 2 minggu" + "value" : "Profil" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Include prova gratuita di 2 settimane" + "value" : "Profilo" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "2週間の無料トライアル付き" + "value" : "プロフィール" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "2주 무료 체험 포함" + "value" : "프로필" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Inclusief 2 weken gratis proefperiode" + "value" : "Profiel" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Zawiera 2-tygodniowy bezpłatny okres próbny" + "value" : "Profil" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Perfil" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Inclui teste gratuito de 2 semanas" + "value" : "Perfil" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Inclui teste gratuito de 2 semanas" + "value" : "Perfil" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Включает двухнедельный бесплатный пробный период" + "value" : "Профиль" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Inkluderar 2 veckors gratis provperiod" + "value" : "Profil" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "รวมทดลองใช้งานฟรี 2 สัปดาห์" + "value" : "โปรไฟล์" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Включає двотижневий безкоштовний пробний період" + "value" : "Профіль" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Bao gồm dùng thử miễn phí 2 tuần" + "value" : "Hồ sơ" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "包含2周免费试用" + "value" : "个人资料" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "包括2星期免費試用" + "value" : "個人資料" } } } }, - "Insufficient balance" : { + "Promoted Locations" : { + "comment" : "A header for a section of the provider list that shows recommended locations.", + "isCommentAutoGenerated" : true, "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "رصيد غير كاف" + "value" : "المواقع المميزة" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Nedostatečný zůstatek" + "value" : "Propagované lokace" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Unzureichendes Guthaben" + "value" : "Beworbene Standorte" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Ανεπαρκές υπόλοιπο" + "value" : "Προωθούμενες τοποθεσίες" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Insufficient balance" + "value" : "Promoted Locations" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Saldo insuficiente" + "value" : "Ubicaciones promocionadas" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Saldo insuficiente" + "value" : "Ubicaciones promocionadas" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ubicaciones promocionadas" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Solde insuffisant" + "value" : "Emplacements promus" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "יתרה לא מספקת" + "value" : "מיקומים מומלצים" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "अपर्याप्त बैलेंस" + "value" : "प्रचारित स्थान" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Saldo tidak mencukupi" + "value" : "Lokasi Unggulan" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Saldo insufficiente" + "value" : "Posizioni in evidenza" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "残高不足" + "value" : "プロモーションロケーション" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "잔액 부족" + "value" : "추천 위치" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Onvoldoende Saldo" + "value" : "Uitgelichte locaties" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Niewystarczające środki" + "value" : "Promowane lokalizacje" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Locais promovidos" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Saldo insuficiente" + "value" : "Locais promovidos" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Saldo insuficiente" + "value" : "Locais promovidos" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Недостаточно средств" + "value" : "Продвигаемые локации" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Otillräckligt saldo" + "value" : "Utvalda platser" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "ยอดเงินไม่เพียงพอ" + "value" : "พื้นที่ที่ได้รับการโปรโมท" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Недостатньо коштів" + "value" : "Рекомендовані локації" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Không đủ số dư" + "value" : "Vị trí nổi bật" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "余额不足" + "value" : "推荐地点" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "餘額不足" + "value" : "推薦地點" } } } }, - "Invalid balance code" : { + "Provide mode" : { + "comment" : "Call to action", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "رمز الرصيد غير صالح" + "value" : "وضع التوفير" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Neplatný kód na zůstatek" + "value" : "Režim poskytování" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Ungültiger Guthabencode" + "value" : "Bereitstellungsmodus" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Μη έγκυρος κωδικός υπολοίπου" + "value" : "Λειτουργία παροχής" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Invalid balance code" + "value" : "Provide mode" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Código de saldo inválido" + "value" : "Modo de provisión" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Código de saldo inválido" + "value" : "Modo de provisión" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Modo de provisión" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Code de solde invalide" + "value" : "Mode de fourniture" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "קוד יתרה לא חוקי" + "value" : "מצב אספקה" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "अमान्य बैलेंस कोड" + "value" : "प्रदान मोड" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Kode saldo tidak valid" + "value" : "Mode penyediaan" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Codice saldo non valido" + "value" : "Modalità di fornitura" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "無効な残高コード" + "value" : "提供モード" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "잘못된 잔액 코드" + "value" : "제공 모드" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Ongeldige saldo code" + "value" : "Leveringsmodus" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Nieprawidłowy kod salda" + "value" : "Tryb udostępniania" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Modo de fornecimento" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Código de saldo inválido" + "value" : "Modo de fornecimento" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Código de saldo inválido" + "value" : "Modo de fornecimento" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Недействительный код баланса" + "value" : "Режим предоставления" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Ogiltig saldo-kod" + "value" : "Tillhandahållandeläge" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "รหัสยอดเงินไม่ถูกต้อง" + "value" : "โหมดการให้บริการ" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Недійсний код балансу" + "value" : "Режим надання" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Mã số dư không hợp lệ" + "value" : "Chế độ cung cấp" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "余额码无效" + "value" : "提供模式" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "餘額碼無效" + "value" : "提供模式" } } } }, - "Invalid referral code. Please try again." : { + "Provider contracts" : { + "comment" : "A label for the provider contracts screen.", + "isCommentAutoGenerated" : true, "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "رمز الإحالة غير صالح. يرجى المحاولة مرة أخرى." + "value" : "عقود المزود" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Neplatný referální kód. Prosím, zkuste to znovu." + "value" : "Kontrakty poskytovatele" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Ungültiger Empfehlungscode. Bitte versuchen Sie es erneut." + "value" : "Anbieter-Verträge" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Μη έγκυρος κωδικός παραπομπής. Παρακαλώ δοκιμάστε ξανά." + "value" : "Συμβόλαια παρόχου" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Invalid referral code. Please try again." + "value" : "Provider contracts" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Código de referencia no válido. Por favor, inténtalo de nuevo." + "value" : "Contratos de proveedor" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Código de referencia no válido. Por favor, inténtalo de nuevo." + "value" : "Contratos de proveedor" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Contratos de proveedor" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Code de parrainage invalide. Veuillez réessayer." + "value" : "Contrats fournisseur" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "קוד הפניה לא חוקי. אנא נסה שוב." + "value" : "חוזי ספק" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "अमान्य रेफ़रल कोड। कृपया पुन: प्रयास करें।" + "value" : "प्रदाता कॉन्ट्रैक्ट" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Kode referensi tidak valid. Silakan coba lagi." + "value" : "Kontrak penyedia" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Codice di riferimento non valido. Per favore riprova." + "value" : "Contratti provider" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "無効な紹介コードです。もう一度お試しください。" + "value" : "プロバイダー契約" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "유효하지 않은 추천 코드입니다. 다시 시도해 주세요." + "value" : "제공자 계약" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Ongeldige referentiecode. Probeer het opnieuw." + "value" : "Providercontracten" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Nieprawidłowy kod polecający. Spróbuj ponownie." + "value" : "Kontrakty dostawcy" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Contratos do fornecedor" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Código de indicação inválido. Por favor, tente novamente." + "value" : "Contratos do provedor" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Código de referência inválido. Por favor, tente novamente." + "value" : "Contratos do fornecedor" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Недействительный реферальный код. Пожалуйста, попробуйте еще раз." + "value" : "Контракты провайдера" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Ogiltig referenskod. Försök igen." + "value" : "Leverantörskontrakt" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "รหัสผู้อ้างอิงไม่ถูกต้อง กรุณาลองใหม่อีกครั้ง" + "value" : "สัญญาผู้ให้บริการ" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Недійсний реферальний код. Будь ласка, спробуйте ще раз." + "value" : "Контракти провайдера" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Mã giới thiệu không hợp lệ. Vui lòng thử lại." + "value" : "Hợp đồng nhà cung cấp" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "推荐码无效。请再试一次。" + "value" : "提供商合约" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "推薦碼無效。請再試一次。" + "value" : "供應商合約" } } } }, - "IPv4" : { - "comment" : "Label for a list of IPv4 addresses.", - "isCommentAutoGenerated" : true - }, - "IPv6" : { - "comment" : "Label for the IPv6 list of DNS servers.", - "isCommentAutoGenerated" : true - }, - "It's nice to see you again" : { + "Provider statistics" : { + "comment" : "Title of a section that shows statistics about the provider.", + "isCommentAutoGenerated" : true, "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "من الجميل رؤيتك مرة أخرى" + "value" : "إحصائيات المزود" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Rád vás opět vidím" + "value" : "Statistiky poskytovatele" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Es ist schön, dich wiederzusehen" + "value" : "Anbieter-Statistiken" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Χαίρομαι που σε ξαναβλέπω" + "value" : "Στατιστικά παρόχου" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "It's nice to see you again" + "value" : "Provider statistics" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Es agradable verte de nuevo" + "value" : "Estadísticas del proveedor" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Es agradable verte de nuevo" + "value" : "Estadísticas del proveedor" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Estadísticas del proveedor" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "C'est agréable de te revoir" + "value" : "Statistiques fournisseur" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "נחמד לראות אותך שוב" + "value" : "סטטיסטיקת ספק" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "आपसे फिर से मिलकर अच्छा लगा" + "value" : "प्रदाता आँकड़े" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Senang melihatmu lagi" + "value" : "Statistik penyedia" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "È bello rivederti" + "value" : "Statistiche provider" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "また会えて嬉しいです" + "value" : "プロバイダー統計" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "다시 만나서 반가워요" + "value" : "제공자 통계" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Fijn je weer te zien" + "value" : "Providerstatistieken" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Miło cię znowu widzieć" + "value" : "Statystyki dostawcy" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Estatísticas do fornecedor" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "É bom ver você de novo" + "value" : "Estatísticas do provedor" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "É bom ver você de novo" + "value" : "Estatísticas do fornecedor" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Приятно снова тебя видеть" + "value" : "Статистика провайдера" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Trevligt att se dig igen" + "value" : "Leverantörsstatistik" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "ดีใจที่ได้เจอคุณอีกครั้ง" + "value" : "สถิติผู้ให้บริการ" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Приємно знову тебе бачити" + "value" : "Статистика провайдера" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Rất vui được gặp lại bạn" + "value" : "Thống kê nhà cung cấp" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "很高兴再次见到你" + "value" : "提供商统计" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "很高興再次見到你" + "value" : "供應商統計" } } } }, - "Join the community on [Discord](https://discord.com/invite/RUNZXMwPRK)" : { + "Providing" : { + "comment" : "Used in Main/Account/Wallets/WalletsView.swift.", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "انضم إلى المجتمع على [Discord](https://discord.com/invite/RUNZXMwPRK)" + "value" : "جاري التوفير" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Připojte se ke komunitě na [Discord](https://discord.com/invite/RUNZXMwPRK)" + "value" : "Poskytování" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Tritt der Community auf [Discord](https://discord.com/invite/RUNZXMwPRK) bei" + "value" : "Bereitstellung" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Συμμετοχή στην κοινότητα στο [Discord](https://discord.com/invite/RUNZXMwPRK)" + "value" : "Παροχή" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Join the community on [Discord](https://discord.com/invite/RUNZXMwPRK)" + "value" : "Providing" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Únete a la comunidad en [Discord](https://discord.com/invite/RUNZXMwPRK)" + "value" : "Proporcionando" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Únete a la comunidad en [Discord](https://discord.com/invite/RUNZXMwPRK)" + "value" : "Proporcionando" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Rejoignez la communauté sur [Discord](https://discord.com/invite/RUNZXMwPRK)" + "value" : "Fourniture" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "הצטרף לקהילה ב-[Discord](https://discord.com/invite/RUNZXMwPRK)" + "value" : "מספק" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "[Discord](https://discord.com/invite/RUNZXMwPRK) पर समुदाय से जुड़ें" + "value" : "प्रदान कर रहा है" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Bergabung dengan komunitas di [Discord](https://discord.com/invite/RUNZXMwPRK)" + "value" : "Menyediakan" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Unisciti alla community su [Discord](https://discord.com/invite/RUNZXMwPRK)" + "value" : "Fornendo" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "[Discord](https://discord.com/invite/RUNZXMwPRK)でコミュニティに参加" + "value" : "提供中" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "[Discord](https://discord.com/invite/RUNZXMwPRK)에서 커뮤니티에 참여하세요" + "value" : "제공 중" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Word lid van de community [Discord](https://discord.com/invite/RUNZXMwPRK)" + "value" : "Verstrekken" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Dołącz do społeczności na [Discord](https://discord.com/invite/RUNZXMwPRK)" + "value" : "Udostępnianie" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Junte-se à comunidade no [Discord](https://discord.com/invite/RUNZXMwPRK)" + "value" : "Fornecendo" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Junte-se à comunidade no [Discord](https://discord.com/invite/RUNZXMwPRK)" + "value" : "A fornecer" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Присоединяйтесь к сообществу в [Discord](https://discord.com/invite/RUNZXMwPRK)" + "value" : "Предоставление" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Gå med i gemenskapen på [Discord](https://discord.com/invite/RUNZXMwPRK)" + "value" : "Tillhandahåller" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "เข้าร่วมชุมชนบน [Discord](https://discord.com/invite/RUNZXMwPRK)" + "value" : "กำลังให้บริการ" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Приєднуйтесь до спільноти у [Discord](https://discord.com/invite/RUNZXMwPRK)" + "value" : "Надання" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Tham gia cộng đồng trên [Discord](https://discord.com/invite/RUNZXMwPRK)" + "value" : "Đang cung cấp" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "在 [Discord](https://discord.com/invite/RUNZXMwPRK) 上加入社区" + "value" : "提供中" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "在 [Discord](https://discord.com/invite/RUNZXMwPRK) 上加入社區" + "value" : "提供中" } } } }, - "Join the movement" : { + "Providing is disabled" : { + "comment" : "A message displayed when the user is not providing.", + "isCommentAutoGenerated" : true, "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "انضم إلى الحركة" + "value" : "التوفير معطّل" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Připojte se k hnutí" + "value" : "Poskytování je vypnuté" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Mach mit bei der Bewegung" + "value" : "Bereitstellung ist deaktiviert" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Γίνετε μέλος του κινήματος" + "value" : "Η παροχή είναι απενεργοποιημένη" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Join the movement" + "value" : "Providing is disabled" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Únete al movimiento" + "value" : "La provisión está desactivada" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Únete al movimiento" + "value" : "La provisión está desactivada" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "La provisión está desactivada" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Rejoignez le mouvement" + "value" : "La fourniture est désactivée" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "הצטרף לתנועה" + "value" : "האספקה מושבתת" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "आंदोलन में शामिल हों" + "value" : "प्रदान करना बंद है" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Bergabunglah dengan gerakan" + "value" : "Penyediaan dinonaktifkan" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Unisciti al movimento" + "value" : "La fornitura è disattivata" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "ムーブメントに参加する" + "value" : "提供は無効です" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "운동에 참여하세요" + "value" : "제공이 비활성화되어 있습니다" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Doe mee aan de beweging" + "value" : "Leveren is uitgeschakeld" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Dołącz do ruchu" + "value" : "Udostępnianie jest wyłączone" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "O fornecimento está desativado" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Junte-se ao movimento" + "value" : "O fornecimento está desativado" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Junte-se ao movimento" + "value" : "O fornecimento está desativado" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Присоединяйтесь к движению" + "value" : "Предоставление отключено" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Gå med i rörelsen" + "value" : "Tillhandahållande är avstängt" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "เข้าร่วมการเคลื่อนไหว" + "value" : "การให้บริการถูกปิดอยู่" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Приєднуйтесь до руху" + "value" : "Надання вимкнено" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Tham gia phong trào" + "value" : "Cung cấp đang tắt" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "加入运动" + "value" : "提供功能已关闭" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "加入運動" + "value" : "提供功能已關閉" } } } }, - "Join URnetwork" : { - "comment" : "URnetwork is the project name and should not be translated", + "Providing is off by default. Choose when your device provides below." : { + "comment" : "A description of the user's ability to provide bandwidth.", + "isCommentAutoGenerated" : true, "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "انضم إلى URnetwork" + "value" : "ضبط الإعداد على \"دائمًا\" لزيادة البيانات المجانية بشكل أسرع." } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Připojte se k URnetwork" + "value" : "Nastavením na „Vždy“ můžete rychleji získat bezplatná data." } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork beitreten" + "value" : "Du kannst die Einstellung auf „Immer“ setzen, um die kostenlosen Daten schneller zu füllen." } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Συμμετοχή στο URnetwork" + "value" : "Μπορείτε να ρυθμίσετε την επιλογή σε «Πάντα» για να γεμίσετε τα δωρεάν δεδομένα πιο γρήγορα." } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Join URnetwork" + "value" : "Providing is off by default. Choose when your device provides below." } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Únete a URnetwork" + "value" : "Puedes ajustar la configuración a \"Siempre\" para llenar los datos gratis más rápido." } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Únete a URnetwork" + "value" : "La provisión está desactivada por defecto. Elige a continuación cuándo proporciona datos tu dispositivo." + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Puedes ajustar la configuración a \"Siempre\" para llenar los datos gratis más rápido." } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Rejoindre URnetwork" + "value" : "Vous pouvez régler le paramètre sur « Toujours » pour remplir les données gratuites plus rapidement." } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "הצטרף ל-URnetwork" + "value" : "האספקה כבויה כברירת מחדל. בחר למטה מתי המכשיר שלך מספק." } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork में शामिल हों" + "value" : "आप सेटिंग को \"हमेशा\" पर समायोजित कर सकते हैं ताकि मुफ्त डेटा तेजी से भर सके।" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Bergabung dengan URnetwork" + "value" : "Penyediaan nonaktif secara default. Pilih di bawah kapan perangkat Anda menyediakan." } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Unisciti a URnetwork" + "value" : "La fornitura è disattivata per impostazione predefinita. Scegli qui sotto quando il tuo dispositivo fornisce." } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "URnetworkに参加" + "value" : "設定を「常に」に変更すると、無料データがより早く増えます。" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork에 가입" + "value" : "설정을 '항상'으로 조정하면 무료 데이터를 더 빨리 채울 수 있습니다." } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Word lid van URnetwork" + "value" : "Je kunt de instelling op 'Altijd' zetten om sneller gratis data te vullen." } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Dołącz do URnetwork" + "value" : "Możesz ustawić opcję na „Zawsze”, aby szybciej zapełnić darmowe dane." + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pode ajustar a definição para \"Sempre\" para preencher os dados gratuitos mais rapidamente." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Junte-se ao URnetwork" + "value" : "Você pode ajustar a configuração para \"Sempre\" para preencher os dados gratuitos mais rápido." } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Junte-se ao URnetwork" + "value" : "O fornecimento está desativado por defeito. Escolha abaixo quando o seu dispositivo deve fornecer." } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Присоединиться к URnetwork" + "value" : "Вы можете изменить настройку на «Всегда», чтобы быстрее получать бесплатные данные." } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Gå med i URnetwork" + "value" : "Tillhandahållande är av som standard. Välj nedan när din enhet ska tillhandahålla." } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "เข้าร่วม URnetwork" + "value" : "คุณสามารถปรับการตั้งค่าเป็น \"เสมอ\" เพื่อเติมข้อมูลฟรีได้เร็วขึ้น" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Приєднатися до URnetwork" + "value" : "Надання за замовчуванням вимкнено. Нижче виберіть, коли ваш пристрій надає дані." } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Tham gia URnetwork" + "value" : "Cung cấp được tắt theo mặc định. Chọn bên dưới khi nào thiết bị của bạn cung cấp." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "加入 URnetwork" + "value" : "您可以将设置调整为“始终”,以更快地填充免费数据" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "加入 URnetwork" + "value" : "提供功能預設關閉。請在下方選擇裝置何時提供。" } } } }, - "key" : { - - }, - "Kill switch" : { + "Providing is optional. When you turn it on, the app can share your connection with the network while it runs in the background on Wi-Fi." : { + "comment" : "A description of the benefits of providing.", + "isCommentAutoGenerated" : true, "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "مفتاح القفل" + "value" : "التوفير اختياري. عند تشغيله، يمكن للتطبيق مشاركة اتصالك مع الشبكة أثناء عمله في الخلفية على الواي فاي." } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Kill switch" + "value" : "Poskytování je volitelné. Když ho zapnete, může aplikace na pozadí sdílet vaše připojení se sítí, pokud jste na Wi-Fi." } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Kill switch" + "value" : "Die Bereitstellung ist optional. Wenn du sie aktivierst, kann die App deine Verbindung mit dem Netzwerk teilen, während sie im Hintergrund im WLAN läuft." } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Kill switch" + "value" : "Η παροχή είναι προαιρετική. Όταν την ενεργοποιήσετε, η εφαρμογή μπορεί να μοιράζεται τη σύνδεσή σας με το δίκτυο όσο εκτελείται στο παρασκήνιο σε Wi-Fi." } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Kill switch" + "value" : "Providing is optional. When you turn it on, the app can share your connection with the network while it runs in the background on Wi-Fi." } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Kill switch" + "value" : "La provisión es opcional. Cuando la activas, la app puede compartir tu conexión con la red mientras se ejecuta en segundo plano con Wi-Fi." } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Kill switch" + "value" : "La provisión es opcional. Cuando la activas, la app puede compartir tu conexión con la red mientras se ejecuta en segundo plano con Wi-Fi." + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "La provisión es opcional. Cuando la activas, la app puede compartir tu conexión con la red mientras se ejecuta en segundo plano con Wi-Fi." } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Kill switch" + "value" : "La fourniture est facultative. Lorsque vous l'activez, l'application peut partager votre connexion avec le réseau pendant qu'elle fonctionne en arrière-plan en Wi-Fi." } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "קיל סוויץ'" + "value" : "האספקה היא אופציונלית. כשתפעיל אותה, האפליקציה תוכל לשתף את החיבור שלך עם הרשת בזמן שהיא פועלת ברקע ב-Wi-Fi." } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "किल स्विच" + "value" : "प्रदान करना वैकल्पिक है। इसे चालू करने पर, ऐप वाई-फ़ाई पर बैकग्राउंड में चलते हुए आपके कनेक्शन को नेटवर्क के साथ साझा कर सकता है।" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Kill switch" + "value" : "Penyediaan bersifat opsional. Saat Anda mengaktifkannya, aplikasi dapat berbagi koneksi Anda dengan jaringan selama berjalan di latar belakang dengan Wi-Fi." } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Kill switch" + "value" : "La fornitura è facoltativa. Quando la attivi, l'app può condividere la tua connessione con la rete mentre resta in background su Wi-Fi." } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "キルスイッチ" + "value" : "提供は任意です。オンにすると、Wi-Fi接続時にバックグラウンドで動作しながら、アプリがあなたの接続をネットワークと共有できます。" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "킬 스위치" + "value" : "제공은 선택 사항입니다. 켜면 Wi-Fi에서 백그라운드로 실행되는 동안 앱이 연결을 네트워크와 공유할 수 있습니다." } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Kill switch" + "value" : "Leveren is optioneel. Als je het aanzet, kan de app je verbinding met het netwerk delen terwijl hij op de achtergrond draait op Wi-Fi." } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Kill switch" + "value" : "Udostępnianie jest opcjonalne. Po włączeniu aplikacja może dzielić się Twoim połączeniem z siecią, działając w tle na Wi-Fi." + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "O fornecimento é opcional. Quando o ativa, a aplicação pode partilhar a sua conexão com a rede enquanto funciona em segundo plano em Wi-Fi." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Kill switch" + "value" : "O fornecimento é opcional. Quando você ativa, o app pode compartilhar sua conexão com a rede enquanto funciona em segundo plano no Wi-Fi." } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Kill switch" + "value" : "O fornecimento é opcional. Quando o ativa, a aplicação pode partilhar a sua conexão com a rede enquanto funciona em segundo plano em Wi-Fi." } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Kill switch" + "value" : "Предоставление необязательно. Если включить его, приложение сможет делиться вашим подключением с сетью, работая в фоновом режиме по Wi-Fi." } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Kill switch" + "value" : "Tillhandahållande är valfritt. När du slår på det kan appen dela din anslutning med nätverket medan den körs i bakgrunden på Wi-Fi." } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "สวิตช์ตัดการเชื่อมต่อ" + "value" : "การให้บริการเป็นทางเลือก เมื่อคุณเปิดใช้งาน แอปจะแบ่งปันการเชื่อมต่อของคุณกับเครือข่ายได้ขณะทำงานเบื้องหลังบน Wi-Fi" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Kill switch" + "value" : "Надання необов'язкове. Якщо ввімкнути його, додаток зможе ділитися вашим підключенням з мережею, працюючи у фоновому режимі через Wi-Fi." } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Kill switch" + "value" : "Cung cấp là tùy chọn. Khi bạn bật, ứng dụng có thể chia sẻ kết nối của bạn với mạng trong lúc chạy nền trên Wi-Fi." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "断开开关 (Kill switch)" + "value" : "提供功能是可选的。开启后,应用在使用 Wi-Fi 并在后台运行时,可与网络共享你的连接。" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "斷線開關 (Kill switch)" + "value" : "提供功能是可選的。開啟後,應用程式在使用 Wi-Fi 並於背景運行時,可與網絡分享你的連接。" } } } }, - "Launch" : { + "Quit URnetwork" : { + "comment" : "Used in NetworkApp.swift.", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "إطلاق" + "value" : "إغلاق URnetwork" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Spustit" + "value" : "Opustit URnetwork" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Starten" + "value" : "URnetwork beenden" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Εκκίνηση" + "value" : "Έξοδος από το URnetwork" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Launch" + "value" : "Quit URnetwork" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Iniciar" + "value" : "Salir de URnetwork" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Iniciar" + "value" : "Salir de URnetwork" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Lancer" + "value" : "Quitter URnetwork" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "הפעל" + "value" : "צא מ-URnetwork" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "लॉन्च करें" + "value" : "URnetwork से बाहर निकलें" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Luncurkan" + "value" : "Keluar dari URnetwork" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Avvia" + "value" : "Chiudi URnetwork" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "起動" + "value" : "URnetworkを終了" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "실행" + "value" : "URnetwork 종료" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Starten" + "value" : "Stop URnetwork" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Uruchom" + "value" : "Zakończ URnetwork" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Lançar" + "value" : "Sair do URnetwork" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Lançar" + "value" : "Sair do URnetwork" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Запустить" + "value" : "Выйти из URnetwork" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Starta" + "value" : "Avsluta URnetwork" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "เปิดใช้งาน" + "value" : "ออกจาก URnetwork" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Запустити" + "value" : "Вийти з URnetwork" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Khởi chạy" + "value" : "Thoát URnetwork" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "启动" + "value" : "退出 URnetwork" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "啟動" + "value" : "退出 URnetwork" } } } }, - "Launch URnetwork on system startup" : { + "Races a local resolver while the tunnel starts. When off, DNS only resolves through the tunnel." : { + "comment" : "A description of the local DNS fallback setting.", + "isCommentAutoGenerated" : true, "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "تشغيل URnetwork عند بدء النظام" + "value" : "يستعلم من محلّل محلي بالتوازي أثناء بدء النفق. عند الإيقاف، يتم حل DNS عبر النفق فقط." } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Spuštění URnetwork při spuštění systému" + "value" : "Během startu tunelu se souběžně dotazuje místního resolveru. Když je vypnuto, DNS se překládá jen přes tunel." } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork beim Systemstart starten" + "value" : "Fragt beim Start des Tunnels parallel einen lokalen Resolver ab. Wenn aus, wird DNS nur über den Tunnel aufgelöst." } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Εκκίνηση του URnetwork κατά την εκκίνηση του συστήματος" + "value" : "Εκτελεί παράλληλα έναν τοπικό επιλυτή όσο ξεκινά το τούνελ. Όταν είναι ανενεργό, το DNS επιλύεται μόνο μέσω του τούνελ." } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Launch URnetwork on system startup" + "value" : "Races a local resolver while the tunnel starts. When off, DNS only resolves through the tunnel." } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Iniciar URnetwork al arrancar el sistema" + "value" : "Consulta en paralelo un resolutor local mientras se inicia el túnel. Si está desactivado, el DNS solo se resuelve a través del túnel." } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Iniciar URnetwork al arrancar el sistema" + "value" : "Consulta en paralelo un resolutor local mientras se inicia el túnel. Si está desactivado, el DNS solo se resuelve a través del túnel." + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Consulta en paralelo un resolutor local mientras se inicia el túnel. Si está desactivado, el DNS solo se resuelve a través del túnel." } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Lancer URnetwork au démarrage du système" + "value" : "Interroge en parallèle un résolveur local pendant le démarrage du tunnel. Désactivé, le DNS ne se résout qu'à travers le tunnel." } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "הפעל את URnetwork בעת הפעלת המערכת" + "value" : "מריץ פותר מקומי במקביל בזמן שהמנהרה עולה. כשהאפשרות כבויה, DNS נפתר רק דרך המנהרה." } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "सिस्टम स्टार्टअप पर URnetwork लॉन्च करें" + "value" : "टनल शुरू होते समय स्थानीय रिज़ॉल्वर को साथ-साथ आज़माता है। बंद होने पर DNS केवल टनल से ही रिज़ॉल्व होता है।" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Jalankan URnetwork saat sistem startup" + "value" : "Menjalankan resolver lokal secara bersamaan saat terowongan dimulai. Jika nonaktif, DNS hanya diselesaikan melalui terowongan." } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Avvia URnetwork all'avvio del sistema" + "value" : "Interroga in parallelo un resolver locale mentre il tunnel si avvia. Se disattivato, il DNS si risolve solo attraverso il tunnel." } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "システム起動時にURnetworkを起動" + "value" : "トンネルの起動中はローカルリゾルバーにも並行して問い合わせます。オフの場合、DNSはトンネル経由でのみ解決されます。" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "시스템 시작 시 URnetwork 실행" + "value" : "터널이 시작되는 동안 로컬 리졸버에도 동시에 질의합니다. 끄면 DNS는 터널을 통해서만 확인됩니다." } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Start URnetwork automatisch bij het opstarten van het systeem" + "value" : "Bevraagt tijdens het opstarten van de tunnel tegelijk een lokale resolver. Als dit uitstaat, wordt DNS alleen via de tunnel opgelost." } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Uruchom URnetwork przy starcie systemu" + "value" : "Podczas uruchamiania tunelu równolegle odpytuje lokalny resolver. Po wyłączeniu DNS jest rozwiązywany tylko przez tunel." + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Consulta um resolvedor local em paralelo enquanto o túnel arranca. Quando desativado, o DNS só é resolvido através do túnel." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Iniciar URnetwork na inicialização do sistema" + "value" : "Consulta um resolvedor local em paralelo enquanto o túnel inicia. Quando desativado, o DNS só é resolvido através do túnel." } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Iniciar URnetwork no arranque do sistema" + "value" : "Consulta um resolvedor local em paralelo enquanto o túnel arranca. Quando desativado, o DNS só é resolvido através do túnel." } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Запускать URnetwork при запуске системы" + "value" : "Пока запускается туннель, параллельно опрашивает локальный резолвер и берёт первый ответ. Если выключено, DNS разрешается только через туннель." } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Starta URnetwork vid systemstart" + "value" : "Kör en lokal resolver parallellt medan tunneln startar. När det är av sker DNS-uppslag endast genom tunneln." } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "เปิด URnetwork เมื่อเริ่มต้นระบบ" + "value" : "ใช้รีโซลเวอร์ภายในเครื่องแข่งกันไปด้วยขณะที่อุโมงค์กำลังเริ่มทำงาน เมื่อปิดอยู่ DNS จะทำงานผ่านอุโมงค์เท่านั้น" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Запускати URnetwork під час запуску системи" + "value" : "Поки запускається тунель, паралельно опитує локальний резолвер і бере першу відповідь. Якщо вимкнено, DNS розв'язується лише через тунель." } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Khởi chạy URnetwork khi khởi động hệ thống" + "value" : "Chạy song song một bộ phân giải cục bộ trong khi đường hầm khởi động. Khi tắt, DNS chỉ phân giải qua đường hầm." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "系统启动时启动 URnetwork" + "value" : "隧道启动时并行查询本地解析器,取最先返回的结果。关闭后,DNS 仅通过隧道解析。" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "系統啟動時啟動 URnetwork" + "value" : "隧道啟動時並行查詢本地解析器,取最先返回的結果。關閉後,DNS 只會透過隧道解析。" } } } }, - "Leaderboard" : { + "Ready to connect" : { + "comment" : "Call to action", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "لوحة المتصدرين" + "value" : "جاهز للاتصال" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Žebříček" + "value" : "Připraveno k připojení" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Bestenliste" + "value" : "Bereit zur Verbindung" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Πίνακας κατάταξης" + "value" : "Έτοιμο για σύνδεση" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Leaderboard" + "value" : "Ready to connect" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Tabla de clasificación" + "value" : "Listo para conectar" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Tabla de posiciones" + "value" : "Listo para conectar" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Listo para conectar" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Classement" + "value" : "Prêt à connecter" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "טבלת הישגים" + "value" : "מוכן להתחבר" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "लीडरबोर्ड" + "value" : "कनेक्ट करने के लिए तैयार" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Papan Peringkat" + "value" : "Siap terhubung" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Classifica" + "value" : "Pronto per connettersi" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "リーダーボード" + "value" : "接続準備完了" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "리더보드" + "value" : "연결 준비 완료" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Ranglijst" + "value" : "Klaar om te verbinden" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Tablica wyników" + "value" : "Gotowy do połączenia" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pronto para conectar" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Placar" + "value" : "Pronto para conectar" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Tabela de classificação" + "value" : "Pronto para conectar" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Таблица лидеров" + "value" : "Готово к подключению" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Topplista" + "value" : "Redo att ansluta" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "ลีดเดอร์บอร์ด" + "value" : "พร้อมเชื่อมต่อ" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Таблиця лідерів" + "value" : "Готовий до підключення" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Bảng xếp hạng" + "value" : "Sẵn sàng kết nối" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "排行榜" + "value" : "准备连接" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "排行榜" + "value" : "準備連接" } } } }, - "Learn more about the multiplier" : { + "Receive connection notifications" : { + "comment" : "Settings", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "تعرف على المزيد حول المُضاعف" + "value" : "تلقي إشعارات الاتصال" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Zjistit víc o násobiteli" + "value" : "Přijímání oznámení o připojení" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Mehr über den Multiplikator erfahren" + "value" : "Verbindungsbenachrichtigungen erhalten" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Μάθετε περισσότερα για τον πολλαπλασιαστή" + "value" : "Λήψη ειδοποιήσεων σύνδεσης" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Learn more about the multiplier" + "value" : "Receive connection notifications" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Más información sobre el multiplicador" + "value" : "Recibir notificaciones de conexión" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Más información sobre el multiplicador" + "value" : "Recibir notificaciones de conexión" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Recibir notificaciones de conexión" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "En savoir plus sur le multiplicateur" + "value" : "Recevoir des notifications de connexion" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "למידע נוסף על המכפיל" + "value" : "קבל התראות חיבור" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "गुणक के बारे में और जानें" + "value" : "कनेक्शन सूचनाएं प्राप्त करें" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Pelajari lebih lanjut tentang pengganda" + "value" : "Terima notifikasi koneksi" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Scopri di più sul moltiplicatore" + "value" : "Ricevi notifiche di connessione" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "倍率の詳細" + "value" : "接続通知を受け取る" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "승수에 대해 자세히 알아보기" + "value" : "연결 알림 받기" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Meer informatie over de vermenigvuldiger" + "value" : "Ontvang verbindingsmeldingen" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Dowiedz się więcej o mnożniku" + "value" : "Otrzymuj powiadomienia o połączeniach" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Receber notificações de conexão" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Saiba mais sobre o multiplicador" + "value" : "Receber notificações de conexão" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Mais informações sobre o multiplicador" + "value" : "Receber notificações de conexão" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Подробнее о множителе" + "value" : "Получать уведомления о подключении" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Läs mer om multiplikatorn" + "value" : "Ta emot anslutningsaviseringar" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "เรียนรู้เพิ่มเติมเกี่ยวกับตัวคูณ" + "value" : "รับแจ้งเตือนการเชื่อมต่อ" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Докладніше про множник" + "value" : "Отримувати сповіщення про підключення" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Tìm hiểu thêm về hệ số nhân" + "value" : "Nhận thông báo kết nối" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "了解有关乘数的更多信息" + "value" : "接收连接通知" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "了解更多關於乘數的資訊" + "value" : "接收連接通知" } } } }, - "Loading..." : { + "Reconnect" : { + "comment" : "Call to action", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "جارٍ التحميل..." + "value" : "إعادة الاتصال" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Načítání..." + "value" : "Znovu připojit" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Laden..." + "value" : "Erneut verbinden" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Φόρτωση..." + "value" : "Επανασύνδεση" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Loading..." + "value" : "Reconnect" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Cargando..." + "value" : "Reconectar" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Cargando..." + "value" : "Reconectar" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Reconectar" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Chargement..." + "value" : "Reconnecter" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "טוען..." + "value" : "התחבר מחדש" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "लोड हो रहा है..." + "value" : "पुनः कनेक्ट करें" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Memuat..." + "value" : "Sambung ulang" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Caricamento..." + "value" : "Riconnetti" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "読み込み中..." + "value" : "再接続する" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "로딩 중..." + "value" : "다시 연결" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Laden..." + "value" : "Opnieuw verbinden" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Ładowanie..." + "value" : "Połącz ponownie" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Reconectar" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Carregando..." + "value" : "Reconectar" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Carregando..." + "value" : "Reconectar" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Загрузка..." + "value" : "Переподключить" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Laddar..." + "value" : "Återanslut" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "กำลังโหลด..." + "value" : "เชื่อมต่อใหม่" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Завантаження..." + "value" : "Перепідключитися" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Đang tải..." + "value" : "Kết nối lại" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "加载中..." + "value" : "重新连接" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "載入中..." + "value" : "重新連接" } } } }, - "local" : { - "comment" : "Toggle label for the local DNS over HTTPS switch.", - "isCommentAutoGenerated" : true - }, - "Local" : { - "comment" : "Label for a chart showing local traffic statistics.", - "isCommentAutoGenerated" : true - }, - "Local DNS" : { - "comment" : "A label displayed in the custom dns settings section.", - "isCommentAutoGenerated" : true - }, - "Local DNS fallback" : { - "comment" : "A label displayed in the custom dns settings section.", - "isCommentAutoGenerated" : true - }, - "Local DNS servers" : { - "comment" : "A label displayed above a list of local DNS servers.", - "isCommentAutoGenerated" : true - }, - "Local DoH URLs" : { - "comment" : "A heading for a list of local DoH URLs.", - "isCommentAutoGenerated" : true - }, - "Local statistics" : { - "comment" : "Title of a card that shows the local traffic statistics.", - "isCommentAutoGenerated" : true - }, - "Log in with Auth Code" : { + "Redeem" : { + "comment" : "Guidance for someone who has no code yet. Deliberately PLAIN TEXT with no link and no button: app store rules forbid steering a user to an external purchase, but nothing stops us saying where codes come from. Without this, the sheet asks for a code and gives no way to obtain one.", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "تسجيل الدخول برمز المصادقة" + "value" : "استرداد" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Přihlásit se ověřovacím kódem" + "value" : "Uplatnit" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Mit Auth-Code anmelden" + "value" : "Einlösen" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Σύνδεση με κωδικό ταυτοποίησης" + "value" : "Εξαργύρωση" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Log in with Auth Code" + "value" : "Redeem" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Iniciar sesión con código de autenticación" + "value" : "Canjear" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Iniciar sesión con código de autenticación" + "value" : "Canjear" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Canjear" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Se connecter avec le code d’authentification" + "value" : "Utiliser" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "התחבר עם קוד אימות" + "value" : "ממש" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "ऑथ कोड से लॉगिन करें" + "value" : "रिडीम करें" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Masuk dengan kode autentikasi" + "value" : "Tukarkan" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Accedi con codice di autenticazione" + "value" : "Riscatta" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "認証コードでログイン" + "value" : "引き換える" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "인증 코드로 로그인" + "value" : "사용하기" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Inloggen met auth-code" + "value" : "Inwisselen" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Zaloguj się kodem autoryzacyjnym" + "value" : "Zrealizuj" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Resgatar" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Entrar com código de autenticação" + "value" : "Resgatar" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Entrar com código de autenticação" + "value" : "Resgatar" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Войти с кодом авторизации" + "value" : "Активировать" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Logga in med autentiseringskod" + "value" : "Lös in" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "เข้าสู่ระบบด้วยรหัสยืนยัน" + "value" : "แลก" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Увійти за кодом авторизації" + "value" : "Активувати" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Đăng nhập bằng mã xác thực" + "value" : "Đổi mã" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "用认证码登录" + "value" : "兑换" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "用認證碼登入" + "value" : "兌換" } } } }, - "Lowest user/IP ratio to access content" : { + "Redeem Balance Code" : { + "comment" : "Billing errors. Every one is shown to the user WITH a remedy: a payment error they cannot see is a payment error they will hit again.", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "أقل نسبة مستخدمين/عنوان IP للوصول إلى المحتوى" + "value" : "استرداد رمز الرصيد" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Nejnižší poměr uživatelů na IP pro přístup k obsahu" + "value" : "Uplatnit kód zůstatku" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Niedrigstes Nutzer/IP-Verhältnis für den Zugriff auf Inhalte" + "value" : "Guthabencode einlösen" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Η χαμηλότερη αναλογία χρηστών/IP για πρόσβαση σε περιεχόμενο" + "value" : "Εξαργύρωση κωδικού υπολοίπου" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Lowest user/IP ratio to access content" + "value" : "Redeem Balance Code" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "La menor proporción de usuarios/IP para acceder a contenido" + "value" : "Canjear código de saldo" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "La menor proporción de usuarios/IP para acceder a contenido" + "value" : "Canjear código de saldo" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Canjear código de saldo" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Le plus faible ratio utilisateur/IP pour accéder au contenu" + "value" : "Utiliser un code de solde" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "יחס המשתמשים/כתובת IP הנמוך ביותר לגישה לתוכן" + "value" : "ממש קוד יתרה" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "सामग्री तक पहुँचने के लिए सबसे कम उपयोगकर्ता/IP अनुपात" + "value" : "बैलेंस कोड रिडीम करें" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Rasio pengguna/IP terendah untuk mengakses konten" + "value" : "Tukarkan kode saldo" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Il più basso rapporto utenti/IP per accedere ai contenuti" + "value" : "Riscatta codice saldo" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "コンテンツにアクセスするための最も低いユーザー/IP比率" + "value" : "残高コードを引き換える" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "콘텐츠 접근을 위한 최저 사용자/IP 비율" + "value" : "잔액 코드 사용" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Laagste gebruiker/IP-verhouding voor toegang tot content" + "value" : "Saldo-code inwisselen" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Najniższy stosunek użytkowników do IP w dostępie do treści" + "value" : "Zrealizuj kod salda" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Resgatar código de saldo" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Menor proporção de usuários/IP para acessar conteúdo" + "value" : "Resgatar código de saldo" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Menor proporção de utilizadores/IP para aceder a conteúdo" + "value" : "Resgatar código de saldo" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Самое низкое соотношение пользователей к IP для доступа к контенту" + "value" : "Активировать код баланса" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Lägsta användare/IP-kvot för att komma åt innehåll" + "value" : "Lös in saldo-kod" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "อัตราส่วนผู้ใช้/IP ต่ำที่สุดสำหรับการเข้าถึงเนื้อหา" + "value" : "แลกรหัสยอดเงิน" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Найнижче співвідношення користувачів до IP для доступу до контенту" + "value" : "Активувати код балансу" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Tỷ lệ người dùng/IP thấp nhất để truy cập nội dung" + "value" : "Đổi mã số dư" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "最低用户/IP比率访问内容" + "value" : "兑换余额码" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "最低用戶/IP比率以存取內容" + "value" : "兌換餘額碼" } } } }, - "Make default" : { + "Redeem Code" : { + "comment" : "Used in Shared/Views/RedeemBalanceCodeSheet/RedeemBalanceCodeSheet.swift.", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "تعيين كافتراضي" + "value" : "استرداد الرمز" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Vytvořit výchozí nastavení" + "value" : "Uplatnit kód" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Als Standard festlegen" + "value" : "Code einlösen" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Ορισμός ως προεπιλογή" + "value" : "Εξαργύρωση κωδικού" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Make default" + "value" : "Redeem Code" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Hacer predeterminado" + "value" : "Canjear código" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Hacer predeterminado" + "value" : "Canjear código" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Définir par défaut" + "value" : "Utiliser le code" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "הפוך לברירת מחדל" + "value" : "ממש קוד" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "डिफ़ॉल्ट बनाएं" + "value" : "कोड रिडीम करें" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Jadikan default" + "value" : "Tukarkan kode" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Imposta come predefinito" + "value" : "Riscatta codice" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "デフォルトにする" + "value" : "コードを引き換え" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "기본값으로 설정" + "value" : "코드 사용" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Stel in als Standaard" + "value" : "Code inwisselen" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Ustaw jako domyślny" + "value" : "Wykorzystaj kod" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Tornar padrão" + "value" : "Resgatar código" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Tornar padrão" + "value" : "Resgatar código" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Сделать по умолчанию" + "value" : "Активировать код" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Gör till standard" + "value" : "Lös in kod" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "ตั้งเป็นค่าเริ่มต้น" + "value" : "แลกรหัส" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Зробити за замовчуванням" + "value" : "Активувати код" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Đặt làm mặc định" + "value" : "Đổi mã" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "设为默认" + "value" : "兑换码" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "設為默認" + "value" : "兌換碼" } } } }, - "Most Popular" : { + "Redeemed" : { + "comment" : "Guidance for someone who has no code yet. Deliberately PLAIN TEXT with no link and no button: app store rules forbid steering a user to an external purchase, but nothing stops us saying where codes come from. Without this, the sheet asks for a code and gives no way to obtain one.", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "الأكثر شعبية" + "value" : "تم الاسترداد" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Nejoblíbenější" + "value" : "Uplatněno" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Am beliebtesten" + "value" : "Eingelöst" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Πιο δημοφιλές" + "value" : "Εξαργυρώθηκε" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Most Popular" + "value" : "Redeemed" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Más popular" + "value" : "Canjeado" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Más popular" + "value" : "Canjeado" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Canjeado" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Le plus populaire" + "value" : "Utilisé" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "הפופולרי ביותר" + "value" : "מומש" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "सबसे लोकप्रिय" + "value" : "रिडीम किया गया" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Paling Populer" + "value" : "Ditukarkan" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Più popolare" + "value" : "Riscattato" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "最も人気" + "value" : "引き換え済み" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "가장 인기 있음" + "value" : "사용됨" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Meest populair" + "value" : "Ingewisseld" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Najpopularniejszy" + "value" : "Zrealizowano" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Resgatado" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Mais popular" + "value" : "Resgatado" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Mais popular" + "value" : "Resgatado" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Самый популярный" + "value" : "Активирован" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Mest populär" + "value" : "Inlöst" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "ได้รับความนิยมสูงสุด" + "value" : "แลกแล้ว" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Найпопулярніший" + "value" : "Активовано" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Phổ biến nhất" + "value" : "Đã đổi" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "最受欢迎" + "value" : "已兑换" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "最受歡迎" + "value" : "已兌換" } } } }, - "Multiplier" : { + "Refer a friend" : { + "comment" : "Used in Shared/Views/Introduction/IntroductionUsageBar.swift.", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "المضاعف" + "value" : "أوصِ صديقًا" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Násobitel" + "value" : "Doporučte přítele" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Multiplikator" + "value" : "Freund empfehlen" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Συστήστε έναν φίλο" } }, - "el" : { + "en" : { "stringUnit" : { "state" : "translated", - "value" : "Πολλαπλασιαστής" + "value" : "Refer a friend" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Multiplicador" + "value" : "Recomienda a un amigo" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Multiplicador" + "value" : "Recomienda a un amigo" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Multiplicateur" + "value" : "Parrainer un ami" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "מכפיל" + "value" : "המלץ על חבר" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "गुणक" + "value" : "मित्र को रेफ़र करें" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Pengali" + "value" : "Referensikan teman" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Moltiplicatore" + "value" : "Invita un amico" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "乗数" + "value" : "友達を紹介する" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "승수" + "value" : "친구 추천" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Vermenigvuldiger" + "value" : "Vriend doorverwijzen" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Mnożnik" + "value" : "Poleć znajomego" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Multiplicador" + "value" : "Indique um amigo" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Multiplicador" + "value" : "Recomendar um amigo" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Множитель" + "value" : "Порекомендовать друга" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Multiplikator" + "value" : "Tipsa en vän" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "ตัวคูณ" + "value" : "แนะนำเพื่อน" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Множник" + "value" : "Порекомендувати друга" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Hệ số nhân" + "value" : "Giới thiệu bạn bè" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "乘数" + "value" : "推荐朋友" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "乘數" + "value" : "推薦朋友" } } } }, - "Name" : { + "Refer a friend = +30 GiB/Month" : { + "comment" : "Used in Shared/Views/Introduction/IntroductionUsageBar.swift.", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "اسم" + "value" : "أوصِ صديقًا = +30 غيغابايت/شهر" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Jméno" + "value" : "Doporučte přítele = +30 GiB/měsíc" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Name" + "value" : "Freund empfehlen = +30 GiB/Monat" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Όνομα" + "value" : "Συστήστε έναν φίλο = +30 GiB/μήνα" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Name" + "value" : "Refer a friend = +30 GiB/Month" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Nombre" + "value" : "Recomienda a un amigo = +30 GiB/mes" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Nombre" + "value" : "Recomienda a un amigo = +30 GiB/mes" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Nom" + "value" : "Parrainer un ami = +30 Go/mois" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "שם" + "value" : "המלץ על חבר = +30 GiB/חודש" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "नाम" + "value" : "मित्र को रेफ़र करें = +30 GiB/माह" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Nama" + "value" : "Referensikan teman = +30 GiB/bulan" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Nome" + "value" : "Invita un amico = +30 GiB/mese" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "名前" + "value" : "友達を紹介 = +30 GiB/月" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "이름" + "value" : "친구 추천 = +30 GiB/월" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Naam" + "value" : "Vriend doorverwijzen = +30 GiB/maand" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Nazwa" + "value" : "Poleć znajomego = +30 GiB/miesiąc" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Nome" + "value" : "Indique um amigo = +30 GiB/mês" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Nome" + "value" : "Recomendar um amigo = +30 GiB/mês" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Имя" + "value" : "Порекомендовать друга = +30 ГиБ/месяц" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Namn" + "value" : "Tipsa en vän = +30 GiB/månad" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "ชื่อ" + "value" : "แนะนำเพื่อน = +30 GiB/เดือน" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Ім'я" + "value" : "Порекомендувати друга = +30 ГіБ/місяць" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Tên" + "value" : "Giới thiệu bạn bè = +30 GiB/tháng" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "名称" + "value" : "推荐朋友 = +30 GiB/月" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "名稱" + "value" : "推薦朋友 = +30 GiB/月" } } } }, - "Net points earned" : { + "Refer friends" : { + "comment" : "Refer overlay", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "صافي النقاط المكتسبة" + "value" : "إحالة الأصدقاء" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Získané \"net\" body" + "value" : "Doporučte přátele" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Netto verdiente Punkte" + "value" : "Freunde werben" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Καθαροί πόντοι που κερδήθηκαν" + "value" : "Προσκαλέστε φίλους" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Net points earned" + "value" : "Refer friends" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Puntos netos ganados" + "value" : "Recomendar a amigos" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Puntos netos ganados" + "value" : "Recomienda amigos" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Recomendar a amigos" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Points nets gagnés" + "value" : "Parrainer des amis" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "נקודות נטו שהושגו" + "value" : "המלץ על חברים" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "कुल अर्जित अंक" + "value" : "दोस्तों को रेफ़र करें" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Poin bersih yang diperoleh" + "value" : "Referensikan teman" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Punti netti guadagnati" + "value" : "Invita amici" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "獲得した純ポイント" + "value" : "友達を紹介する" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "순획득 포인트" + "value" : "친구 추천하기" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Totaal verdiende punten" + "value" : "Vrienden doorverwijzen" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Uzyskane punkty netto" + "value" : "Poleć znajomych" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Indicar amigos" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Pontos líquidos ganhos" + "value" : "Indicar amigos" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Pontos líquidos ganhos" + "value" : "Recomendar amigos" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Заработанные чистые баллы" + "value" : "Пригласить друзей" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Nettopoäng intjänade" + "value" : "Tipsa vänner" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "แต้มสุทธิที่ได้รับ" + "value" : "แนะนำเพื่อน" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Чисті зароблені бали" + "value" : "Запросити друзів" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Điểm ròng đã kiếm được" + "value" : "Giới thiệu bạn bè" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "净赚积分" + "value" : "推荐朋友" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "淨賺積分" + "value" : "推薦朋友" } } } }, - "Net Provided" : { + "Refer some friends and watch your free data go up." : { + "comment" : "Call to action", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "صافي البيانات المقدمة" + "value" : "قم بإحالة بعض الأصدقاء وشاهد بياناتك المجانية تزداد." } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Poskytnuto" + "value" : "Doporučte pár přátel a sledujte, jak vaše bezplatná data rostou." } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Bereitgestelltes Netz" + "value" : "Empfehle ein paar Freunde und sieh zu, wie dein kostenloses Datenvolumen steigt." } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Καθαρό παρεχόμενο" + "value" : "Προτείνετε μερικούς φίλους και δείτε τα δωρεάν δεδομένα σας να αυξάνονται." } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Net Provided" + "value" : "Refer some friends and watch your free data go up." } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Red proporcionada" + "value" : "Recomienda algunos amigos y observa cómo aumenta tu datos gratis." } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Red proporcionada" + "value" : "Recomienda a algunos amigos y observa cómo aumenta tu datos gratis." + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Recomienda algunos amigos y observa cómo aumenta tu datos gratis." } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Net fourni" + "value" : "Parrainez des amis et voyez vos données gratuites augmenter." } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "סופק נטו" + "value" : "המלץ על חברים וצפה בנתונים החינמיים שלך גדלים." } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "नेट प्रदान किया गया" + "value" : "कुछ दोस्तों को रेफ़र करें और अपनी मुफ्त डेटा को बढ़ते हुए देखें।" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Bersih Disediakan" + "value" : "Referensikan beberapa teman dan lihat data gratis Anda bertambah." } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Fornito netto" + "value" : "Invita degli amici e guarda i tuoi dati gratuiti aumentare." } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "ネット提供" + "value" : "友達を紹介して、無料データが増えるのを見ましょう。" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "순 제공량" + "value" : "친구를 추천하고 무료 데이터가 늘어나는 것을 확인하세요." } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Totaal geleverd" + "value" : "Verwijs wat vrienden door en zie je gratis data stijgen." } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Dostarczono netto" + "value" : "Poleć kilku znajomych i zobacz, jak rosną Twoje darmowe dane." + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Recomende alguns amigos e veja os seus dados gratuitos aumentarem." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Fornecido líquido" + "value" : "Indique alguns amigos e veja seus dados gratuitos aumentarem." } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Fornecido líquido" + "value" : "Recomende alguns amigos e veja os seus dados gratuitos aumentarem." } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Предоставлено сети (нетто)" + "value" : "Пригласите друзей и наблюдайте, как увеличивается ваш бесплатный трафик." } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Nettotillhandahållen" + "value" : "Tipsa några vänner och se din fria data öka." } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "ให้สุทธิ" + "value" : "แนะนำเพื่อนสักสองสามคนแล้วดูว่าข้อมูลฟรีของคุณเพิ่มขึ้นอย่างไร" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Надано мережею (нетто)" + "value" : "Запросіть друзів і спостерігайте, як збільшується ваш безкоштовний трафік." } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Mạng cung cấp (ròng)" + "value" : "Giới thiệu bạn bè và xem dữ liệu miễn phí của bạn tăng lên." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "净提供" + "value" : "推荐一些朋友,看看你的免费流量如何增加。" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "淨提供" + "value" : "推薦一些朋友,看看你的免費數據如何增加。" } } } }, - "Network earnings" : { + "Referral" : { + "comment" : "Call to action", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "أرباح الشبكة" + "value" : "إحالة" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Výnosy ze sítě" + "value" : "Doporučení" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Netzwerk-Einnahmen" + "value" : "Empfehlung" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Κέρδη δικτύου" + "value" : "Παραπομπή" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Network earnings" + "value" : "Referral" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Ganancias de la red" + "value" : "Referido" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Ganancias de la red" + "value" : "Referencia" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Referido" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Gains du réseau" + "value" : "Parrainage" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "רווחי רשת" + "value" : "הפניה" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "नेटवर्क आय" + "value" : "रेफ़रल" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Penghasilan Jaringan" + "value" : "Referensi" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Guadagni della rete" + "value" : "Referenza" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "ネットワーク収益" + "value" : "紹介" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "네트워크 수익" + "value" : "추천" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Netwerkinkomsten" + "value" : "Doorverwijzing" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Zarobki sieci" + "value" : "Polecenie" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Referência" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Ganhos da rede" + "value" : "Indicação" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Ganhos da rede" + "value" : "Indicação" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Доход сети" + "value" : "Реферал" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Nätverksintäkter" + "value" : "Rekommendation" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "รายได้ของเครือข่าย" + "value" : "การแนะนำเชิญชวน" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Заробіток мережі" + "value" : "Реферал" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Thu nhập mạng" + "value" : "Giới thiệu" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "网络收益" + "value" : "推荐" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "網絡收益" + "value" : "推薦" } } } }, - "Network name" : { + "Referral Bonus applied" : { + "comment" : "Used in Authenticate/CreateNetwork/CreateNetworkView.swift.", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "اسم الشبكة" + "value" : "تم تطبيق مكافأة الإحالة" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Jméno sítě" + "value" : "Uplatnění bonusu za doporučení" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Netzwerkname" + "value" : "Empfehlungsbonus angewendet" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Όνομα δικτύου" + "value" : "Εφαρμόστηκε το μπόνους παραπομπής" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Network name" + "value" : "Referral Bonus applied" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Nombre de la red" + "value" : "Bonificación por referencia aplicada" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Nombre de la red" + "value" : "Bonificación por referencia aplicada" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Nom du réseau" + "value" : "Bonus de parrainage appliqué" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "שם הרשת" + "value" : "הוחל בונוס הפניה" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "नेटवर्क का नाम" + "value" : "रेफरल बोनस लागू किया गया" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Nama jaringan" + "value" : "Bonus rujukan diterapkan" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Nome della rete" + "value" : "Bonus di riferimento applicato" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "ネットワーク名" + "value" : "紹介ボーナスが適用されました" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "네트워크 이름" + "value" : "추천 보너스가 적용되었습니다" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Netwerknaam" + "value" : "Verwijzingsbonus toegepast" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Nazwa sieci" + "value" : "Zastosowano bonus za polecenie" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Nome da rede" + "value" : "Bônus de indicação aplicado" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Nome da rede" + "value" : "Bônus de indicação aplicado" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Имя сети" + "value" : "Применен реферальный бонус" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Nätverksnamn" + "value" : "Rekommendationsbonus tillämpad" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "ชื่อเครือข่าย" + "value" : "ใช้โบนัสการแนะนำแล้ว" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Назва мережі" + "value" : "Застосовано реферальний бонус" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Tên mạng" + "value" : "Đã áp dụng tiền thưởng giới thiệu" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "网络名称" + "value" : "已应用推荐奖金" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "網絡名稱" + "value" : "已應用推薦獎金" } } } }, - "Network name is too short. Try one with at least 6 characters" : { - "comment" : "Supporting text for a text field that is invalid.", - "isCommentAutoGenerated" : true - }, - "Network names must be 6 characters or more" : { + "Referral network" : { + "comment" : "Call to action", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "يجب أن تكون أسماء الشبكات مكونة من 6 أحرف أو أكثر" + "value" : "شبكة الإحالة" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Jméno sítě musí být delší než 6 písmen a čísel" + "value" : "Síť doporučení" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Netzwerknamen müssen mindestens 6 Zeichen lang sein" + "value" : "Empfehlungsnetzwerk" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Τα ονόματα δικτύου πρέπει να έχουν τουλάχιστον 6 χαρακτήρες" + "value" : "Δίκτυο παραπομπών" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Network names must be 6 characters or more" + "value" : "Referral network" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Los nombres de red deben tener 6 caracteres o más" + "value" : "Red de referidos" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Los nombres de red deben tener 6 caracteres o más" + "value" : "Red de referidos" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Red de referidos" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Les noms de réseau doivent comporter au moins 6 caractères" + "value" : "Réseau de parrainage" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "שמות רשת חייבים להיות באורך של 6 תווים או יותר" + "value" : "רשת הפניות" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "नेटवर्क नाम 6 वर्ण या अधिक का होना चाहिए" + "value" : "रेफ़रल नेटवर्क" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Nama jaringan harus terdiri dari 6 karakter atau lebih" + "value" : "Jaringan referensi" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "I nomi di rete devono essere di almeno 6 caratteri" + "value" : "Rete di riferimento" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "ネットワーク名は6文字以上である必要があります" + "value" : "紹介ネットワーク" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "네트워크 이름은 6자 이상이어야 합니다" + "value" : "추천 네트워크" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Netwerk moet minstens 6 tekens lang zijn" + "value" : "Referentienetwerk" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Nazwy sieci muszą mieć co najmniej 6 znaków" + "value" : "Sieć poleceń" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rede de referências" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Os nomes de rede devem ter 6 caracteres ou mais" + "value" : "Rede de indicações" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Os nomes de rede devem ter 6 caracteres ou mais" + "value" : "Rede de recomendações" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Имена сетей должны содержать не менее 6 символов" + "value" : "Реферальная сеть" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Nätverksnamn måste vara minst 6 tecken långa" + "value" : "Referensnätverk" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "ชื่อเครือข่ายต้องมีอย่างน้อย 6 ตัวอักษร" + "value" : "แนะนำเครือข่าย" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Імена мереж повинні містити щонайменше 6 символів" + "value" : "Реферальна мережа" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Tên mạng phải dài ít nhất 6 ký tự" + "value" : "Mạng giới thiệu" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "网络名称必须为6个字符或以上" + "value" : "推荐网络" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "網絡名稱必須為6個字符或以上" + "value" : "推薦網絡" } } } }, - "Network peers" : { - "comment" : "A label displayed above the list of connected peers.", - "isCommentAutoGenerated" : true - }, - "Never" : { + "Referrals" : { + "comment" : "Used in Shared/Views/ReferBar.swift.", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "أبدًا" + "value" : "الإحالات" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Nikdy" + "value" : "Doporučení" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Nie" + "value" : "Empfehlungen" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Ποτέ" + "value" : "Παραπομπές" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Never" + "value" : "Referrals" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Nunca" + "value" : "Referidos" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Nunca" + "value" : "Referidos" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Jamais" + "value" : "Parrainages" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "אף פעם" + "value" : "הפניות" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "कभी नहीं" + "value" : "रेफ़रल" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Tidak pernah" + "value" : "Referensi" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Mai" + "value" : "Inviti" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "決して" + "value" : "紹介" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "절대" + "value" : "추천" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Nooit" + "value" : "Verwijzingen" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Nigdy" + "value" : "Polecenia" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Nunca" + "value" : "Indicações" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Nunca" + "value" : "Referências" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Никогда" + "value" : "Рефералы" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Aldrig" + "value" : "Rekommendationer" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "ไม่เคย" + "value" : "การแนะนำ" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Ніколи" + "value" : "Реферали" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Không bao giờ" + "value" : "Giới thiệu" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "从不" + "value" : "推荐" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "從不" + "value" : "推薦" } } } }, - "New split rule" : { - "comment" : "A label displayed above the form for creating a new split rule.", - "isCommentAutoGenerated" : true - }, - "Nice! This network name is available" : { + "Reliability" : { + "comment" : "Call to action", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "رائع! اسم الشبكة هذا متاح" + "value" : "الموثوقية" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Pěkně! Todle jméno sítě je dostupné." + "value" : "Spolehlivost" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Schön! Dieser Netzwerkname ist verfügbar" + "value" : "Zuverlässigkeit" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Ωραία! Αυτό το όνομα δικτύου είναι διαθέσιμο" + "value" : "Αξιοπιστία" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Nice! This network name is available" + "value" : "Reliability" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "¡Genial! Este nombre de red está disponible" + "value" : "Fiabilidad" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "¡Genial! Este nombre de red está disponible" + "value" : "Fiabilidad" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fiabilidad" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Super ! Ce nom de réseau est disponible" + "value" : "Fiabilité" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "נחמד! שם הרשת הזה זמין" + "value" : "אמינות" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "अच्छा! यह नेटवर्क नाम उपलब्ध है" + "value" : "विश्वसनीयता" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Bagus! Nama jaringan ini tersedia" + "value" : "Keandalan" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Bene! Questo nome di rete è disponibile" + "value" : "Affidabilità" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "いいですね!このネットワーク名は使用可能です" + "value" : "信頼性" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "좋아요! 이 네트워크 이름을 사용할 수 있습니다" + "value" : "신뢰성" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Mooi! Deze netwerknaam is beschikbaar" + "value" : "Betrouwbaarheid" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Świetnie! Ta nazwa sieci jest dostępna" + "value" : "Niezawodność" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fiabilidade" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Legal! Este nome de rede está disponível" + "value" : "Confiabilidade" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Legal! Este nome de rede está disponível" + "value" : "Confiabilidade" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Отлично! Это имя сети доступно" + "value" : "Надежность" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Bra! Detta nätverksnamn är tillgängligt" + "value" : "Tillförlitlighet" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "ดี! ชื่อเครือข่ายนี้สามารถใช้ได้" + "value" : "ความน่าเชื่อถือ" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Чудово! Це ім'я мережі доступне" + "value" : "Надійність" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Tuyệt! Tên mạng này có sẵn" + "value" : "Độ tin cậy" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "很好!此网络名称可用" + "value" : "可靠性" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "很好!此網絡名稱可用" + "value" : "可靠性" } } } }, - "Nicely done." : { + "Reliability Settings" : { + "extractionState" : "stale", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "عمل جيد." + "value" : "إعدادات الموثوقية" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Pěkně uděláno." + "value" : "Nastavení spolehlivosti" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Gut gemacht." + "value" : "Zuverlässigkeitseinstellungen" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Μπράβο." + "value" : "Ρυθμίσεις αξιοπιστίας" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Nicely done." + "value" : "Reliability Settings" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Bien hecho." + "value" : "Configuración de fiabilidad" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Bien hecho." + "value" : "Configuración de fiabilidad" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Bien joué." + "value" : "Paramètres de fiabilité" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "כל הכבוד." + "value" : "הגדרות אמינות" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "अच्छा किया।" + "value" : "विश्वसनीयता सेटिंग्स" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Kerja bagus." + "value" : "Pengaturan Keandalan" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Ben fatto." + "value" : "Impostazioni di affidabilità" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "よくできました。" + "value" : "信頼性設定" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "잘했어요." + "value" : "신뢰도 설정" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Goed gedaan!" + "value" : "Betrouwbaarheidsinstellingen" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Dobra robota." + "value" : "Ustawienia niezawodności" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Bem feito." + "value" : "Configurações de confiabilidade" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Bem feito." + "value" : "Definições de fiabilidade" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Отлично сделано." + "value" : "Настройки надежности" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Bra gjort." + "value" : "Inställningar för tillförlitlighet" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "ทำได้ดีมาก." + "value" : "การตั้งค่าความน่าเชื่อถือ" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Добре зроблено." + "value" : "Налаштування надійності" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Làm tốt lắm." + "value" : "Cài đặt độ tin cậy" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "做得好。" + "value" : "可靠性设置" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "做得好。" + "value" : "可靠性設定" } } } }, - "No balance codes found" : { + "Reliability Weight" : { + "comment" : "Call to action", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "لم يتم العثور على رموز رصيد" + "value" : "وزن الموثوقية" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Nebyly nalezeny žádné kódy na zůstatek" + "value" : "Váha spolehlivosti" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Keine Guthabencodes gefunden" + "value" : "Zuverlässigkeitsgewicht" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Δεν βρέθηκαν κωδικοί υπολοίπου" + "value" : "Βάρος αξιοπιστίας" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "No balance codes found" + "value" : "Reliability Weight" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "No se encontraron códigos de saldo" + "value" : "Peso de fiabilidad" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "No se encontraron códigos de saldo" + "value" : "Peso de fiabilidad" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Peso de fiabilidad" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Aucun code de solde trouvé" + "value" : "Poids de fiabilité" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "לא נמצאו קודי יתרה" + "value" : "משקל אמינות" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "कोई बैलेंस कोड नहीं मिला" + "value" : "विश्वसनीयता भार" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Tidak ditemukan kode saldo" + "value" : "Bobot keandalan" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Nessun codice saldo trovato" + "value" : "Peso di affidabilità" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "残高コードが見つかりません" + "value" : "信頼性の重み" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "잔액 코드가 없습니다" + "value" : "신뢰성 가중치" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Geen saldo codes gevonden" + "value" : "Betrouwbaarheidsgewicht" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Nie znaleziono kodów salda" + "value" : "Waga niezawodności" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Peso de fiabilidade" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Nenhum código de saldo encontrado" + "value" : "Peso de confiabilidade" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Nenhum código de saldo encontrado" + "value" : "Peso de confiabilidade" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Коды баланса не найдены" + "value" : "надёжности" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Inga saldo-koder hittades" + "value" : "Tillförlitlighetsvikt" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "ไม่พบรหัสยอดเงิน" + "value" : "น้ำหนักความน่าเชื่อถือ" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Кодів балансу не знайдено" + "value" : "Вага надійності" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Không tìm thấy mã số dư nào" + "value" : "Trọng số độ tin cậy" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "未找到余额码" + "value" : "可靠性权重" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "找不到餘額碼" + "value" : "可靠性權重" } } } }, - "No blocked locations" : { + "Reliability: %@" : { + "extractionState" : "stale", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "لا توجد مواقع محظورة" + "value" : "الموثوقية: %@" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Žádné blokované lokality" + "value" : "Spolehlivost: %@" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Keine blockierten Standorte" + "value" : "Zuverlässigkeit: %@" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Δεν υπάρχουν αποκλεισμένες τοποθεσίες" + "value" : "Αξιοπιστία: %@" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "No blocked locations" + "value" : "Reliability: %@" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "No hay ubicaciones bloqueadas" + "value" : "Fiabilidad: %@" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "No hay ubicaciones bloqueadas" + "value" : "Fiabilidad: %@" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Aucun emplacement bloqué" + "value" : "Fiabilité : %@" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "אין מיקומים חסומים" + "value" : "אמינות: %@" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "कोई अवरुद्ध स्थान नहीं" + "value" : "विश्वसनीयता: %@" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Tidak ada lokasi yang diblokir" + "value" : "Keandalan: %@" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Nessuna posizione bloccata" + "value" : "Affidabilità: %@" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "ブロックされた場所はありません" + "value" : "信頼性: %@" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "차단된 위치 없음" + "value" : "신뢰도: %@" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Geen geblokkeerde locaties" + "value" : "Betrouwbaarheid: %@" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Brak zablokowanych lokalizacji" + "value" : "Niezawodność: %@" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Nenhuma localização bloqueada" + "value" : "Confiabilidade: %@" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Nenhuma localização bloqueada" + "value" : "Fiabilidade: %@" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Нет заблокированных местоположений" + "value" : "Надежность: %@" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Inga blockerade platser" + "value" : "Tillförlitlighet: %@" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "ไม่มีตำแหน่งที่ถูกบล็อก" + "value" : "ความน่าเชื่อถือ: %@" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Немає заблокованих місць" + "value" : "Надійність: %@" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Không có vị trí bị chặn" + "value" : "Độ tin cậy: %@" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "没有被屏蔽的位置" + "value" : "可靠性:%@" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "沒有被屏蔽的位置" + "value" : "可靠性:%@" } } } }, - "No open contracts" : { - "comment" : "A label displayed when there are no open contracts.", - "isCommentAutoGenerated" : true - }, - "Notifications" : { + "Reliability: %lf" : { + "extractionState" : "stale", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "الإشعارات" + "value" : "الموثوقية: %lf" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Notifikace" + "value" : "Spolehlivost: %lf" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Benachrichtigungen" + "value" : "Zuverlässigkeit: %lf" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Ειδοποιήσεις" + "value" : "Αξιοπιστία: %lf" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Notifications" + "value" : "Reliability: %lf" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Notificaciones" + "value" : "Fiabilidad: %lf" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Notificaciones" + "value" : "Fiabilidad: %lf" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Notifications" + "value" : "Fiabilité : %lf" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "התראות" + "value" : "אמינות: %lf" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "सूचनाएं" + "value" : "विश्वसनीयता: %lf" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Notifikasi" + "value" : "Keandalan: %lf" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Notifiche" + "value" : "Affidabilità: %lf" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "通知" + "value" : "信頼性: %lf" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "알림" + "value" : "신뢰도: %lf" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Meldingen" + "value" : "Betrouwbaarheid: %lf" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Powiadomienia" + "value" : "Niezawodność: %lf" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Notificações" + "value" : "Confiabilidade: %lf" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Notificações" + "value" : "Fiabilidade: %lf" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Уведомления" + "value" : "Надежность: %lf" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Aviseringar" + "value" : "Tillförlitlighet: %lf" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "การแจ้งเตือน" + "value" : "ความน่าเชื่อถือ: %lf" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Сповіщення" + "value" : "Надійність: %lf" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Thông báo" + "value" : "Độ tin cậy: %lf" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "通知" + "value" : "可靠性:%lf" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "通知" - } - } - } - }, - "of %@" : { - "comment" : "A label that shows the total size of the contract.", - "isCommentAutoGenerated" : true - }, - "Off" : { - "comment" : "A label for a DNS setting that is currently off.", - "isCommentAutoGenerated" : true - }, - "On" : { - "comment" : "A label for a DNS setting that is enabled.", - "isCommentAutoGenerated" : true - }, - "or" : { - "comment" : "Referring to the two options 'Get started' *or* 'Login with Apple'", - "localizations" : { - "cs" : { - "stringUnit" : { - "state" : "translated", - "value" : "nebo" - } - }, - "nl" : { - "stringUnit" : { - "state" : "translated", - "value" : "Of" + "value" : "可靠性:%lf" } } } }, - "Other" : { + "remote" : { + "comment" : "dns settings", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "آخر" + "value" : "بعيد" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Jiné" + "value" : "vzdáleně" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Andere" + "value" : "extern" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Άλλο" + "value" : "απομακρυσμένο" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Other" + "value" : "remote" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Otro" + "value" : "remoto" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Otro" + "value" : "remoto" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "remoto" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Autre" + "value" : "distant" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "אחר" + "value" : "מרוחק" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "अन्य" + "value" : "रिमोट" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Lainnya" + "value" : "jarak jauh" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Altro" + "value" : "remoto" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "その他" + "value" : "リモート" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "기타" + "value" : "원격" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Andere" + "value" : "extern" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Inne" + "value" : "zdalnie" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "remoto" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Outro" + "value" : "remoto" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Outro" + "value" : "remoto" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Другое" + "value" : "удалённо" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Annat" + "value" : "fjärr" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "อื่นๆ" + "value" : "ระยะไกล" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Інше" + "value" : "віддалено" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Khác" + "value" : "từ xa" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "其他" + "value" : "远程" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "其他" + "value" : "遠端" } } } }, - "Participate in the network and get free access to the community edition." : { - "extractionState" : "stale", + "Remote" : { + "comment" : "Label for a chart showing the remote traffic.", + "isCommentAutoGenerated" : true, "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "شارك في الشبكة واحصل على وصول مجاني إلى إصدار المجتمع." + "value" : "بعيد" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Zapojte se do sítě a získejte bezplatný přístup k komunitní edici." + "value" : "Vzdáleně" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Nehmen Sie am Netzwerk teil und erhalten Sie kostenlosen Zugang zur Community Edition." + "value" : "Extern" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Συμμετέχετε στο δίκτυο και αποκτήστε δωρεάν πρόσβαση στην έκδοση κοινότητας." + "value" : "Απομακρυσμένο" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Participate in the network and get free access to the community edition." + "value" : "Remote" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Participa en la red y obtén acceso gratuito a la edición comunitaria." + "value" : "Remoto" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Participa en la red y obtén acceso gratuito a la edición comunitaria." + "value" : "Remoto" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Remoto" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Participez au réseau et obtenez un accès gratuit à l’édition communautaire." + "value" : "Distant" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "השתתף ברשת וקבל גישה חינם למהדורת הקהילה." + "value" : "מרוחק" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "नेटवर्क में भाग लें और कम्युनिटी संस्करण तक मुफ्त पहुँच प्राप्त करें।" + "value" : "रिमोट" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Berpartisipasi di jaringan dan dapatkan akses gratis ke edisi komunitas." + "value" : "Jarak jauh" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Partecipa alla rete e ottieni l’accesso gratuito all’edizione community." + "value" : "Remoto" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "ネットワークに参加して、コミュニティ版に無料でアクセスしましょう。" + "value" : "リモート" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "네트워크에 참여하고 커뮤니티 에디션을 무료로 이용하세요." + "value" : "원격" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Neem deel aan het netwerk en krijg gratis toegang tot de community-editie." + "value" : "Extern" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Weź udział w sieci i uzyskaj bezpłatny dostęp do edycji społecznościowej." + "value" : "Zdalnie" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Remoto" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Participe da rede e tenha acesso gratuito à edição comunitária." + "value" : "Remoto" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Participe na rede e tenha acesso gratuito à edição comunitária." + "value" : "Remoto" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Участвуйте в сети и получите бесплатный доступ к версии для сообщества." + "value" : "Удалённо" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Delta i nätverket och få gratis tillgång till community-utgåvan." + "value" : "Fjärr" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "เข้าร่วมเครือข่ายและรับสิทธิ์ใช้งานฉบับชุมชนฟรี" + "value" : "ระยะไกล" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Беріть участь у мережі та отримайте безкоштовний доступ до спільнотної версії." + "value" : "Віддалено" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Tham gia mạng lưới và nhận quyền truy cập miễn phí vào phiên bản cộng đồng." + "value" : "Từ xa" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "参与网络,免费获得社区版访问权限。" + "value" : "远程" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "參與網絡,免費獲得社群版存取權限。" + "value" : "遠端" } } } }, - "Password" : { + "Remote DNS servers" : { + "comment" : "A label displayed above a list of DNS servers.", + "isCommentAutoGenerated" : true, "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "كلمة المرور" + "value" : "خوادم DNS البعيدة" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Heslo" + "value" : "Vzdálené servery DNS" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Passwort" + "value" : "Externe DNS-Server" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Κωδικός πρόσβασης" + "value" : "Απομακρυσμένοι διακομιστές DNS" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Password" + "value" : "Remote DNS servers" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Contraseña" + "value" : "Servidores DNS remotos" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Contraseña" + "value" : "Servidores DNS remotos" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Servidores DNS remotos" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Mot de passe" + "value" : "Serveurs DNS distants" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "סיסמה" + "value" : "שרתי DNS מרוחקים" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "पासवर्ड" + "value" : "रिमोट DNS सर्वर" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Kata sandi" + "value" : "Server DNS jarak jauh" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Password" + "value" : "Server DNS remoti" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "パスワード" + "value" : "リモートDNSサーバー" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "비밀번호" + "value" : "원격 DNS 서버" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Wachtwoord" + "value" : "Externe DNS-servers" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Hasło" + "value" : "Zdalne serwery DNS" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Servidores DNS remotos" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Senha" + "value" : "Servidores DNS remotos" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Senha" + "value" : "Servidores DNS remotos" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Пароль" + "value" : "Удалённые DNS-серверы" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Lösenord" + "value" : "Fjärr-DNS-servrar" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "รหัสผ่าน" + "value" : "เซิร์ฟเวอร์ DNS ระยะไกล" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Пароль" + "value" : "Віддалені DNS-сервери" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Mật khẩu" + "value" : "Máy chủ DNS từ xa" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "密码" + "value" : "远程 DNS 服务器" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "密碼" + "value" : "遠端 DNS 伺服器" } } } }, - "Password must be at least 12 characters long" : { + "Remote DoH URLs" : { + "comment" : "A heading for a list of remote DoH URLs.", + "isCommentAutoGenerated" : true, "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "يجب أن تكون كلمة المرور مكونة من 12 حرفًا على الأقل" + "value" : "عناوين URL لـ DoH البعيد" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Heslo musí být minimálně 12 znaků dlouhé" + "value" : "Vzdálené adresy URL DoH" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Das Passwort muss mindestens 12 Zeichen lang sein" + "value" : "Externe DoH-URLs" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Ο κωδικός πρόσβασης πρέπει να έχει τουλάχιστον 12 χαρακτήρες" + "value" : "Απομακρυσμένα URL DoH" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Password must be at least 12 characters long" + "value" : "Remote DoH URLs" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "La contraseña debe tener al menos 12 caracteres" + "value" : "URLs DoH remotas" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "La contraseña debe tener al menos 12 caracteres" + "value" : "URLs DoH remotas" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "URLs DoH remotas" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Le mot de passe doit comporter au moins 12 caractères" + "value" : "URL DoH distantes" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "הסיסמה חייבת להיות באורך של לפחות 12 תווים" + "value" : "כתובות URL ל-DoH מרוחק" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "पासवर्ड कम से कम 12 अक्षरों का होना चाहिए" + "value" : "रिमोट DoH URLs" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Kata sandi harus terdiri dari minimal 12 karakter" + "value" : "URL DoH jarak jauh" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "La password deve essere lunga almeno 12 caratteri" + "value" : "URL DoH remoti" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "パスワードは12文字以上である必要があります" + "value" : "リモートDoH URL" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "비밀번호는 최소 12자 이상이어야 합니다" + "value" : "원격 DoH URL" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Wachtwoord moet minstens 12 tekens lang zijn" + "value" : "Externe DoH-URL's" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Hasło musi mieć co najmniej 12 znaków" + "value" : "Zdalne adresy URL DoH" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "URLs DoH remotos" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "A senha deve ter pelo menos 12 caracteres" + "value" : "URLs DoH remotas" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "A senha deve ter pelo menos 12 caracteres" + "value" : "URLs DoH remotos" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Пароль должен содержать не менее 12 символов" + "value" : "Удалённые DoH URL" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Lösenordet måste vara minst 12 tecken långt" + "value" : "Fjärr-DoH-URL:er" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "รหัสผ่านต้องมีความยาวอย่างน้อย 12 ตัวอักษร" + "value" : "DoH URL ระยะไกล" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Пароль повинен містити щонайменше 12 символів" + "value" : "Віддалені DoH URL" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Mật khẩu phải dài ít nhất 12 ký tự" + "value" : "URL DoH từ xa" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "密码必须至少12个字符长" + "value" : "远程 DoH URLs" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "密碼必須至少12個字符長" + "value" : "遠端 DoH URLs" } } } }, - "Payment Pending" : { + "Remove" : { + "comment" : "A label for a button that removes a rule.", + "isCommentAutoGenerated" : true, "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "الدفع معلق" + "value" : "أزل" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Čekající platba" + "value" : "Odstranit" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Zahlung ausstehend" + "value" : "Entferne" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Εκκρεμεί πληρωμή" + "value" : "Αφαίρεση" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Payment Pending" + "value" : "Remove" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Pago pendiente" + "value" : "Elimina" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Pago pendiente" + "value" : "Quitar" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elimina" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Paiement en attente" + "value" : "Supprimer" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "התשלום ממתין" + "value" : "הסר" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "भुगतान लंबित है" + "value" : "हटाएं" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Pembayaran tertunda" + "value" : "Hapus" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Pagamento in sospeso" + "value" : "Rimuovi" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "支払い保留中" + "value" : "削除" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "결제 대기 중" + "value" : "제거" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Betaling in behandeling" + "value" : "Verwijderen" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Płatność oczekuje" + "value" : "Usuń" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Remova" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Pagamento pendente" + "value" : "Remover" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Pagamento pendente" + "value" : "Remover" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Ожидание оплаты" + "value" : "Удалить" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Betalning väntar" + "value" : "Ta bort" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "การชำระเงินรอดำเนินการ" + "value" : "ลบ" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Очікується оплата" + "value" : "Видалити" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Thanh toán đang chờ xử lý" + "value" : "Gỡ bỏ" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "付款待处理" + "value" : "移除" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "付款待處理" + "value" : "移除" } } } }, - "Payout" : { + "Remove rule" : { + "comment" : "A button to remove a split rule.", + "isCommentAutoGenerated" : true, "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "دفع" + "value" : "إزالة القاعدة" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Výplata" + "value" : "Odstranit pravidlo" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Auszahlung" + "value" : "Regel entfernen" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Πληρωμή" + "value" : "Αφαίρεση κανόνα" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Payout" + "value" : "Remove rule" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Pago" + "value" : "Eliminar regla" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Pago" + "value" : "Eliminar regla" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Eliminar regla" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Paiement" + "value" : "Supprimer la règle" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "תשלום" + "value" : "הסר כלל" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "भुगतान" + "value" : "नियम हटाएं" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Pembayaran" + "value" : "Hapus aturan" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Pagamento" + "value" : "Rimuovi regola" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "支払い" + "value" : "ルールを削除" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "지급" + "value" : "규칙 제거" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Uitbetaling" + "value" : "Regel verwijderen" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Wypłata" + "value" : "Usuń regułę" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Remover regra" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Pagamento" + "value" : "Remover regra" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Pagamento" + "value" : "Remover regra" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Выплата" + "value" : "Удалить правило" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Utbetalning" + "value" : "Ta bort regel" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "การจ่ายเงิน" + "value" : "ลบกฎ" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Виплата" + "value" : "Видалити правило" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Thanh toán" + "value" : "Xóa quy tắc" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "支付" + "value" : "移除规则" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "支付" + "value" : "移除規則" } } } }, - "Payout Wallets" : { + "Remove wallet" : { + "comment" : "Used in Main/Account/AccountNavStackView.swift.", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "محافظ الدفع" + "value" : "إزالة المحفظة" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Výplatní peněženky" + "value" : "Odstranit peněženku" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Auszahlungs-Wallets" + "value" : "Wallet entfernen" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Πορτοφόλια πληρωμών" + "value" : "Αφαίρεση πορτοφολιού" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Payout Wallets" + "value" : "Remove wallet" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Carteras de pago" + "value" : "Eliminar cartera" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Carteras de pago" + "value" : "Eliminar billetera" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Portefeuilles de paiement" + "value" : "Supprimer le portefeuille" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "ארנקי תשלום" + "value" : "הסר ארנק" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "भुगतान वॉलेट" + "value" : "वॉलेट हटाएं" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Dompet Pembayaran" + "value" : "Hapus dompet" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Portafogli di pagamento" + "value" : "Rimuovi portafoglio" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "支払いウォレット" + "value" : "ウォレットを削除" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "지급 지갑" + "value" : "지갑 제거" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Uitbetalingswallet" + "value" : "Verwijder wallet" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Portfele wypłat" + "value" : "Usuń portfel" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Carteiras de pagamento" + "value" : "Remover carteira" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Carteiras de pagamento" + "value" : "Remover carteira" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Кошельки для выплат" + "value" : "Удалить кошелек" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Utbetalningsplånböcker" + "value" : "Ta bort plånbok" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "กระเป๋าเงินจ่ายเงิน" + "value" : "ลบกระเป๋าเงิน" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Гаманці для виплат" + "value" : "Видалити гаманець" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Ví thanh toán" + "value" : "Xóa ví" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "支付钱包" + "value" : "移除钱包" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "支付錢包" + "value" : "移除錢包" } } } }, - "Payouts occur every Sunday at 00:00 UTC, and require meeting a minimum USDC threshold." : { + "Resend code" : { + "comment" : "Button text to resend the verification code", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "تحدث عمليات الدفع كل يوم أحد الساعة 00:00 بتوقيت UTC، وتتطلب تحقيق حد أدنى من USDC." + "value" : "إعادة إرسال الرمز" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Výplaty probíhají každou neděli ve 00:00 UTC a vyžadují splnění minimálního USDC limitu." + "value" : "Odeslat kód znovu" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Auszahlungen erfolgen jeden Sonntag um 00:00 UTC und erfordern das Erreichen eines Mindest-USDC-Schwellenwerts." + "value" : "Code erneut senden" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Οι πληρωμές πραγματοποιούνται κάθε Κυριακή στις 00:00 UTC και απαιτούν την επίτευξη ενός ελάχιστου ορίου USDC." + "value" : "Επανάληψη αποστολής κωδικού" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Payouts occur every Sunday at 00:00 UTC, and require meeting a minimum USDC threshold." + "value" : "Resend code" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Los pagos se realizan cada domingo a las 00:00 UTC y requieren cumplir con un umbral mínimo de USDC." + "value" : "Reenviar código" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Los pagos se realizan cada domingo a las 00:00 UTC y requieren cumplir con un umbral mínimo de USDC." + "value" : "Reenviar código" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Reenviar código" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Les paiements ont lieu chaque dimanche à 00:00 UTC et nécessitent de respecter un seuil minimum de USDC." + "value" : "Renvoyer le code" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "התשלומים מתבצעים בכל יום ראשון בשעה 00:00 UTC ודורשים עמידה בסף מינימלי של USDC." + "value" : "שלח קוד מחדש" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "भुगतान हर रविवार 00:00 UTC पर होते हैं, और न्यूनतम USDC सीमा पूरी करना आवश्यक है।" + "value" : "कोड फिर से भेजें" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Pembayaran dilakukan setiap hari Minggu pukul 00:00 UTC dan memerlukan pencapaian ambang minimum USDC." + "value" : "Kirim ulang kode" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "I pagamenti avvengono ogni domenica alle 00:00 UTC e richiedono il raggiungimento di una soglia minima di USDC." + "value" : "Rinvia codice" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "支払いは毎週日曜日の00:00 UTCに行われ、最低USDCしきい値の達成が必要です。" + "value" : "コードを再送信" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "지급은 매주 일요일 00:00 UTC에 이루어지며, 최소 USDC 기준을 충족해야 합니다." + "value" : "코드 재전송" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Code opnieuw versturen" } }, - "nl" : { + "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Uitbetalingen vinden elke zondag om 00:00 UTC plaats en vereisen het behalen van een minimale USDC-drempel." + "value" : "Wyślij kod ponownie" } }, - "pl" : { + "pt" : { "stringUnit" : { "state" : "translated", - "value" : "Wypłaty odbywają się w każdą niedzielę o 00:00 UTC i wymagają spełnienia minimalnego progu USDC." + "value" : "Reenviar código" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Os pagamentos ocorrem todo domingo às 00:00 UTC e exigem atingir um limite mínimo de USDC." + "value" : "Reenviar código" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Os pagamentos ocorrem todos os domingos às 00:00 UTC e exigem atingir um limite mínimo de USDC." + "value" : "Reenviar código" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Выплаты происходят каждое воскресенье в 00:00 UTC и требуют достижения минимального порога USDC." + "value" : "Отправить код повторно" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Utbetalningar sker varje söndag kl. 00:00 UTC och kräver att en minsta USDC-gräns uppnås." + "value" : "Skicka kod igen" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "การจ่ายเงินเกิดขึ้นทุกวันอาทิตย์เวลา 00:00 UTC และต้องมีเกณฑ์ขั้นต่ำ USDC" + "value" : "ส่งโค้ดใหม่" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Виплати відбуваються щонеділі о 00:00 UTC і вимагають досягнення мінімального порогу USDC." + "value" : "Надіслати код повторно" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Thanh toán diễn ra vào mỗi Chủ Nhật lúc 00:00 UTC và yêu cầu đạt ngưỡng USDC tối thiểu." + "value" : "Gửi lại mã" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "每周日 00:00 UTC 进行支付,并且需要达到最低 USDC 门槛。" + "value" : "重新发送代码" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "每週日 00:00 UTC 進行支付,並且需要達到最低 USDC 門檻。" + "value" : "重新發送驗證碼" } } } }, - "Pending: %@ MB provided" : { + "Reset it." : { + "comment" : "Button text for resetting password", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "قيد الانتظار: تم توفير %@ ميغابايت" + "value" : "إعادة تعيينها" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Čekající: %@ MB poskytnuto" + "value" : "Resetujte si ho" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Ausstehend: %@ MB bereitgestellt" + "value" : "Zurücksetzen" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Σε εκκρεμότητα: παρέχονται %@ MB" + "value" : "Επαναφορά" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Pending: %@ MB provided" + "value" : "Reset it." } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Pendiente: %@ MB proporcionados" + "value" : "Restablecerla" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Pendiente: %@ MB proporcionados" + "value" : "Restablécelo" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Restablecerla" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "En attente : %@ MB fournis" + "value" : "Réinitialiser" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "ממתין: סופקו %@ מגה-בייט" + "value" : "אפס את זה" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "लंबित: %@ एमबी प्रदान किए गए" + "value" : "इसे रीसेट करें" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Tertunda: %@ MB disediakan" + "value" : "Atur ulang" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "In sospeso: %@ MB forniti" + "value" : "Reimpostalo" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "保留中: %@ MB 提供済み" + "value" : "リセットする" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "보류 중: %@ MB 제공됨" + "value" : "재설정" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "In behandeling: %@ geleverd" + "value" : "Reset het" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Oczekujące: dostarczono %@ MB" + "value" : "Zresetuj je" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Redefini-la" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Pendente: %@ MB fornecidos" + "value" : "Redefini-la" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Pendente: %@ MB fornecidos" + "value" : "Redefinir" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "В ожидании: предоставлено %@ МБ" + "value" : "Восстановить" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Väntar: %@ MB tillhandahållna" + "value" : "Återställ" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "รอดำเนินการ: ให้บริการ %@ MB" + "value" : "ตั้งใหม่เลยสิ" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Очікується: надано %@ МБ" + "value" : "Скинути" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Đang chờ xử lý: đã cung cấp %@ MB" + "value" : "Đặt lại" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "待处理:提供了 %@ MB" + "value" : "重置密码" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "待處理:提供了 %@ MB" + "value" : "重設它" } } } }, - "Phantom" : { - "localizations" : { - "cs" : { - "stringUnit" : { - "state" : "translated", - "value" : "Phantom" - } - }, - "nl" : { - "stringUnit" : { - "state" : "translated", - "value" : "Phantom" - } - } - }, - "shouldTranslate" : false - }, - "Placeholder" : { - "comment" : "Placeholder text for an empty text field.", - "isCommentAutoGenerated" : true - }, - "Plan" : { + "Resolvers" : { + "comment" : "A section header for the DNS resolvers.", + "isCommentAutoGenerated" : true, "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "الخطة" + "value" : "محلّلات DNS" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Plán" + "value" : "Resolvery" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Plan" + "value" : "Resolver" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Σχέδιο" + "value" : "Επιλυτές" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Plan" + "value" : "Resolvers" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Plan" + "value" : "Resolutores" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Plan" + "value" : "Resolutores" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Resolutores" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Forfait" + "value" : "Résolveurs" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "תוכנית" + "value" : "פותרי DNS" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "योजना" + "value" : "रिज़ॉल्वर" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Paket" + "value" : "Resolver" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Piano" + "value" : "Resolver" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "プラン" + "value" : "リゾルバー" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "플랜" + "value" : "리졸버" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Plan" + "value" : "Resolvers" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Plan" + "value" : "Resolvery" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Resolvedores" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Plano" + "value" : "Resolvedores" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Plano" + "value" : "Resolvedores" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "План" + "value" : "Резолверы" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Plan" + "value" : "Resolvrar" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "แผน" + "value" : "รีโซลเวอร์" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "План" + "value" : "Резолвери" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Kế hoạch" + "value" : "Bộ phân giải" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "计划" + "value" : "解析器" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "計劃" + "value" : "解析器" } } } }, - "Please install Phantom or Solflare to use this feature" : { + "Restore the most secure settings: encrypted DNS over HTTPS through the tunnel." : { + "comment" : "dns settings", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "يرجى تثبيت Phantom أو Solflare لاستخدام هذه الميزة" + "value" : "استعادة أكثر الإعدادات أمانًا: DNS مشفر عبر HTTPS من خلال النفق." } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Prosím nainstalujte peněženku/y Phantom či Solflare pro použité této featury." + "value" : "Obnovte nejbezpečnější nastavení: šifrovaný DNS přes HTTPS v tunelu." } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Bitte installiere Phantom oder Solflare, um diese Funktion zu nutzen" + "value" : "Stelle die sichersten Einstellungen wieder her: verschlüsseltes DNS über HTTPS durch den Tunnel." } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Παρακαλώ εγκαταστήστε το Phantom ή το Solflare για να χρησιμοποιήσετε αυτήν τη λειτουργία" + "value" : "Επαναφέρετε τις πιο ασφαλείς ρυθμίσεις: κρυπτογραφημένο DNS μέσω HTTPS μέσα από το τούνελ." } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Please install Phantom or Solflare to use this feature" + "value" : "Restore the most secure settings: encrypted DNS over HTTPS through the tunnel." } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Por favor, instala Phantom o Solflare para usar esta función" + "value" : "Restaura los ajustes más seguros: DNS sobre HTTPS cifrado a través del túnel." } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Por favor, instala Phantom o Solflare para usar esta función" + "value" : "Restaura la configuración más segura: DNS sobre HTTPS cifrado a través del túnel." + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Restaura la configuración más segura: DNS sobre HTTPS cifrado a través del túnel." } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Veuillez installer Phantom ou Solflare pour utiliser cette fonctionnalité" + "value" : "Rétablir les paramètres les plus sécurisés : DNS chiffré via HTTPS à travers le tunnel." } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "אנא התקן את Phantom או Solflare כדי להשתמש בתכונה זו" + "value" : "שחזור ההגדרות המאובטחות ביותר: DNS מוצפן דרך HTTPS במנהרה." } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "कृपया इस सुविधा का उपयोग करने के लिए Phantom या Solflare इंस्टॉल करें" + "value" : "सबसे सुरक्षित सेटिंग्स बहाल करें: टनल के ज़रिए HTTPS पर एन्क्रिप्टेड DNS।" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Silakan instal Phantom atau Solflare untuk menggunakan fitur ini" + "value" : "Pulihkan pengaturan paling aman: DNS melalui HTTPS terenkripsi lewat terowongan." } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Per favore, installa Phantom o Solflare per utilizzare questa funzionalità" + "value" : "Ripristina le impostazioni più sicure: DNS crittografato su HTTPS attraverso il tunnel." } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "この機能を使用するにはPhantomまたはSolflareをインストールしてください" + "value" : "最も安全な設定に戻します:トンネル経由の暗号化されたDNS over HTTPS。" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "이 기능을 사용하려면 Phantom 또는 Solflare를 설치하십시오" + "value" : "가장 안전한 설정으로 복원합니다: 터널을 통한 암호화된 DNS over HTTPS." } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Installeer Phantom of Solflare om deze functie te gebruiken" + "value" : "Herstel de veiligste instellingen: versleutelde DNS over HTTPS via de tunnel." } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Zainstaluj Phantom lub Solflare, aby korzystać z tej funkcji" + "value" : "Przywróć najbezpieczniejsze ustawienia: szyfrowany DNS przez HTTPS w tunelu." + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Repor as definições mais seguras: DNS sobre HTTPS encriptado através do túnel." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Instale o Phantom ou Solflare para usar este recurso" + "value" : "Restaurar as configurações mais seguras: DNS sobre HTTPS criptografado através do túnel." } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Instale o Phantom ou Solflare para usar esta funcionalidade" + "value" : "Repor as definições mais seguras: DNS sobre HTTPS encriptado através do túnel." } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Пожалуйста, установите Phantom или Solflare, чтобы использовать эту функцию" + "value" : "Восстановите самые безопасные настройки: шифрованный DNS через HTTPS в туннеле." } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Vänligen installera Phantom eller Solflare för att använda denna funktion" + "value" : "Återställ de säkraste inställningarna: krypterad DNS över HTTPS genom tunneln." } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "โปรดติดตั้ง Phantom หรือ Solflare เพื่อใช้คุณสมบัตินี้" + "value" : "คืนค่าการตั้งค่าที่ปลอดภัยที่สุด: DNS ที่เข้ารหัสผ่าน HTTPS ผ่านอุโมงค์" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Будь ласка, встановіть Phantom або Solflare, щоб використовувати цю функцію" + "value" : "Відновіть найбезпечніші налаштування: шифрований DNS через HTTPS у тунелі." } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Vui lòng cài đặt Phantom hoặc Solflare để sử dụng tính năng này" + "value" : "Khôi phục cài đặt an toàn nhất: DNS được mã hóa qua HTTPS thông qua đường hầm." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "请安装 Phantom 或 Solflare 以使用此功能" + "value" : "恢复最安全的设置:通过隧道使用加密的 DNS over HTTPS。" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "請安裝 Phantom 或 Solflare 以使用此功能" + "value" : "還原最安全的設定:透過隧道使用加密的 DNS over HTTPS。" } } } }, - "Please login to URnetwork" : { + "Restore to most secure settings" : { + "comment" : "dns settings", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "يرجى تسجيل الدخول إلى URnetwork" + "value" : "استعادة أكثر الإعدادات أمانًا" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Prosím přihlašte se do URnetwork" + "value" : "Obnovit nejbezpečnější nastavení" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Bitte melde dich bei URnetwork an" + "value" : "Sicherste Einstellungen wiederherstellen" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Παρακαλώ συνδεθείτε στο URnetwork" + "value" : "Επαναφορά στις πιο ασφαλείς ρυθμίσεις" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Please login to URnetwork" + "value" : "Restore to most secure settings" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Por favor, inicia sesión en URnetwork" + "value" : "Restaurar los ajustes más seguros" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Por favor, inicia sesión en URnetwork" + "value" : "Restaurar la configuración más segura" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Restaurar la configuración más segura" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Veuillez vous connecter à URnetwork" + "value" : "Rétablir les paramètres les plus sécurisés" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "אנא התחבר ל-URnetwork" + "value" : "שחזר להגדרות המאובטחות ביותר" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "कृपया URnetwork में लॉग इन करें" + "value" : "सबसे सुरक्षित सेटिंग्स बहाल करें" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Silakan masuk ke URnetwork" + "value" : "Pulihkan ke pengaturan paling aman" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Per favore, accedi a URnetwork" + "value" : "Ripristina le impostazioni più sicure" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "URnetworkにログインしてください" + "value" : "最も安全な設定に戻す" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork에 로그인하십시오" + "value" : "가장 안전한 설정으로 복원" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Log in op URnetwork" + "value" : "Veiligste instellingen herstellen" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Zaloguj się do URnetwork" + "value" : "Przywróć najbezpieczniejsze ustawienia" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Repor definições mais seguras" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Faça login no URnetwork" + "value" : "Restaurar configurações mais seguras" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Faça login no URnetwork" + "value" : "Repor definições mais seguras" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Пожалуйста, войдите в URnetwork" + "value" : "Вернуть самые безопасные настройки" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Vänligen logga in på URnetwork" + "value" : "Återställ till säkraste inställningarna" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "โปรดเข้าสู่ระบบ URnetwork" + "value" : "คืนค่าเป็นการตั้งค่าที่ปลอดภัยที่สุด" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Будь ласка, увійдіть до URnetwork" + "value" : "Повернути найбезпечніші налаштування" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Vui lòng đăng nhập vào URnetwork" + "value" : "Khôi phục cài đặt an toàn nhất" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "请登录到 URnetwork" + "value" : "恢复最安全设置" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "請登錄到 URnetwork" - } - } - } - }, - "Please login to URnetwork to connect" : { - "localizations" : { - "cs" : { - "stringUnit" : { - "state" : "translated", - "value" : "Pro připojení se přihlaste do URnetwork" - } - }, - "nl" : { - "stringUnit" : { - "state" : "translated", - "value" : "Log in op URnetwork om te verbinden" + "value" : "還原最安全設定" } } } }, - "Please try again." : { + "Retry" : { + "comment" : "Retry action label", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "يرجى المحاولة مرة أخرى." + "value" : "إعادة المحاولة" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Zkuste to prosím znovu." + "value" : "Zkusit znovu" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Bitte versuchen Sie es erneut." + "value" : "Erneut versuchen" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Παρακαλώ δοκιμάστε ξανά." + "value" : "Προσπαθήστε ξανά" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Please try again." + "value" : "Retry" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Por favor, inténtalo de nuevo." + "value" : "Reintentar" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Por favor, inténtalo de nuevo." + "value" : "Reintentar" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Reintentar" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Veuillez réessayer." + "value" : "Réessayer" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "אנא נסה שוב." + "value" : "נסה שוב" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "कृपया फिर से कोशिश करें।" + "value" : "पुनः प्रयास करें" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Silakan coba lagi." + "value" : "Coba lagi" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Per favore riprova." + "value" : "Riprova" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "もう一度お試しください。" + "value" : "再試行" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "다시 시도해 주세요." + "value" : "다시 시도" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Probeer het alstublieft opnieuw." + "value" : "Opnieuw proberen" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Proszę spróbować ponownie." + "value" : "Spróbuj ponownie" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tentar novamente" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Tente novamente, por favor." + "value" : "Tentar novamente" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Por favor, tente novamente." + "value" : "Tentar novamente" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Пожалуйста, попробуйте снова." + "value" : "Повторить попытку" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Försök igen, tack." + "value" : "Försök igen" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "โปรดลองอีกครั้ง" + "value" : "ลองอีกครั้ง" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Будь ласка, спробуйте ще раз." + "value" : "Повторити" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Vui lòng thử lại." + "value" : "Thử lại" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "请重试。" + "value" : "重试" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "請再試一次。" + "value" : "重試" } } } }, - "Points breakdown" : { + "Review URnetwork" : { + "comment" : "Used in Main/Account/AccountRootView.swift.", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "تفصيل النقاط" + "value" : "تقييم URnetwork" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Rozdělení bodů" + "value" : "Ohodnoďte URnetwork" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Punkteübersicht" + "value" : "URnetwork bewerten" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Ανάλυση πόντων" + "value" : "Αξιολογήστε το URnetwork" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Points breakdown" + "value" : "Review URnetwork" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Desglose de puntos" + "value" : "Valorar URnetwork" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Desglose de puntos" + "value" : "Calificar URnetwork" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Répartition des points" + "value" : "Évaluer URnetwork" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "פירוט נקודות" + "value" : "דרג את URnetwork" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "अंक विवरण" + "value" : "URnetwork की समीक्षा करें" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Rincian poin" + "value" : "Ulas URnetwork" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Dettaglio punti" + "value" : "Recensisci URnetwork" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "ポイントの内訳" + "value" : "URnetworkを評価" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "포인트 내역" + "value" : "URnetwork 리뷰하기" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Puntenoverzicht" + "value" : "Beoordeel URnetwork" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Podział punktów" + "value" : "Oceń URnetwork" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Detalhamento de pontos" + "value" : "Avaliar URnetwork" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Detalhe dos pontos" + "value" : "Avaliar URnetwork" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Разбивка баллов" + "value" : "Оценить URnetwork" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Poängfördelning" + "value" : "Recensera URnetwork" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "รายละเอียดคะแนน" + "value" : "รีวิว URnetwork" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Розподіл балів" + "value" : "Оцінити URnetwork" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Phân tích điểm" + "value" : "Đánh giá URnetwork" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "积分明细" + "value" : "评价 URnetwork" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "積分明細" + "value" : "評價 URnetwork" } } } }, - "Preorder Now" : { + "Routing activity appears here while connected." : { + "comment" : "A description of the content of the \"Activity\" section of the split rules view.", + "isCommentAutoGenerated" : true, "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "اطلب مسبقًا الآن" + "value" : "يظهر نشاط التوجيه هنا أثناء الاتصال." } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Předobjednat" + "value" : "Aktivita směrování se zde zobrazí po připojení." } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Jetzt vorbestellen" + "value" : "Routing-Aktivität erscheint hier, während du verbunden bist." } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Προπαραγγελία τώρα" + "value" : "Η δραστηριότητα δρομολόγησης εμφανίζεται εδώ όταν είστε συνδεδεμένοι." } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Preorder Now" + "value" : "Routing activity appears here while connected." } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Reservar ahora" + "value" : "La actividad de enrutamiento aparece aquí mientras estás conectado." } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Reservar ahora" + "value" : "La actividad de enrutamiento aparece aquí mientras estás conectado." + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "La actividad de enrutamiento aparece aquí mientras estás conectado." } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Précommander" + "value" : "L'activité de routage apparaît ici lorsque vous êtes connecté." } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "הזמן מראש" + "value" : "פעילות הניתוב מופיעה כאן בזמן החיבור." } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "अभी प्री-ऑर्डर करें" + "value" : "कनेक्ट होने पर रूटिंग गतिविधि यहाँ दिखाई देगी।" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Pesan Sekarang" + "value" : "Aktivitas perutean muncul di sini saat terhubung." } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Ordina ora" + "value" : "L'attività di instradamento appare qui mentre sei connesso." } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "今すぐ予約" + "value" : "接続中にルーティングのアクティビティがここに表示されます。" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "지금 예약 주문하기" + "value" : "연결 중에 라우팅 활동이 여기에 표시됩니다." } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Nu pre-orderen" + "value" : "Routeringsactiviteit verschijnt hier terwijl je verbonden bent." } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Zamów w przedsprzedaży" + "value" : "Aktywność routingu pojawi się tutaj po połączeniu." + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "A atividade de encaminhamento aparece aqui enquanto estiver ligado." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Compre na pré-venda" + "value" : "A atividade de roteamento aparece aqui enquanto você estiver conectado." } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Pré-encomendar agora" + "value" : "A atividade de encaminhamento aparece aqui enquanto estiver ligado." } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Предзаказ" + "value" : "Активность маршрутизации появится здесь при подключении." } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Förbeställ nu" + "value" : "Dirigeringsaktivitet visas här när du är ansluten." } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "สั่งซื้อล่วงหน้า" + "value" : "กิจกรรมการกำหนดเส้นทางจะแสดงที่นี่ขณะเชื่อมต่อ" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Передзамовити зараз" + "value" : "Активність маршрутизації з'явиться тут під час підключення." } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Đặt hàng trước ngay" + "value" : "Hoạt động định tuyến xuất hiện ở đây khi đang kết nối." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "立即预购" + "value" : "连接后,路由活动会显示在这里。" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "立即預購" + "value" : "連接後,路由活動會在此顯示。" } } } }, - "Private Network" : { - "comment" : "Network name when privacy is enabled", + "Rules" : { + "comment" : "A heading for the pinned split rules.", + "isCommentAutoGenerated" : true, "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "شبكة خاصة" + "value" : "القواعد" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Soukromá sít" + "value" : "Pravidla" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Privates Netzwerk" + "value" : "Regeln" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Ιδιωτικό δίκτυο" + "value" : "Κανόνες" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rules" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Red privada" + "value" : "Reglas" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Red privada" + "value" : "Reglas" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Reglas" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Réseau privé" + "value" : "Règles" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "רשת פרטית" + "value" : "כללים" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "निजी नेटवर्क" + "value" : "नियम" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Jaringan Pribadi" + "value" : "Aturan" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Rete privata" + "value" : "Regole" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "プライベートネットワーク" + "value" : "ルール" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "사설망" + "value" : "규칙" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Privénetwerk" + "value" : "Regels" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Sieć prywatna" + "value" : "Reguły" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Regras" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Rede privada" + "value" : "Regras" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Rede privada" + "value" : "Regras" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Частная сеть" + "value" : "Правила" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Privat nätverk" + "value" : "Regler" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "เครือข่ายส่วนตัว" + "value" : "กฎ" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Приватна мережа" + "value" : "Правила" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Mạng riêng tư" + "value" : "Quy tắc" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "专用网络" + "value" : "规则" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "私人網絡" + "value" : "規則" } } } }, - "Processing subscription balance..." : { + "Save" : { + "comment" : "Profile", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "جارٍ معالجة رصيد الاشتراك..." + "value" : "حفظ" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Zpracování zůstatku předplatného..." + "value" : "Uložit" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Abonnementguthaben wird verarbeitet..." + "value" : "Speichern" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Επεξεργασία υπολοίπου συνδρομής..." + "value" : "Αποθήκευση" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Processing subscription balance..." + "value" : "Save" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Procesando el saldo de la suscripción..." + "value" : "Guardar" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Procesando el saldo de la suscripción..." + "value" : "Guardar" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Guardar" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Traitement du solde de l'abonnement..." + "value" : "Enregistrer" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "מעבד את יתרת המנוי..." + "value" : "שמור" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "सदस्यता बैलेंस संसाधित किया जा रहा है..." + "value" : "सहेजें" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Memproses saldo langganan..." + "value" : "Simpan" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Elaborazione del saldo dell'abbonamento..." + "value" : "Salva" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "サブスクリプション残高を処理中..." + "value" : "保存" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "구독 잔액 처리 중..." + "value" : "저장" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Abonnementsaldo wordt verwerkt..." + "value" : "Opslaan" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Przetwarzanie salda subskrypcji..." + "value" : "Zapisz" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Salvar" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Processando saldo da assinatura..." + "value" : "Salvar" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "A processar saldo da subscrição..." + "value" : "Guardar" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Обработка баланса подписки..." + "value" : "Сохранить" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Bearbetar prenumerationssaldo..." + "value" : "Spara" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "กำลังประมวลผลยอดเงินสำหรับการสมัครสมาชิก..." + "value" : "บันทึก" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Обробка балансу підписки..." + "value" : "Зберегти" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Đang xử lý số dư đăng ký..." + "value" : "Lưu" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "正在处理订阅余额..." + "value" : "保存" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "正在處理訂閱餘額..." + "value" : "儲存" } } } }, - "Profile" : { + "Save Logs" : { + "comment" : "Used in Shared/Views/ExportLogsButton/ExportLogsButton.swift.", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "الملف الشخصي" + "value" : "حفظ السجلات" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Profil" + "value" : "Uložit logy" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Profil" + "value" : "Logs speichern" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Προφίλ" + "value" : "Αποθήκευση αρχείων καταγραφής" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Profile" + "value" : "Save Logs" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Perfil" + "value" : "Guardar registros" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Perfil" + "value" : "Guardar registros" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Profil" + "value" : "Enregistrer les journaux" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "פרופיל" + "value" : "שמור לוגים" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "प्रोफ़ाइल" + "value" : "लॉग्स सहेजें" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Profil" + "value" : "Simpan log" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Profilo" + "value" : "Salva log" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "プロフィール" + "value" : "ログを保存" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "프로필" + "value" : "로그 저장" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Profiel" + "value" : "Logbestanden opslaan" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Profil" + "value" : "Zapisz logi" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Perfil" + "value" : "Salvar logs" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Perfil" + "value" : "Guardar logs" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Профиль" + "value" : "Сохранить логи" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Profil" + "value" : "Spara loggar" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "โปรไฟล์" + "value" : "บันทึกล็อก" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Профіль" + "value" : "Зберегти логи" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Hồ sơ" + "value" : "Lưu nhật ký" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "个人资料" + "value" : "保存日志" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "個人資料" + "value" : "儲存日誌" } } } }, - "Promoted Locations" : { - "comment" : "A header for a section of the provider list that shows recommended locations.", - "isCommentAutoGenerated" : true - }, - "Provide mode" : { + "Search providers" : { + "comment" : "Search providers input label", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "وضع التوفير" + "value" : "البحث عن مزودي الخدمة" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vyhledání poskytovatelů" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Bereitstellungsmodus" + "value" : "Anbieter suchen" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Λειτουργία παροχής" + "value" : "Αναζήτηση παρόχων" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Provide mode" + "value" : "Search providers" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Modo de provisión" + "value" : "Buscar proveedores" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Modo de provisión" + "value" : "Buscar proveedores" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Buscar proveedores" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Mode de fourniture" + "value" : "Rechercher des fournisseurs" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "מצב אספקה" + "value" : "חפש ספקים" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "प्रदान मोड" + "value" : "प्रदाता खोजें" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Mode penyediaan" + "value" : "Cari penyedia" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Modalità di fornitura" + "value" : "Cerca provider" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "提供モード" + "value" : "プロバイダーを検索" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "제공 모드" + "value" : "공급자 검색" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "aanlevermodus" + "value" : "Zoek providers" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Szukaj dostawców" } }, - "pl" : { + "pt" : { "stringUnit" : { "state" : "translated", - "value" : "Tryb udostępniania" + "value" : "Pesquisar provedores" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Modo de fornecimento" + "value" : "Buscar provedores" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Modo de fornecimento" + "value" : "Pesquisar fornecedores" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Режим предоставления" + "value" : "Поиск провайдеров" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Tillhandahållandeläge" + "value" : "Sök leverantörer" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "โหมดการให้บริการ" + "value" : "ค้นหาผู้ให้บริการ" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Режим надання" + "value" : "Пошук провайдерів" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Chế độ cung cấp" + "value" : "Tìm nhà cung cấp" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "提供模式" + "value" : "搜索供应商" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "提供模式" + "value" : "搜尋供應商" } } } }, - "Provider contracts" : { - "comment" : "A label for the provider contracts screen.", - "isCommentAutoGenerated" : true - }, - "Provider statistics" : { - "comment" : "Title of a section that shows statistics about the provider.", - "isCommentAutoGenerated" : true - }, - "Providing" : { + "Seeker Token Verified!" : { + "comment" : "Call to action", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "جاري التوفير" + "value" : "تم التحقق من رمز Seeker!" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Poskytování" + "value" : "Token Seeker ověřen!" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Bereitstellung" + "value" : "Seeker-Token verifiziert!" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Παροχή" + "value" : "Το διακριτικό Seeker επαληθεύτηκε!" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Seeker Token Verified!" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Proporcionando" + "value" : "¡Token Seeker verificado!" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Proporcionando" + "value" : "¡Token Seeker verificado!" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "¡Token Seeker verificado!" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Fourniture" + "value" : "Jeton Seeker vérifié !" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "מספק" + "value" : "אסימון Seeker אומת!" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "प्रदान कर रहा है" + "value" : "Seeker टोकन सत्यापित हुआ!" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Menyediakan" + "value" : "Token Seeker Terverifikasi!" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Fornendo" + "value" : "Token Seeker verificato!" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "提供中" + "value" : "Seekerトークンが認証されました!" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "제공 중" + "value" : "Seeker 토큰이 인증되었습니다!" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Verstrekken" + "value" : "Seeker Token geverifieerd!" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Udostępnianie" + "value" : "Token Seeker zweryfikowany!" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Token Seeker verificado!" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Fornecendo" + "value" : "Token Seeker verificado!" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "A fornecer" + "value" : "Token Seeker verificado!" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Предоставление" + "value" : "Токен Seeker проверен!" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Tillhandahåller" + "value" : "Seeker-token verifierat!" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "กำลังให้บริการ" + "value" : "ยืนยัน Seeker Token สำเร็จ" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Надання" + "value" : "Токен Seeker перевірено!" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Đang cung cấp" + "value" : "Đã xác minh Mã thông báo Seeker!" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "提供中" + "value" : "Seeker 令牌已验证" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "提供中" + "value" : "Seeker 權杖已驗證!" } } } }, - "Providing is disabled" : { - "comment" : "A message displayed when the user is not providing.", - "isCommentAutoGenerated" : true - }, - "Providing is off by default. Choose when your device provides below." : { - "comment" : "A description of the user's ability to provide bandwidth.", - "isCommentAutoGenerated" : true - }, - "Providing is optional. When you turn it on, the app can share your connection with the network while it runs in the background on Wi-Fi." : { - "comment" : "A description of the benefits of providing.", - "isCommentAutoGenerated" : true - }, - "Quit URnetwork" : { + "Select country to block" : { + "comment" : "Used in Main/Account/BlockedLocations/BlockedLocationsView.swift.", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "إغلاق URnetwork" + "value" : "حدد الدولة للحظر" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Opustit URnetwork" + "value" : "Vyberte zemi k blokování" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork beenden" + "value" : "Land zum Blockieren auswählen" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Έξοδος από το URnetwork" + "value" : "Επιλέξτε χώρα για αποκλεισμό" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Quit URnetwork" + "value" : "Select country to block" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Salir de URnetwork" + "value" : "Selecciona el país para bloquear" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Salir de URnetwork" + "value" : "Selecciona el país para bloquear" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Quitter URnetwork" + "value" : "Sélectionnez le pays à bloquer" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "צא מ-URnetwork" + "value" : "בחר מדינה לחסימה" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork से बाहर निकलें" + "value" : "ब्लॉक करने के लिए देश चुनें" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Keluar dari URnetwork" + "value" : "Pilih negara untuk diblokir" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Chiudi URnetwork" + "value" : "Seleziona il paese da bloccare" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "URnetworkを終了" + "value" : "ブロックする国を選択" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork 종료" + "value" : "차단할 국가 선택" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Stop URnetwork" + "value" : "Selecteer land om te blokkeren" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Zakończ URnetwork" + "value" : "Wybierz kraj do zablokowania" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Sair do URnetwork" + "value" : "Selecione o país para bloquear" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Sair do URnetwork" + "value" : "Selecione o país para bloquear" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Выйти из URnetwork" + "value" : "Выберите страну для блокировки" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Avsluta URnetwork" + "value" : "Välj land att blockera" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "ออกจาก URnetwork" + "value" : "เลือกประเทศที่จะบล็อก" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Вийти з URnetwork" + "value" : "Виберіть країну для блокування" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Thoát URnetwork" + "value" : "Chọn quốc gia để chặn" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "退出 URnetwork" + "value" : "选择要屏蔽的国家" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "退出 URnetwork" + "value" : "選擇要屏蔽的國家" } } } }, - "Races a local resolver while the tunnel starts. When off, DNS only resolves through the tunnel." : { - "comment" : "A description of the local DNS fallback setting.", - "isCommentAutoGenerated" : true - }, - "Ready to connect" : { + "Selected hosts are routed locally and bypass the tunnel." : { + "comment" : "A description of the functionality of the split rule editor.", + "isCommentAutoGenerated" : true, "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "جاهز للاتصال" + "value" : "يتم توجيه المضيفين المحددين محليًا لتجاوز النفق." } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Připraveno k připojení" + "value" : "Vybraní hostitelé jsou směrováni lokálně a obcházejí tunel." } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Bereit zum Verbinden" + "value" : "Ausgewählte Hosts werden lokal geleitet und umgehen den Tunnel." } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Έτοιμο για σύνδεση" + "value" : "Οι επιλεγμένοι hosts δρομολογούνται τοπικά και παρακάμπτουν το τούνελ." } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Ready to connect" + "value" : "Selected hosts are routed locally and bypass the tunnel." } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Listo para conectar" + "value" : "Los hosts seleccionados se enrutan localmente y evitan el túnel." } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Listo para conectar" + "value" : "Los hosts seleccionados se enrutan localmente y evitan el túnel." + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Los hosts seleccionados se enrutan localmente y evitan el túnel." } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Prêt à se connecter" + "value" : "Les hôtes sélectionnés sont routés localement et contournent le tunnel." } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "מוכן להתחבר" + "value" : "המארחים שנבחרו מנותבים מקומית ועוקפים את המנהרה." } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "कनेक्ट करने के लिए तैयार" + "value" : "चयनित होस्ट स्थानीय रूप से रूट होते हैं और टनल को बायपास करते हैं।" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Siap terhubung" + "value" : "Host yang dipilih dirutekan secara lokal dan melewati terowongan." } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Pronto per connettersi" + "value" : "Gli host selezionati vengono instradati localmente e bypassano il tunnel." } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "接続準備完了" + "value" : "選択したホストはローカルにルーティングされ、トンネルを迂回します。" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "연결 준비 완료" + "value" : "선택한 호스트는 터널을 우회하여 로컬로 라우팅됩니다." } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Klaar om te verbinden" + "value" : "Geselecteerde hosts worden lokaal gerouteerd en omzeilen de tunnel." } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Gotowy do połączenia" + "value" : "Wybrane hosty są kierowane lokalnie i omijają tunel." + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Os hosts selecionados são encaminhados localmente e contornam o túnel." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Pronto para conectar" + "value" : "Os hosts selecionados são roteados localmente e contornam o túnel." } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Pronto para conectar" + "value" : "Os hosts selecionados são encaminhados localmente e contornam o túnel." } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Готов к подключению" + "value" : "Выбранные хосты маршрутизируются локально и обходят туннель." } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Redo att ansluta" + "value" : "Valda värdar dirigeras lokalt och går förbi tunneln." } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "พร้อมที่จะเชื่อมต่อ" + "value" : "โฮสต์ที่เลือกจะถูกส่งผ่านภายในเครื่องและไม่ผ่านอุโมงค์" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Готовий до підключення" + "value" : "Вибрані хости маршрутизуються локально та обходять тунель." } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Sẵn sàng kết nối" + "value" : "Các host đã chọn được định tuyến cục bộ và không qua đường hầm." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "准备连接" + "value" : "选中的主机将走本地路由,绕过隧道。" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "準備連接" + "value" : "已選主機將走本地路由,繞過隧道。" } } } }, - "Receive connection notifications" : { + "Send" : { + "comment" : "Call to action", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "تلقي إشعارات الاتصال" + "value" : "أرسل" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Přijímání oznámení o připojení" + "value" : "Odeslat" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Verbindungsbenachrichtigungen erhalten" + "value" : "Sende" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Λήψη ειδοποιήσεων σύνδεσης" + "value" : "Αποστολή" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Receive connection notifications" + "value" : "Send" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Recibir notificaciones de conexión" + "value" : "Envía" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Recibir notificaciones de conexión" + "value" : "Enviar" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Envía" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Recevoir des notifications de connexion" + "value" : "Envoie" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "קבל התראות חיבור" + "value" : "שלח" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "कनेक्शन सूचनाएं प्राप्त करें" + "value" : "भेजें" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Terima notifikasi koneksi" + "value" : "Kirim" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Ricevi notifiche di connessione" + "value" : "Invia" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "接続通知を受け取る" + "value" : "送信" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "연결 알림 받기" + "value" : "보내기" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Ontvang verbindingsmeldingen" + "value" : "Verstuur" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Otrzymuj powiadomienia o połączeniu" + "value" : "Wyślij" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Envie" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Receber notificações de conexão" + "value" : "Enviar" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Receber notificações de conexão" + "value" : "Enviar" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Получать уведомления о подключении" + "value" : "Отправить" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Ta emot anslutningsaviseringar" + "value" : "Skicka" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "รับการแจ้งเตือนการเชื่อมต่อ" + "value" : "ส่ง" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Отримувати сповіщення про підключення" + "value" : "Надіслати" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Nhận thông báo kết nối" + "value" : "Gửi" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "接收连接通知" + "value" : "发送" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "接收連接通知" + "value" : "發送" } } } }, - "Reconnect" : { + "Send me product updates" : { + "comment" : "Settings", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "إعادة الاتصال" + "value" : "إرسال تحديثات المنتج لي" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Přepojení" + "value" : "Zasílání aktualizací produktů" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Neu verbinden" + "value" : "Sende mir Produkt-Updates" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Επανασύνδεση" + "value" : "Στείλτε μου ενημερώσεις προϊόντων" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Reconnect" + "value" : "Send me product updates" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Reconectar" + "value" : "Enviarme actualizaciones de productos" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Reconectar" + "value" : "Enviarme actualizaciones del producto" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enviarme actualizaciones de productos" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Reconnecter" + "value" : "M'envoyer des mises à jour sur le produit" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "התחבר מחדש" + "value" : "שלח לי עדכוני מוצר" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "पुनः कनेक्ट करें" + "value" : "मुझे उत्पाद अपडेट भेजें" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Sambung ulang" + "value" : "Kirim pembaruan produk" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Riconnetti" + "value" : "Inviami aggiornamenti sul prodotto" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "再接続" + "value" : "製品のアップデートを送信する" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "다시 연결" + "value" : "제품 업데이트 소식 받기" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Opnieuw verbinden" + "value" : "Stuur mij productupdates" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Połącz ponownie" + "value" : "Wysyłaj mi aktualizacje produktu" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enviar atualizações de produtos" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Reconectar" + "value" : "Enviar atualizações de produtos" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Reconectar" + "value" : "Enviar-me atualizações do produto" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Переподключиться" + "value" : "Присылайте мне обновления продукта" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Återanslut" + "value" : "Skicka produktuppdateringar" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "เชื่อมต่อใหม่" + "value" : "ส่งข้อมูลการอัพเดตผลิตภัณฑ์มาที่ฉัน" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Перепідключитися" + "value" : "Надсилати оновлення продукту" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Kết nối lại" + "value" : "Gửi cập nhật sản phẩm cho tôi" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "重新连接" + "value" : "向我发送产品更新" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "重新連接" + "value" : "向我發送產品更新" } } } }, - "Redeem" : { + "Send reset link" : { + "comment" : "Used in Authenticate/ResetPassword/ResetPasswordView.swift.", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "استرداد" + "value" : "إرسال رابط إعادة التعيين" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Uplatnit" + "value" : "Odeslat odkaz na obnovení" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Einlösen" + "value" : "Reset-Link senden" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Εξαργύρωση" + "value" : "Αποστολή συνδέσμου επαναφοράς" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Redeem" + "value" : "Send reset link" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Canjear" + "value" : "Enviar enlace de restablecimiento" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Canjear" + "value" : "Enviar enlace de restablecimiento" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Utiliser" + "value" : "Envoyer le lien de réinitialisation" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "ממש" + "value" : "שלח קישור לאיפוס" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "रिडीम करें" + "value" : "रीसेट लिंक भेजें" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Tukarkan" + "value" : "Kirim tautan reset" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Riscatta" + "value" : "Invia link di reset" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "引き換え" + "value" : "リセットリンクを送信" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "사용" + "value" : "재설정 링크 보내기" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Inwisselen" + "value" : "Resetlink versturen" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Wykorzystaj" + "value" : "Wyślij link do resetowania" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Resgatar" + "value" : "Enviar link de redefinição" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Resgatar" + "value" : "Enviar link de reposição" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Активировать" + "value" : "Отправить ссылку для сброса" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Lös in" + "value" : "Skicka återställningslänk" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "แลกรับ" + "value" : "ส่งลิงก์รีเซ็ต" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Активувати" + "value" : "Надіслати посилання для скидання" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Đổi mã" + "value" : "Gửi liên kết đặt lại" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "兑换" + "value" : "发送重置链接" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "兌換" + "value" : "發送重設連結" } } } }, - "Redeem Balance Code" : { + "Send us your feedback directly or [join our Discord](https://discord.com/invite/RUNZXMwPRK) for direct support." : { + "comment" : "Used in Main/Feedback/FeedbackView.swift.", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "استرداد رمز الرصيد" + "value" : "أرسل لنا ملاحظاتك مباشرة أو [انضم إلى Discord الخاص بنا](https://discord.com/invite/RUNZXMwPRK) للحصول على دعم مباشر." } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Uplatnit kód na zůstatek" + "value" : "Pošlete nám svou zpětnou vazbu přímo nebo [se připojte k našemu Discordu](https://discord.com/invite/RUNZXMwPRK) pro přímou podporu." } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Guthabencode einlösen" + "value" : "Senden Sie uns Ihr Feedback direkt oder [treten Sie unserem Discord bei](https://discord.com/invite/RUNZXMwPRK) für direkten Support." } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Εξαργύρωση κωδικού υπολοίπου" + "value" : "Στείλτε μας τα σχόλιά σας απευθείας ή [εγγραφείτε στο Discord μας](https://discord.com/invite/RUNZXMwPRK) για άμεση υποστήριξη." } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Redeem Balance Code" + "value" : "Send us your feedback directly or [join our Discord](https://discord.com/invite/RUNZXMwPRK) for direct support." } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Canjear código de saldo" + "value" : "Envíanos tus comentarios directamente o [únete a nuestro Discord](https://discord.com/invite/RUNZXMwPRK) para obtener soporte directo." } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Canjear código de saldo" + "value" : "Envíanos tus comentarios directamente o [únete a nuestro Discord](https://discord.com/invite/RUNZXMwPRK) para obtener soporte directo." } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Utiliser le code de solde" + "value" : "Envoyez-nous vos commentaires directement ou [rejoignez notre Discord](https://discord.com/invite/RUNZXMwPRK) pour un support direct." } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "ממש קוד יתרה" + "value" : "שלח לנו משוב ישירות או [הצטרף לדיסקורד שלנו](https://discord.com/invite/RUNZXMwPRK) לתמיכה ישירה." } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "बैलेंस कोड रिडीम करें" + "value" : "अपनी प्रतिक्रिया सीधे हमें भेजें या सीधे सहायता के लिए [हमारे Discord से जुड़ें](https://discord.com/invite/RUNZXMwPRK)।" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Tukarkan kode saldo" + "value" : "Kirimkan masukan Anda langsung atau [bergabunglah dengan Discord kami](https://discord.com/invite/RUNZXMwPRK) untuk dukungan langsung." } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Riscatta codice saldo" + "value" : "Inviaci direttamente i tuoi feedback o [unisciti al nostro Discord](https://discord.com/invite/RUNZXMwPRK) per un supporto diretto." } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "残高コードを引き換え" + "value" : "フィードバックを直接送信するか、直接サポートを受けるために[Discordに参加](https://discord.com/invite/RUNZXMwPRK)してください。" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "잔액 코드 사용" + "value" : "직접 피드백을 보내거나 직접 지원을 받으려면 [Discord에 가입](https://discord.com/invite/RUNZXMwPRK)하세요." } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Saldo code inwisselen" + "value" : "Stuur on direct je feedback of word lid van onze [Discord](https://discord.com/invite/RUNZXMwPRK) voor directe ondersteuning." } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Wykorzystaj kod salda" + "value" : "Wyślij nam swoją opinię bezpośrednio lub [dołącz do naszego Discorda](https://discord.com/invite/RUNZXMwPRK), aby uzyskać bezpośrednie wsparcie." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Resgatar código de saldo" + "value" : "Envie seu feedback diretamente ou [junte-se ao nosso Discord](https://discord.com/invite/RUNZXMwPRK) para suporte direto." } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Resgatar código de saldo" + "value" : "Envie-nos o seu feedback diretamente ou [junte-se ao nosso Discord](https://discord.com/invite/RUNZXMwPRK) para suporte direto." } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Активировать код баланса" + "value" : "Отправьте нам свой отзыв напрямую или [присоединитесь к нашему Discord](https://discord.com/invite/RUNZXMwPRK) для прямой поддержки." } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Lös in saldo-kod" + "value" : "Skicka din feedback direkt till oss eller [gå med i vår Discord](https://discord.com/invite/RUNZXMwPRK) för direkt support." } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "แลกรหัสยอดเงิน" + "value" : "ส่งความคิดเห็นของคุณให้เราโดยตรงหรือ[เข้าร่วม Discord ของเรา](https://discord.com/invite/RUNZXMwPRK) เพื่อรับการสนับสนุนโดยตรง" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Активувати код балансу" + "value" : "Надішліть нам свій відгук безпосередньо або [приєднайтеся до нашого Discord](https://discord.com/invite/RUNZXMwPRK) для прямої підтримки." } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Đổi mã số dư" + "value" : "Gửi phản hồi của bạn trực tiếp cho chúng tôi hoặc [tham gia Discord của chúng tôi](https://discord.com/invite/RUNZXMwPRK) để được hỗ trợ trực tiếp." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "兑换余额码" + "value" : "直接向我们发送反馈,或[加入我们的 Discord](https://discord.com/invite/RUNZXMwPRK) 获取直接支持。" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "兌換餘額碼" + "value" : "直接向我們發送反饋,或[加入我們的 Discord](https://discord.com/invite/RUNZXMwPRK) 獲取直接支持。" } } } }, - "Redeem Code" : { + "Sent! Thanks for your feedback." : { + "comment" : "Snackbar shown after feedback is sent successfully. Used in Main/Feedback/FeedbackView.swift.", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "استرداد الرمز" + "value" : "تم الإرسال! شكرًا على ملاحظاتك." } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Uplatnit kód" + "value" : "Odesláno! Děkujeme za zpětnou vazbu." } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Code einlösen" + "value" : "Gesendet! Danke für dein Feedback." } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Εξαργύρωση κωδικού" + "value" : "Στάλθηκε! Ευχαριστούμε για τα σχόλιά σας." } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Redeem Code" + "value" : "Sent! Thanks for your feedback." } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Canjear código" + "value" : "¡Enviado! Gracias por tus comentarios." } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Canjear código" + "value" : "¡Enviado! Gracias por tus comentarios." + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "¡Enviado! Gracias por tus comentarios." } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Utiliser le code" + "value" : "Envoyé ! Merci pour votre retour." } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "ממש קוד" + "value" : "נשלח! תודה על המשוב." } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "कोड रिडीम करें" + "value" : "भेज दिया गया! प्रतिक्रिया के लिए धन्यवाद।" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Tukarkan kode" + "value" : "Terkirim! Terima kasih atas umpan balik Anda." } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Riscatta codice" + "value" : "Inviato! Grazie per il feedback." } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "コードを引き換え" + "value" : "送信しました!フィードバックありがとうございます。" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "코드 사용" + "value" : "보냈습니다! 피드백 감사합니다." } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Code inwisselen" + "value" : "Verzonden! Bedankt voor je feedback." } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Wykorzystaj kod" + "value" : "Wysłano! Dziękujemy za opinię." + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enviado! Obrigado pelo feedback." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Resgatar código" + "value" : "Enviado! Obrigado pelo feedback." } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Resgatar código" + "value" : "Enviado! Obrigado pelo feedback." } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Активировать код" + "value" : "Отправлено! Спасибо за отзыв." } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Lös in kod" + "value" : "Skickat! Tack för din feedback." } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "แลกรหัส" + "value" : "ส่งแล้ว! ขอบคุณสำหรับข้อเสนอแนะ" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Активувати код" + "value" : "Надіслано! Дякуємо за відгук." } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Đổi mã" + "value" : "Đã gửi! Cảm ơn phản hồi của bạn." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "兑换码" + "value" : "已发送!感谢您的反馈。" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "兌換碼" + "value" : "已發送!感謝您的反饋。" } } } }, - "Redeemed" : { + "Settings" : { + "comment" : "Account Screen", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "تم الاسترداد" + "value" : "الإعدادات" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Uplatněno" + "value" : "Nastavení" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Eingelöst" + "value" : "Einstellungen" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Εξαργυρώθηκε" + "value" : "Ρυθμίσεις" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Redeemed" + "value" : "Settings" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Canjeado" + "value" : "Ajustes" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Canjeado" + "value" : "Configuración" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ajustes" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Utilisé" + "value" : "Paramètres" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "מומש" + "value" : "הגדרות" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "रिडीम किया गया" + "value" : "सेटिंग" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Ditukarkan" + "value" : "Pengaturan" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Riscattato" + "value" : "Impostazioni" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "引き換え済み" + "value" : "設定" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "사용됨" + "value" : "설정" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Ingewisseld" + "value" : "Instellingen" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Wykorzystano" + "value" : "Ustawienia" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configurações" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Resgatado" + "value" : "Configurações" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Resgatado" + "value" : "Definições" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Активирован" + "value" : "Настройки" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Inlöst" + "value" : "Inställningar" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "แลกแล้ว" + "value" : "การตั้งค่า" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Активовано" + "value" : "Налаштування" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Đã đổi" + "value" : "Cài đặt" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "已兑换" + "value" : "设置" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "已兌換" + "value" : "設定" } } } }, - "Refer a friend" : { + "Share Logs" : { + "comment" : "Used in Shared/Views/ExportLogsButton/ExportLogsButton.swift.", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "أوصِ صديقًا" + "value" : "مشاركة السجلات" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Doporučte přítele" + "value" : "Sdílet logy" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Freund empfehlen" + "value" : "Logs teilen" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Συστήστε έναν φίλο" + "value" : "Κοινοποίηση αρχείων καταγραφής" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Refer a friend" + "value" : "Share Logs" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Recomienda a un amigo" + "value" : "Compartir registros" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Recomienda a un amigo" + "value" : "Compartir registros" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Parrainer un ami" + "value" : "Partager les journaux" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "המלץ על חבר" + "value" : "שתף לוגים" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "मित्र को रेफ़र करें" + "value" : "लॉग्स साझा करें" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Referensikan teman" + "value" : "Bagikan log" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Invita un amico" + "value" : "Condividi log" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "友達を紹介する" + "value" : "ログを共有" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "친구 추천" + "value" : "로그 공유" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Vriend doorverwijzen" + "value" : "Logbestanden delen" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Poleć znajomego" + "value" : "Udostępnij logi" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Indique um amigo" + "value" : "Compartilhar logs" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Recomendar um amigo" + "value" : "Partilhar logs" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Порекомендовать друга" + "value" : "Поделиться логами" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Tipsa en vän" + "value" : "Dela loggar" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "แนะนำเพื่อน" + "value" : "แชร์ล็อก" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Порекомендувати друга" + "value" : "Поділитися логами" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Giới thiệu bạn bè" + "value" : "Chia sẻ nhật ký" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "推荐朋友" + "value" : "分享日志" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "推薦朋友" + "value" : "分享日誌" } } } }, - "Refer a friend = +30 GiB/Month" : { + "Share URnetwork" : { + "comment" : "Used in Shared/Views/AccountMenu.swift.", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "أوصِ صديقًا = +30 غيغابايت/شهر" + "value" : "مشاركة URnetwork" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Doporučte přítele = +30 GiB/měsíc" + "value" : "Sdílet URnetwork" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Freund empfehlen = +30 GiB/Monat" + "value" : "URnetwork teilen" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Συστήστε έναν φίλο = +30 GiB/μήνα" + "value" : "Κοινοποίηση URnetwork" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Refer a friend = +30 GiB/Month" + "value" : "Share URnetwork" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Recomienda a un amigo = +30 GiB/mes" + "value" : "Compartir URnetwork" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Recomienda a un amigo = +30 GiB/mes" + "value" : "Compartir URnetwork" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Parrainer un ami = +30 Go/mois" + "value" : "Partager URnetwork" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "המלץ על חבר = +30 GiB/חודש" + "value" : "שתף URnetwork" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "मित्र को रेफ़र करें = +30 GiB/माह" + "value" : "URnetwork शेयर करें" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Referensikan teman = +30 GiB/bulan" + "value" : "Bagikan URnetwork" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Invita un amico = +30 GiB/mese" + "value" : "Condividi URnetwork" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "友達を紹介 = +30 GiB/月" + "value" : "URnetworkを共有" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "친구 추천 = +30 GiB/월" + "value" : "URnetwork 공유" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Vriend doorverwijzen = +30 GiB/maand" + "value" : "Deel URnetwork" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Poleć znajomego = +30 GiB/miesiąc" + "value" : "Udostępnij URnetwork" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Indique um amigo = +30 GiB/mês" + "value" : "Compartilhar URnetwork" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Recomendar um amigo = +30 GiB/mês" + "value" : "Partilhar URnetwork" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Порекомендовать друга = +30 ГиБ/месяц" + "value" : "Поделиться URnetwork" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Tipsa en vän = +30 GiB/månad" + "value" : "Dela URnetwork" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "แนะนำเพื่อน = +30 GiB/เดือน" + "value" : "แชร์ URnetwork" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Порекомендувати друга = +30 ГіБ/місяць" + "value" : "Поділитися URnetwork" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Giới thiệu bạn bè = +30 GiB/tháng" + "value" : "Chia sẻ URnetwork" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "推荐朋友 = +30 GiB/月" + "value" : "分享 URnetwork" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "推薦朋友 = +30 GiB/月" + "value" : "分享 URnetwork" } } } }, - "Refer friends" : { + "Show" : { + "comment" : "Used in Main/Connect/ConnectView-macOS.swift.", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "أوصِ أصدقاء" + "value" : "إظهار" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Doporučte přátele" + "value" : "Ukázat" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Freunde empfehlen" + "value" : "Anzeigen" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Συστήστε φίλους" + "value" : "Εμφάνιση" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Refer friends" + "value" : "Show" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Recomienda amigos" + "value" : "Mostrar" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Recomienda amigos" + "value" : "Mostrar" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Parrainer des amis" + "value" : "Afficher" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "המלץ על חברים" + "value" : "הצג" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "मित्रों को रेफ़र करें" + "value" : "दिखाएं" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Referensikan teman" + "value" : "Tampilkan" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Invita amici" + "value" : "Mostra" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "友達を紹介" + "value" : "表示" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "친구 추천" + "value" : "보기" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Vrienden doorverwijzen" + "value" : "Toon" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Poleć znajomych" + "value" : "Pokaż" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Indique amigos" + "value" : "Mostrar" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Recomendar amigos" + "value" : "Mostrar" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Пригласить друзей" + "value" : "Показать" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Tipsa vänner" + "value" : "Visa" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "แนะนำเพื่อน" + "value" : "แสดง" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Запросити друзів" + "value" : "Показати" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Giới thiệu bạn bè" + "value" : "Hiện" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "推荐朋友" + "value" : "显示" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "推薦朋友" + "value" : "顯示" } } } }, - "Refer some friends and watch your free data go up." : { + "Show Provider List" : { + "comment" : "Used in Main/Connect/ConnectView-macOS.swift.", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "أوصِ بعض الأصدقاء وشاهد بياناتك المجانية تزداد." + "value" : "إظهار قائمة المزودين" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Pozvěte přátele a sledujte, jak se vaše bezplatná data zvyšují." + "value" : "Zobrazit seznam poskytovatelů" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Empfehlen Sie Freunden und sehen Sie, wie Ihr kostenloses Datenvolumen steigt." + "value" : "Anbieter-Liste anzeigen" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Συστήστε μερικούς φίλους και δείτε τα δωρεάν δεδομένα σας να αυξάνονται." + "value" : "Εμφάνιση λίστας παρόχων" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Refer some friends and watch your free data go up." + "value" : "Show Provider List" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Recomienda a algunos amigos y observa cómo aumenta tu datos gratis." + "value" : "Mostrar lista de proveedores" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Recomienda a algunos amigos y observa cómo aumenta tu datos gratis." + "value" : "Mostrar lista de proveedores" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Parrainez des amis et voyez vos données gratuites augmenter." + "value" : "Afficher la liste des fournisseurs" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "המלץ על חברים וצפה בנתונים החינמיים שלך גדלים." + "value" : "הצג רשימת ספקים" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "कुछ मित्रों को रेफ़र करें और अपनी मुफ्त डेटा बढ़ते देखें।" + "value" : "प्रदाता सूची दिखाएं" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Referensikan beberapa teman dan lihat data gratis Anda bertambah." + "value" : "Tampilkan Daftar Penyedia" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Invita degli amici e guarda i tuoi dati gratuiti aumentare." + "value" : "Mostra elenco provider" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "友達を紹介して、無料データが増えるのを見よう。" + "value" : "プロバイダーリストを表示" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "친구를 추천하고 무료 데이터가 늘어나는 것을 확인하세요." + "value" : "제공업체 목록 표시" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Verwijs vrienden door en zie je gratis data stijgen." + "value" : "Toon Provider Lijst" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Poleć znajomych i obserwuj, jak rosną Twoje darmowe dane." + "value" : "Pokaż listę dostawców" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Indique alguns amigos e veja seus dados gratuitos aumentarem." + "value" : "Mostrar lista de provedores" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Recomende alguns amigos e veja os seus dados gratuitos aumentarem." + "value" : "Mostrar lista de fornecedores" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Пригласите друзей и наблюдайте, как увеличивается ваш бесплатный трафик." + "value" : "Показать список провайдеров" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Tipsa några vänner och se din fria data öka." + "value" : "Visa leverantörslista" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "แนะนำเพื่อน ๆ แล้วดูข้อมูลฟรีของคุณเพิ่มขึ้น" + "value" : "แสดงรายชื่อผู้ให้บริการ" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Запросіть друзів і спостерігайте, як збільшується ваш безкоштовний трафік." + "value" : "Показати список провайдерів" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Giới thiệu bạn bè và xem dữ liệu miễn phí của bạn tăng lên." + "value" : "Hiển thị danh sách nhà cung cấp" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "推荐一些朋友,看看你的免费流量如何增加。" + "value" : "显示供应商列表" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "推薦一些朋友,看看你的免費數據如何增加。" + "value" : "顯示供應商列表" } } } }, - "Referral" : { + "Sign in" : { + "comment" : "Used in Authenticate/LoginInitial/LoginInitialView.swift.", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "إحالة" + "value" : "تسجيل الدخول" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Referral" + "value" : "Přihlásit se" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Empfehlung" + "value" : "Anmelden" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Παραπομπή" + "value" : "Είσοδος" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Referral" + "value" : "Sign in" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Referencia" + "value" : "Iniciar sesión" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Referencia" + "value" : "Iniciar sesión" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Parrainage" + "value" : "Se connecter" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "הפניה" + "value" : "התחבר" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "रेफ़रल" + "value" : "साइन इन करें" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Referensi" + "value" : "Masuk" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Referenza" + "value" : "Accedi" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "紹介" + "value" : "サインイン" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "추천" + "value" : "로그인" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Verwijzing" + "value" : "Log in" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Polecenie" + "value" : "Zaloguj się" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Indicação" + "value" : "Entrar" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Indicação" + "value" : "Iniciar sessão" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Реферал" + "value" : "Войти" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Rekommendation" + "value" : "Logga in" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "การแนะนำ" + "value" : "ลงชื่อเข้าใช้" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Реферал" + "value" : "Увійти" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Giới thiệu" + "value" : "Đăng nhập" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "推荐" + "value" : "登录" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "推薦" + "value" : "登入" } } } }, - "Referral Bonus applied" : { + "Sign in with Bittensor" : { + "comment" : "Call to action", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "تم تطبيق مكافأة الإحالة" + "value" : "تسجيل الدخول باستخدام Bittensor" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Uplatnění bonusu za doporučení" + "value" : "Přihlásit se přes Bittensor" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Empfehlungsbonus angewendet" + "value" : "Mit Bittensor anmelden" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Εφαρμόστηκε το μπόνους παραπομπής" + "value" : "Σύνδεση με Bittensor" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Referral Bonus applied" + "value" : "Sign in with Bittensor" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Bonificación por referencia aplicada" + "value" : "Iniciar sesión con Bittensor" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Bonificación por referencia aplicada" + "value" : "Iniciar sesión con Bittensor" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Iniciar sesión con Bittensor" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Bonus de parrainage appliqué" + "value" : "Se connecter avec Bittensor" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "הוחל בונוס הפניה" + "value" : "התחבר עם Bittensor" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "रेफरल बोनस लागू किया गया" + "value" : "Bittensor से साइन इन करें" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Bonus rujukan diterapkan" + "value" : "Masuk dengan Bittensor" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Bonus di riferimento applicato" + "value" : "Accedi con Bittensor" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "紹介ボーナスが適用されました" + "value" : "Bittensorでサインイン" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "추천 보너스가 적용되었습니다" + "value" : "Bittensor로 로그인" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Verwijzingsbonus toegepast" + "value" : "Inloggen met Bittensor" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Zastosowano bonus za polecenie" + "value" : "Zaloguj się przez Bittensor" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Iniciar sessão com Bittensor" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Bônus de indicação aplicado" + "value" : "Entrar com Bittensor" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Bônus de indicação aplicado" + "value" : "Iniciar sessão com Bittensor" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Применен реферальный бонус" + "value" : "Войти через Bittensor" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Rekommendationsbonus tillämpad" + "value" : "Logga in med Bittensor" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "ใช้โบนัสการแนะนำแล้ว" + "value" : "เข้าสู่ระบบด้วย Bittensor" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Застосовано реферальний бонус" + "value" : "Увійти через Bittensor" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Đã áp dụng tiền thưởng giới thiệu" + "value" : "Đăng nhập bằng Bittensor" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "已应用推荐奖金" + "value" : "使用 Bittensor 登录" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "已應用推薦獎金" + "value" : "使用 Bittensor 登入" } } } }, - "Referral network" : { + "Sign in with Google" : { + "comment" : "Used in Authenticate/LoginInitial/UrGoogleSignInButton.swift.", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "شبكة الإحالة" + "value" : "تسجيل الدخول باستخدام Google" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Síť doporučení" + "value" : "Přihlásit se pomocí Google" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Empfehlungsnetzwerk" + "value" : "Mit Google anmelden" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Δίκτυο παραπομπών" + "value" : "Σύνδεση με Google" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Referral network" + "value" : "Sign in with Google" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Red de referidos" + "value" : "Iniciar sesión con Google" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Red de referidos" + "value" : "Iniciar sesión con Google" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Réseau de parrainage" + "value" : "Se connecter avec Google" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "רשת הפניות" + "value" : "התחבר עם Google" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "रेफ़रल नेटवर्क" + "value" : "Google से साइन इन करें" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Jaringan referensi" + "value" : "Masuk dengan Google" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Rete di riferimento" + "value" : "Accedi con Google" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "リファラルネットワーク" + "value" : "Google でサインイン" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "추천 네트워크" + "value" : "Google로 로그인" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Verwijzingsnetwerk" + "value" : "Log in met Google" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Sieć poleceń" + "value" : "Zaloguj się przez Google" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Rede de indicações" + "value" : "Entrar com Google" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Rede de recomendações" + "value" : "Iniciar sessão com Google" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Реферальная сеть" + "value" : "Войти через Google" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Referensnätverk" + "value" : "Logga in med Google" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "เครือข่ายผู้อ้างอิง" + "value" : "เข้าสู่ระบบด้วย Google" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Реферальна мережа" + "value" : "Увійти через Google" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Mạng giới thiệu" + "value" : "Đăng nhập bằng Google" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "推荐网络" + "value" : "使用 Google 登录" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "推薦網絡" + "value" : "使用 Google 登入" } } } }, - "Referrals" : { + "Sign in with Solana" : { + "comment" : "Call to action", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "الإحالات" + "value" : "تسجيل الدخول باستخدام Solana" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Doporučení" + "value" : "Přihlaste se pomocí Solana" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Empfehlungen" + "value" : "Mit Solana anmelden" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Παραπομπές" + "value" : "Είσοδος με Solana" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Referrals" + "value" : "Sign in with Solana" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Referidos" + "value" : "Iniciar sesión con Solana" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Referidos" + "value" : "Iniciar sesión con Solana" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Iniciar sesión con Solana" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Parrainages" + "value" : "Se connecter avec Solana" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "הפניות" + "value" : "התחבר עם Solana" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "रेफ़रल" + "value" : "Solana से साइन इन करें" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Referensi" + "value" : "Masuk dengan Solana" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Inviti" + "value" : "Accedi con Solana" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "紹介" + "value" : "Solanaでサインイン" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "추천" + "value" : "Solana로 로그인" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Verwijzingen" + "value" : "Inloggen met Solana" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Polecenia" + "value" : "Zaloguj się za pomocą Solana" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Iniciar sessão com Solana" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Indicações" + "value" : "Entrar com Solana" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Referências" + "value" : "Iniciar sessão com Solana" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Рефералы" + "value" : "Войти через Solana" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Rekommendationer" + "value" : "Logga in med Solana" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "การแนะนำ" + "value" : "เข้าสู่ระบบด้วยเครือข่าย Solana" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Реферали" + "value" : "Увійти через Solana" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Giới thiệu" + "value" : "Đăng nhập bằng Solana" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "推荐" + "value" : "使用 Solana 登录" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "推薦" + "value" : "使用 Solana 登入" } } } }, - "Reliability" : { + "Sign out" : { + "comment" : "Used in NetworkApp.swift.", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "الموثوقية" + "value" : "تسجيل الخروج" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Spolehlivost" + "value" : "Odhlásit se" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Zuverlässigkeit" + "value" : "Abmelden" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Αξιοπιστία" + "value" : "Αποσύνδεση" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Reliability" + "value" : "Sign out" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Fiabilidad" + "value" : "Cerrar sesión" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Fiabilidad" + "value" : "Cerrar sesión" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Fiabilité" + "value" : "Déconnexion" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "אמינות" + "value" : "התנתק" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "विश्वसनीयता" + "value" : "साइन आउट" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Keandalan" + "value" : "Keluar" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Affidabilità" + "value" : "Esci" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "信頼性" + "value" : "サインアウト" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "신뢰성" + "value" : "로그아웃" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Betrouwbaarheid" + "value" : "Log uit" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Niezawodność" + "value" : "Wyloguj" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Confiabilidade" + "value" : "Sair" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Confiabilidade" + "value" : "Terminar sessão" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Надежность" + "value" : "Выйти" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Tillförlitlighet" + "value" : "Logga ut" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "ความน่าเชื่อถือ" + "value" : "ออกจากระบบ" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Надійність" + "value" : "Вийти" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Độ tin cậy" + "value" : "Đăng xuất" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "可靠性" + "value" : "退出登录" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "可靠性" + "value" : "登出" } } } }, - "Reliability Settings" : { - "extractionState" : "stale", + "Solana USDC addresses and Bittensor (TAO) addresses are supported. Bittensor wallets are stored for future use and can't receive payouts yet." : { + "comment" : "Used in Main/Account/Wallets/ConnectWalletSheet/EnterWalletAddress/EnterWalletAddressView.swift.", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "إعدادات الموثوقية" + "value" : "عناوين USDC على Solana وعناوين Bittensor (TAO) مدعومة. يتم حفظ محافظ Bittensor للاستخدام المستقبلي ولا يمكنها استلام المدفوعات بعد." } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Nastavení spolehlivosti" + "value" : "Podporovány jsou adresy USDC na síti Solana a adresy Bittensor (TAO). Peněženky Bittensor se ukládají pro budoucí použití a zatím nemohou přijímat výplaty." } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Zuverlässigkeitseinstellungen" + "value" : "Solana-USDC-Adressen und Bittensor-Adressen (TAO) werden unterstützt. Bittensor-Wallets werden für die spätere Nutzung gespeichert und können noch keine Auszahlungen empfangen." } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Ρυθμίσεις αξιοπιστίας" + "value" : "Υποστηρίζονται διευθύνσεις USDC στο Solana και διευθύνσεις Bittensor (TAO). Τα πορτοφόλια Bittensor αποθηκεύονται για μελλοντική χρήση και δεν μπορούν ακόμη να λάβουν πληρωμές." } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Reliability Settings" + "value" : "Solana USDC addresses and Bittensor (TAO) addresses are supported. Bittensor wallets are stored for future use and can't receive payouts yet." } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Configuración de fiabilidad" + "value" : "Se admiten las direcciones USDC en Solana y las direcciones de Bittensor (TAO). Las billeteras de Bittensor se guardan para uso futuro y aún no pueden recibir pagos." } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Configuración de fiabilidad" + "value" : "Se admiten las direcciones USDC en Solana y las direcciones de Bittensor (TAO). Las billeteras de Bittensor se guardan para uso futuro y aún no pueden recibir pagos." } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Paramètres de fiabilité" + "value" : "Les adresses USDC sur Solana et les adresses Bittensor (TAO) sont prises en charge. Les portefeuilles Bittensor sont enregistrés pour un usage futur et ne peuvent pas encore recevoir de paiements." } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "הגדרות אמינות" + "value" : "כתובות USDC ב-Solana וכתובות Bittensor (TAO) נתמכות. ארנקי Bittensor נשמרים לשימוש עתידי ועדיין לא יכולים לקבל תשלומים." } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "विश्वसनीयता सेटिंग्स" + "value" : "Solana USDC पते और Bittensor (TAO) पते समर्थित हैं। Bittensor वॉलेट भविष्य के उपयोग के लिए सहेजे जाते हैं और अभी भुगतान प्राप्त नहीं कर सकते।" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Pengaturan Keandalan" + "value" : "Alamat USDC Solana dan alamat Bittensor (TAO) didukung. Dompet Bittensor disimpan untuk penggunaan mendatang dan belum bisa menerima pembayaran." } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Impostazioni di affidabilità" + "value" : "Sono supportati gli indirizzi USDC su Solana e gli indirizzi Bittensor (TAO). I portafogli Bittensor vengono salvati per un uso futuro e non possono ancora ricevere pagamenti." } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "信頼性設定" + "value" : "Solana USDCアドレスとBittensor(TAO)アドレスに対応しています。Bittensorウォレットは将来の利用のために保存されますが、まだ支払いは受け取れません。" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "신뢰도 설정" + "value" : "Solana USDC 주소와 Bittensor(TAO) 주소가 지원됩니다. Bittensor 지갑은 향후 사용을 위해 저장되며 아직 지급금을 받을 수 없습니다." } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Betrouwbaarheidsinstellingen" + "value" : "Solana USDC-adressen en Bittensor-adressen (TAO) worden ondersteund. Bittensor-wallets worden bewaard voor toekomstig gebruik en kunnen nog geen uitbetalingen ontvangen." } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Ustawienia niezawodności" + "value" : "Obsługiwane są adresy USDC na Solana oraz adresy Bittensor (TAO). Portfele Bittensor są zapisywane na przyszłość i nie mogą jeszcze otrzymywać wypłat." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Configurações de confiabilidade" + "value" : "Endereços USDC em Solana e endereços Bittensor (TAO) são compatíveis. As carteiras Bittensor são armazenadas para uso futuro e ainda não podem receber pagamentos." } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Definições de fiabilidade" + "value" : "Endereços USDC em Solana e endereços Bittensor (TAO) são compatíveis. As carteiras Bittensor são guardadas para uso futuro e ainda não podem receber pagamentos." } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Настройки надежности" + "value" : "Поддерживаются адреса USDC в сети Solana и адреса Bittensor (TAO). Кошельки Bittensor сохраняются для будущего использования и пока не могут получать выплаты." } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Inställningar för tillförlitlighet" + "value" : "Solana USDC-adresser och Bittensor-adresser (TAO) stöds. Bittensor-plånböcker sparas för framtida bruk och kan inte ta emot utbetalningar än." } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "การตั้งค่าความน่าเชื่อถือ" + "value" : "รองรับที่อยู่ USDC บน Solana และที่อยู่ Bittensor (TAO) กระเป๋า Bittensor จะถูกบันทึกไว้ใช้ในอนาคต และยังรับยอดจ่ายไม่ได้" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Налаштування надійності" + "value" : "Підтримуються адреси USDC у мережі Solana та адреси Bittensor (TAO). Гаманці Bittensor зберігаються для майбутнього використання і поки не можуть отримувати виплати." } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Cài đặt độ tin cậy" + "value" : "Hỗ trợ địa chỉ USDC trên Solana và địa chỉ Bittensor (TAO). Ví Bittensor được lưu để dùng trong tương lai và chưa thể nhận thanh toán." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "可靠性设置" + "value" : "支持 Solana USDC 地址和 Bittensor (TAO) 地址。Bittensor 钱包会保存以备将来使用,目前还不能接收支付。" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "可靠性設定" + "value" : "支援 Solana USDC 地址和 Bittensor (TAO) 地址。Bittensor 錢包會儲存以備日後使用,暫時未能接收支付。" } } } }, - "Reliability Weight" : { + "Solflare" : { + "comment" : "Used in Main/Account/Wallets/ConnectWalletSheet/ConnectWalletSheetView.swift.", + "shouldTranslate" : false + }, + "Something went wrong finding your account" : { + "comment" : "Snackbar shown when the account lookup for a password reset fails. Used in Main/Account/Profile/ProfileView.swift.", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "وزن الموثوقية" + "value" : "حدث خطأ ما أثناء البحث عن حسابك" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Váha spolehlivosti" + "value" : "Při hledání vašeho účtu se něco pokazilo" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Zuverlässigkeitsgewicht" + "value" : "Beim Suchen deines Kontos ist etwas schief gelaufen" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Βάρος αξιοπιστίας" + "value" : "Κάτι πήγε στραβά κατά την εύρεση του λογαριασμού σας" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Reliability Weight" + "value" : "Something went wrong finding your account" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Peso de fiabilidad" + "value" : "Algo salió mal al buscar tu cuenta" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Peso de fiabilidad" + "value" : "Algo salió mal al buscar tu cuenta" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Algo salió mal al buscar tu cuenta" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Poids de fiabilité" + "value" : "Une erreur s'est produite lors de la recherche de votre compte" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "משקל אמינות" + "value" : "משהו השתבש באיתור החשבון שלך" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "विश्वसनीयता भार" + "value" : "आपका खाता खोजने में कुछ गलत हो गया" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Bobot keandalan" + "value" : "Terjadi kesalahan saat mencari akun Anda" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Peso di affidabilità" + "value" : "Qualcosa è andato storto durante la ricerca del tuo account" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "信頼性の重み" + "value" : "アカウントの検索中に問題が発生しました" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "신뢰성 가중치" + "value" : "계정을 찾는 중 문제가 발생했습니다" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Betrouwbaarheidsgewicht" + "value" : "Er ging iets mis bij het vinden van je account" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Waga niezawodności" + "value" : "Coś poszło nie tak podczas wyszukiwania konta" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Algo correu mal ao procurar a sua conta" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Peso de confiabilidade" + "value" : "Algo deu errado ao localizar sua conta" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Peso de confiabilidade" + "value" : "Algo correu mal ao procurar a sua conta" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Вес надежности" + "value" : "Что-то пошло не так при поиске вашего аккаунта" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Tillförlitlighetsvikt" + "value" : "Något gick fel när ditt konto skulle hittas" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "น้ำหนักความน่าเชื่อถือ" + "value" : "เกิดข้อผิดพลาดในการค้นหาบัญชีของคุณ" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Вага надійності" + "value" : "Щось пішло не так під час пошуку вашого облікового запису" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Trọng số độ tin cậy" + "value" : "Đã xảy ra sự cố khi tìm tài khoản của bạn" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "可靠性权重" + "value" : "查找您的账户时出现问题" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "可靠性權重" + "value" : "尋找您的帳戶時發生問題" } } } }, - "Reliability: %@" : { - "extractionState" : "stale", + "Something went wrong. Please try again later." : { + "comment" : "Used in Main/Account/Settings/UpdateReferralNetworkSheet/UpdateReferralNetworkSheetViewModel.swift.", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "الموثوقية: %@" + "value" : "حدث خطأ ما. يرجى المحاولة مرة أخرى لاحقًا." } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Spolehlivost: %@" + "value" : "Něco se pokazilo. Zkuste to prosím později." } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Zuverlässigkeit: %@" + "value" : "Etwas ist schief gelaufen. Bitte versuchen Sie es später noch einmal." } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Αξιοπιστία: %@" + "value" : "Κάτι πήγε στραβά. Παρακαλώ δοκιμάστε ξανά αργότερα." } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Reliability: %@" + "value" : "Something went wrong. Please try again later." } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Fiabilidad: %@" + "value" : "Algo salió mal. Por favor, inténtalo de nuevo más tarde." } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Fiabilidad: %@" + "value" : "Algo salió mal. Por favor, inténtalo de nuevo más tarde." } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Fiabilité : %@" + "value" : "Une erreur s'est produite. Veuillez réessayer plus tard." } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "אמינות: %@" + "value" : "משהו השתבש. אנא נסה שוב מאוחר יותר." } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "विश्वसनीयता: %@" + "value" : "कुछ गलत हो गया। कृपया बाद में पुन: प्रयास करें।" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Keandalan: %@" + "value" : "Terjadi kesalahan. Silakan coba lagi nanti." } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Affidabilità: %@" + "value" : "Qualcosa è andato storto. Per favore riprova più tardi." } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "信頼性: %@" + "value" : "問題が発生しました。後でもう一度お試しください。" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "신뢰도: %@" + "value" : "문제가 발생했습니다. 나중에 다시 시도해 주세요." } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Betrouwbaarheid: %@" + "value" : "Er ging iets mis. Probeer het later opnieuw." } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Niezawodność: %@" + "value" : "Coś poszło nie tak. Spróbuj ponownie później." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Confiabilidade: %@" + "value" : "Algo deu errado. Por favor, tente novamente mais tarde." } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Fiabilidade: %@" + "value" : "Algo correu mal. Por favor, tente novamente mais tarde." } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Надежность: %@" + "value" : "Что-то пошло не так. Пожалуйста, попробуйте позже." } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Tillförlitlighet: %@" + "value" : "Något gick fel. Försök igen senare." } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "ความน่าเชื่อถือ: %@" + "value" : "เกิดข้อผิดพลาด กรุณาลองใหม่อีกครั้งในภายหลัง" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Надійність: %@" + "value" : "Щось пішло не так. Будь ласка, спробуйте пізніше." } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Độ tin cậy: %@" + "value" : "Đã xảy ra sự cố. Vui lòng thử lại sau." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "可靠性:%@" + "value" : "出现问题。请稍后再试。" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "可靠性:%@" + "value" : "發生錯誤。請稍後再試。" } } } }, - "Reliability: %lf" : { - "extractionState" : "stale", + "Sorry, there was an error claiming multiplier: %@" : { + "comment" : "Snackbar shown when claiming the earnings multiplier fails, with the underlying error message. Used in Main/Account/Settings/SettingsView.swift.", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "الموثوقية: %lf" + "value" : "عذرًا، حدث خطأ أثناء المطالبة بالمضاعف: %@" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Spolehlivost: %lf" + "value" : "Omlouváme se, při nárokování násobitele došlo k chybě: %@" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Zuverlässigkeit: %lf" + "value" : "Entschuldigung, beim Beanspruchen des Multiplikators ist ein Fehler aufgetreten: %@" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Αξιοπιστία: %lf" + "value" : "Λυπούμαστε, παρουσιάστηκε σφάλμα κατά την αξίωση του πολλαπλασιαστή: %@" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Reliability: %lf" + "value" : "Sorry, there was an error claiming multiplier: %@" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Fiabilidad: %lf" + "value" : "Lo sentimos, se ha producido un error al reclamar el multiplicador: %@" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Fiabilidad: %lf" + "value" : "Lo sentimos, se produjo un error al reclamar el multiplicador: %@" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lo sentimos, se produjo un error al reclamar el multiplicador: %@" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Fiabilité : %lf" + "value" : "Désolé, une erreur s'est produite lors de la réclamation du multiplicateur : %@" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "אמינות: %lf" + "value" : "מצטערים, אירעה שגיאה בתביעת המכפיל: %@" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "विश्वसनीयता: %lf" + "value" : "क्षमा करें, गुणक का दावा करते समय त्रुटि हुई: %@" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Keandalan: %lf" + "value" : "Maaf, terjadi kesalahan saat mengklaim pengganda: %@" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Affidabilità: %lf" + "value" : "Spiacenti, si è verificato un errore durante la richiesta del moltiplicatore: %@" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "信頼性: %lf" + "value" : "申し訳ありません。乗数の受け取り中にエラーが発生しました:%@" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "신뢰도: %lf" + "value" : "죄송합니다. 배율을 받는 중 오류가 발생했습니다: %@" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Betrouwbaarheid: %lf" + "value" : "Sorry, er is een fout opgetreden bij het claimen van de vermenigvuldiger: %@" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Niezawodność: %lf" + "value" : "Przepraszamy, wystąpił błąd podczas odbierania mnożnika: %@" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lamentamos, ocorreu um erro ao reivindicar o multiplicador: %@" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Confiabilidade: %lf" + "value" : "Desculpe, ocorreu um erro ao reivindicar o multiplicador: %@" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Fiabilidade: %lf" + "value" : "Lamentamos, ocorreu um erro ao reivindicar o multiplicador: %@" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Надежность: %lf" + "value" : "К сожалению, произошла ошибка при получении множителя: %@" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Tillförlitlighet: %lf" + "value" : "Tyvärr uppstod ett fel när multiplikatorn skulle hämtas: %@" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "ความน่าเชื่อถือ: %lf" + "value" : "ขออภัย เกิดข้อผิดพลาดในการรับสิทธิ์ตัวคูณ: %@" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Надійність: %lf" + "value" : "На жаль, сталася помилка під час отримання множника: %@" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Độ tin cậy: %lf" + "value" : "Rất tiếc, đã xảy ra lỗi khi yêu cầu hệ số nhân: %@" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "可靠性:%lf" + "value" : "抱歉,领取倍数时出错:%@" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "可靠性:%lf" + "value" : "抱歉,申領倍數時發生錯誤:%@" } } } }, - "remote" : { - - }, - "Remote" : { - "comment" : "Label for a chart showing the remote traffic.", - "isCommentAutoGenerated" : true - }, - "Remote DNS servers" : { - "comment" : "A label displayed above a list of DNS servers.", - "isCommentAutoGenerated" : true - }, - "Remote DoH URLs" : { - "comment" : "A heading for a list of remote DoH URLs.", - "isCommentAutoGenerated" : true - }, - "Remove" : { - "comment" : "A label for a button that removes a rule.", - "isCommentAutoGenerated" : true - }, - "Remove rule" : { - "comment" : "A button to remove a split rule.", - "isCommentAutoGenerated" : true - }, - "Remove wallet" : { + "Sorry, there was an error claiming multiplier." : { + "comment" : "Snackbar shown when claiming the earnings multiplier fails. Used in Main/Account/Settings/SettingsView.swift.", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "إزالة المحفظة" + "value" : "عذرًا، حدث خطأ أثناء المطالبة بالمضاعف." } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Odstranit peněženku" + "value" : "Omlouváme se, při nárokování násobitele došlo k chybě." } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Wallet entfernen" + "value" : "Entschuldigung, beim Beanspruchen des Multiplikators ist ein Fehler aufgetreten." } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Αφαίρεση πορτοφολιού" + "value" : "Λυπούμαστε, παρουσιάστηκε σφάλμα κατά την αξίωση του πολλαπλασιαστή." } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Remove wallet" + "value" : "Sorry, there was an error claiming multiplier." } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Eliminar cartera" + "value" : "Lo sentimos, se ha producido un error al reclamar el multiplicador." } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Eliminar billetera" + "value" : "Lo sentimos, se produjo un error al reclamar el multiplicador." + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lo sentimos, se produjo un error al reclamar el multiplicador." } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Supprimer le portefeuille" + "value" : "Désolé, une erreur s'est produite lors de la réclamation du multiplicateur." } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "הסר ארנק" + "value" : "מצטערים, אירעה שגיאה בתביעת המכפיל." } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "वॉलेट हटाएं" + "value" : "क्षमा करें, गुणक का दावा करते समय त्रुटि हुई।" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Hapus dompet" + "value" : "Maaf, terjadi kesalahan saat mengklaim pengganda." } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Rimuovi portafoglio" + "value" : "Spiacenti, si è verificato un errore durante la richiesta del moltiplicatore." } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "ウォレットを削除" + "value" : "申し訳ありません。乗数の受け取り中にエラーが発生しました。" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "지갑 제거" + "value" : "죄송합니다. 배율을 받는 중 오류가 발생했습니다." } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Verwijder wallet" + "value" : "Sorry, er is een fout opgetreden bij het claimen van de vermenigvuldiger." } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Usuń portfel" + "value" : "Przepraszamy, wystąpił błąd podczas odbierania mnożnika." + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lamentamos, ocorreu um erro ao reivindicar o multiplicador." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Remover carteira" + "value" : "Desculpe, ocorreu um erro ao reivindicar o multiplicador." } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Remover carteira" + "value" : "Lamentamos, ocorreu um erro ao reivindicar o multiplicador." } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Удалить кошелек" + "value" : "К сожалению, произошла ошибка при получении множителя." } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Ta bort plånbok" + "value" : "Tyvärr uppstod ett fel när multiplikatorn skulle hämtas." } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "ลบกระเป๋าเงิน" + "value" : "ขออภัย เกิดข้อผิดพลาดในการรับสิทธิ์ตัวคูณ" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Видалити гаманець" + "value" : "На жаль, сталася помилка під час отримання множника." } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Xóa ví" + "value" : "Rất tiếc, đã xảy ra lỗi khi yêu cầu hệ số nhân." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "移除钱包" + "value" : "抱歉,领取倍数时出错。" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "移除錢包" + "value" : "抱歉,申領倍數時發生錯誤。" } } } }, - "Resend code" : { + "Sorry, there was an error deleting your account." : { + "comment" : "Snackbar shown when deleting the account fails. Used in Main/Account/Settings/SettingsView.swift.", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "إعادة إرسال الرمز" + "value" : "عذرًا، حدث خطأ أثناء حذف حسابك." } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Opětovné odeslání kódu" + "value" : "Omlouváme se, při mazání účtu došlo k chybě." } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Code erneut senden" + "value" : "Entschuldigung, beim Löschen deines Kontos ist ein Fehler aufgetreten." } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Επανάληψη αποστολής κωδικού" + "value" : "Λυπούμαστε, παρουσιάστηκε σφάλμα κατά τη διαγραφή του λογαριασμού σας." } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Resend code" + "value" : "Sorry, there was an error deleting your account." } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Reenviar código" + "value" : "Lo sentimos, se ha producido un error al eliminar tu cuenta." } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Reenviar código" + "value" : "Lo sentimos, se produjo un error al eliminar tu cuenta." + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lo sentimos, se produjo un error al eliminar tu cuenta." } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Renvoyer le code" + "value" : "Désolé, une erreur s'est produite lors de la suppression de votre compte." } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "שלח קוד מחדש" + "value" : "מצטערים, אירעה שגיאה במחיקת החשבון שלך." } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "कोड पुनः भेजें" + "value" : "क्षमा करें, आपका खाता हटाते समय त्रुटि हुई।" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Kirim ulang kode" + "value" : "Maaf, terjadi kesalahan saat menghapus akun Anda." } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Rinvia codice" + "value" : "Spiacenti, si è verificato un errore durante l'eliminazione del tuo account." } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "コードを再送信" + "value" : "申し訳ありません。アカウントの削除中にエラーが発生しました。" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "코드 재전송" + "value" : "죄송합니다. 계정을 삭제하는 중 오류가 발생했습니다." } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Code opnieuw verzenden" + "value" : "Sorry, er is een fout opgetreden bij het verwijderen van je account." } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Wyślij kod ponownie" + "value" : "Przepraszamy, wystąpił błąd podczas usuwania konta." + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lamentamos, ocorreu um erro ao eliminar a sua conta." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Reenviar código" + "value" : "Desculpe, ocorreu um erro ao excluir sua conta." } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Reenviar código" + "value" : "Lamentamos, ocorreu um erro ao eliminar a sua conta." } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Отправить код повторно" + "value" : "К сожалению, произошла ошибка при удалении вашего аккаунта." } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Skicka kod igen" + "value" : "Tyvärr uppstod ett fel när ditt konto skulle raderas." } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "ส่งรหัสอีกครั้ง" + "value" : "ขออภัย เกิดข้อผิดพลาดในการลบบัญชีของคุณ" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Надіслати код повторно" + "value" : "На жаль, сталася помилка під час видалення вашого облікового запису." } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Gửi lại mã" + "value" : "Rất tiếc, đã xảy ra lỗi khi xóa tài khoản của bạn." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "重新发送验证码" + "value" : "抱歉,删除账户时出错。" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "重新發送驗證碼" + "value" : "抱歉,刪除帳戶時發生錯誤。" } } } }, - "Reset it." : { - "comment" : "Referring to resetting the password", + "Spec" : { + "comment" : "device", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "إعادة تعيينه" + "value" : "المواصفات" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Obnovte jej." + "value" : "Specifikace" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Zurücksetzen" + "value" : "Spezifikation" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Επαναφέρετε το" + "value" : "Προδιαγραφές" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Reset it." + "value" : "Spec" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Restablécelo" + "value" : "Modelo" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Restablécelo" + "value" : "Modelo" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Modelo" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Réinitialisez-le" + "value" : "Modèle" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "אפס את זה" + "value" : "מפרט" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "इसे रीसेट करें" + "value" : "स्पेक" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Atur ulang" + "value" : "Spesifikasi" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Reimpostalo" + "value" : "Modello" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "リセットする" + "value" : "スペック" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "재설정" + "value" : "사양" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Stel het opnieuw in" + "value" : "Specificatie" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Zresetuj to" + "value" : "Specyfikacja" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Especificação" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Redefinir" + "value" : "Especificação" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Redefinir" + "value" : "Especificação" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Сбросить" + "value" : "Модель" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Återställ" + "value" : "Specifikation" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "รีเซ็ต" + "value" : "สเปก" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Скинути" + "value" : "Модель" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Đặt lại" + "value" : "Thông số" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "重置它" + "value" : "规格" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "重設它" + "value" : "規格" } } } }, - "Resolvers" : { - "comment" : "A section header for the DNS resolvers.", - "isCommentAutoGenerated" : true - }, - "Restore the most secure settings: encrypted DNS over HTTPS through the tunnel." : { - - }, - "Restore to most secure settings" : { - - }, - "Retry" : { + "Split rules" : { + "comment" : "transfer stats and split rules", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "إعادة المحاولة" + "value" : "قواعد التقسيم" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Zkusit znovu" + "value" : "Pravidla rozdělení" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Erneut versuchen" + "value" : "Split-Regeln" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Δοκιμή ξανά" + "value" : "Κανόνες διαχωρισμού" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Retry" + "value" : "Split rules" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Reintentar" + "value" : "Reglas de división" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Reintentar" + "value" : "Reglas de división" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Reglas de división" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Réessayer" + "value" : "Règles de séparation" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "נסה שוב" + "value" : "כללי פיצול" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "फिर से प्रयास करें" + "value" : "स्प्लिट नियम" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Coba lagi" + "value" : "Aturan pemisahan" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Riprova" + "value" : "Regole di suddivisione" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "再試行" + "value" : "分割ルール" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "다시 시도" + "value" : "분할 규칙" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Opnieuw proberen" + "value" : "Splitsregels" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Spróbuj ponownie" + "value" : "Reguły podziału" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Regras de divisão" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Tentar novamente" + "value" : "Regras de divisão" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Tentar novamente" + "value" : "Regras de divisão" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Повторить" + "value" : "Правила разделения" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Försök igen" + "value" : "Delningsregler" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "ลองอีกครั้ง" + "value" : "กฎแยกเส้นทาง" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Повторити" + "value" : "Правила розділення" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Thử lại" + "value" : "Quy tắc tách luồng" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "重试" + "value" : "分流规则" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "重試" + "value" : "分流規則" } } } }, - "Review URnetwork" : { + "Start 2 week free trial" : { + "comment" : "Used in Shared/Views/Introduction/IntroductionView.swift.", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "تقييم URnetwork" + "value" : "ابدأ تجربة مجانية لمدة أسبوعين" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Ohodnoďte URnetwork" + "value" : "Začněte dvoutýdenní zkušební verzi zdarma" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork bewerten" + "value" : "Beginnen Sie die zweiwöchige kostenlose Testphase" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Αξιολογήστε το URnetwork" + "value" : "Ξεκινήστε τη δωρεάν δοκιμή δύο εβδομάδων" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Review URnetwork" + "value" : "Start 2 week free trial" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Valorar URnetwork" + "value" : "Comienza la prueba gratuita de 2 semanas" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Calificar URnetwork" + "value" : "Comienza la prueba gratuita de 2 semanas" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Évaluer URnetwork" + "value" : "Commencez l’essai gratuit de 2 semaines" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "דרג את URnetwork" + "value" : "התחל ניסיון חינם לשבועיים" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork की समीक्षा करें" + "value" : "2 सप्ताह की मुफ्त ट्रायल शुरू करें" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Ulas URnetwork" + "value" : "Mulai uji coba gratis 2 minggu" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Recensisci URnetwork" + "value" : "Inizia la prova gratuita di 2 settimane" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "URnetworkを評価" + "value" : "2週間の無料トライアルを開始" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork 리뷰하기" + "value" : "2주 무료 체험 시작" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Beoordeel URnetwork" + "value" : "Begin 2 weken gratis proefperiode" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Oceń URnetwork" + "value" : "Rozpocznij 2-tygodniowy bezpłatny okres próbny" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Avaliar URnetwork" + "value" : "Inicie o teste gratuito de 2 semanas" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Avaliar URnetwork" + "value" : "Inicie o teste gratuito de 2 semanas" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Оценить URnetwork" + "value" : "Начать двухнедельный бесплатный пробный период" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Recensera URnetwork" + "value" : "Starta 2 veckors gratis provperiod" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "รีวิว URnetwork" + "value" : "เริ่มทดลองใช้งานฟรี 2 สัปดาห์" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Оцінити URnetwork" + "value" : "Почати двотижневий безкоштовний пробний період" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Đánh giá URnetwork" + "value" : "Bắt đầu dùng thử miễn phí 2 tuần" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "评价 URnetwork" + "value" : "开始2周免费试用" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "評價 URnetwork" + "value" : "開始2星期免費試用" } } } }, - "Routing activity appears here while connected." : { - "comment" : "A description of the content of the \"Activity\" section of the split rules view.", - "isCommentAutoGenerated" : true - }, - "Rules" : { - "comment" : "A heading for the pinned split rules.", - "isCommentAutoGenerated" : true - }, - "Save" : { - - }, - "Save Logs" : { + "Stay connected daily = Earn up to 100 GiB/Day" : { + "extractionState" : "stale", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "حفظ السجلات" + "value" : "ابق متصلاً يوميًا = اربح حتى 100 غيغابايت/يوم" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Uložit logy" + "value" : "Zůstaňte připojeni denně = Získejte až 100 GiB/den" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Logs speichern" + "value" : "Täglich verbunden bleiben = Verdienen Sie bis zu 100 GiB/Tag" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Αποθήκευση αρχείων καταγραφής" + "value" : "Μείνετε συνδεδεμένοι καθημερινά = Κερδίστε έως και 100 GiB/ημέρα" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Save Logs" + "value" : "Stay connected daily = Earn up to 100 GiB/Day" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Guardar registros" + "value" : "Permanece conectado diariamente = Gana hasta 100 GiB/día" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Guardar registros" + "value" : "Permanece conectado diariamente = Gana hasta 100 GiB/día" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Enregistrer les journaux" + "value" : "Restez connecté chaque jour = Gagnez jusqu’à 100 Go/jour" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "שמור לוגים" + "value" : "הישאר מחובר מדי יום = תרוויח עד 100 GiB/יום" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "लॉग्स सहेजें" + "value" : "रोज़ाना जुड़े रहें = प्रति दिन 100 GiB तक कमाएँ" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Simpan log" + "value" : "Tetap terhubung setiap hari = Dapatkan hingga 100 GiB/hari" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Salva log" + "value" : "Rimani connesso ogni giorno = Guadagna fino a 100 GiB/giorno" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "ログを保存" + "value" : "毎日接続を維持 = 最大100 GiB/日を獲得" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "로그 저장" + "value" : "매일 연결 유지 = 하루 최대 100 GiB 획득" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Logbestanden opslaan" + "value" : "Blijf dagelijks verbonden = Verdien tot 100 GiB/dag" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Zapisz logi" + "value" : "Pozostań codziennie połączony = Zarabiaj do 100 GiB/dzień" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Salvar logs" + "value" : "Fique conectado diariamente = Ganhe até 100 GiB/dia" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Guardar logs" + "value" : "Mantenha-se ligado diariamente = Ganhe até 100 GiB/dia" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Сохранить логи" + "value" : "Оставайтесь на связи ежедневно = Зарабатывайте до 100 ГиБ/день" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Spara loggar" + "value" : "Var ansluten dagligen = Tjäna upp till 100 GiB/dag" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "บันทึกล็อก" + "value" : "เชื่อมต่อทุกวัน = รับสูงสุด 100 GiB/วัน" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Зберегти логи" + "value" : "Залишайтеся на зв’язку щодня = Заробляйте до 100 ГіБ/день" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Lưu nhật ký" + "value" : "Kết nối hàng ngày = Kiếm tới 100 GiB/ngày" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "保存日志" + "value" : "每日保持连接 = 每天赚取高达100 GiB" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "儲存日誌" + "value" : "每天保持連接 = 每日賺取高達100 GiB" } } } }, - "Search providers" : { + "Stay in touch" : { + "comment" : "Settings", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "البحث عن مزودي الخدمة" + "value" : "البقاء على اتصال" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Vyhledání poskytovatelů" + "value" : "Zůstaňte v kontaktu" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Anbieter suchen" + "value" : "In Kontakt bleiben" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Αναζήτηση παρόχων" + "value" : "Μείνετε σε επαφή" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Search providers" + "value" : "Stay in touch" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Buscar proveedores" + "value" : "Mantente en contacto" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Buscar proveedores" + "value" : "Mantente en contacto" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mantente en contacto" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Rechercher des fournisseurs" + "value" : "Rester en contact" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "חפש ספקים" + "value" : "שמור על קשר" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "प्रदाता खोजें" + "value" : "संपर्क में रहें" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Cari penyedia" + "value" : "Tetap terhubung" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Cerca provider" + "value" : "Rimani in contatto" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "プロバイダーを検索" + "value" : "連絡を取り合う" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "공급자 검색" + "value" : "연락 유지하기" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Zoek providers" + "value" : "Blijf in contact" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Szukaj dostawców" + "value" : "Bądźmy w kontakcie" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Manter contato" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Buscar provedores" + "value" : "Manter contato" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Pesquisar fornecedores" + "value" : "Mantenha-se em contacto" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Поиск провайдеров" + "value" : "Оставайтесь на связи" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Sök leverantörer" + "value" : "Håll kontakten" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "ค้นหาผู้ให้บริการ" + "value" : "ติดตามเรา" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Пошук провайдерів" + "value" : "Залишайтеся на зв'язку" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Tìm nhà cung cấp" + "value" : "Giữ liên lạc" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "搜索供应商" + "value" : "保持联系" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "搜尋供應商" + "value" : "保持聯繫" } } } }, - "Seeker Token Verified!" : { + "Step into the internet as it should be." : { + "comment" : "Login initial", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "تم التحقق من رمز Seeker!" + "value" : "انطلق إلى الإنترنت كما ينبغي أن يكون." } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Seeker Token ověřen!" + "value" : "Vstupte na internet, jak má být." } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Seeker-Token verifiziert!" + "value" : "Betreten Sie das Internet, wie es sein sollte." } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Το Token Seeker επαληθεύτηκε!" + "value" : "Μπείτε στο διαδίκτυο όπως θα έπρεπε να είναι." } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Seeker Token Verified!" + "value" : "Step into the internet as it should be." } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "¡Token Seeker verificado!" + "value" : "Adéntrate en el internet como debería ser." } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "¡Token Seeker verificado!" + "value" : "Ingresa al internet como debería ser." + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "¡Éntrale al internet como debe ser!" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Token Seeker vérifié !" + "value" : "Découvrez Internet tel qu'il devrait être." } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "אסימון Seeker אומת!" + "value" : "צעד אל האינטרנט כפי שהוא אמור להיות." } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "सीकर टोकन सत्यापित!" + "value" : "इंटरनेट में कदम रखें जैसा कि यह होना चाहिए" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Token Seeker Terverifikasi!" + "value" : "Masuki internet sebagaimana seharusnya." } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Token Seeker verificato!" + "value" : "Entra in internet come dovrebbe essere." } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "Seekerトークンが確認されました!" + "value" : "本来あるべきインターネットへ" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "Seeker 토큰 인증 완료!" + "value" : "인터넷이 마땅히 되어야 할 모습으로 들어가세요" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Seeker Token geverifieerd!" + "value" : "Stap het internet binnen zoals het hoort te zijn." } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Token Seeker zweryfikowany!" + "value" : "Wejdź do internetu, jakim powinien być." + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Acesse a internet como ela deveria ser." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Token Seeker verificado!" + "value" : "Acesse a internet como ela deveria ser." } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Token Seeker verificado!" + "value" : "Entre na internet como ela deveria ser." } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Токен Seeker подтвержден!" + "value" : "Откройте для себя интернет таким, каким он должен быть." } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Seeker-token verifierat!" + "value" : "Kliv in i internet som det borde vara." } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "ยืนยันโทเค็น Seeker แล้ว!" + "value" : "ก้าวเข้าอินเทอร์เน็ตในแบบที่ควรจะเป็น" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Токен Seeker перевірено!" + "value" : "Увійдіть в інтернет таким, яким він має бути." } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Đã xác minh Mã thông báo Seeker!" + "value" : "Bước vào internet như bản chất của nó." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "Seeker 令牌已验证!" + "value" : "进入互联网应有的样子" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "Seeker 權杖已驗證!" + "value" : "體驗應有的互聯網。" } } } }, - "Select country to block" : { + "Streaming" : { + "comment" : "A choice for connecting to a tunnel in streaming mode.", + "isCommentAutoGenerated" : true, "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "حدد الدولة للحظر" + "value" : "البث" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Vyberte zemi k blokování" + "value" : "Streamování" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Land zum Blockieren auswählen" + "value" : "Streaming" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Επιλέξτε χώρα για αποκλεισμό" + "value" : "Ροή" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Select country to block" + "value" : "Streaming" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Selecciona el país para bloquear" + "value" : "Streaming" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Selecciona el país para bloquear" + "value" : "Streaming" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Streaming" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Sélectionnez le pays à bloquer" + "value" : "Streaming" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "בחר מדינה לחסימה" + "value" : "סטרימינג" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "ब्लॉक करने के लिए देश चुनें" + "value" : "स्ट्रीमिंग" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Pilih negara untuk diblokir" + "value" : "Streaming" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Seleziona il paese da bloccare" + "value" : "Streaming" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "ブロックする国を選択" + "value" : "ストリーミング" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "차단할 국가 선택" + "value" : "스트리밍" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Selecteer land om te blokkeren" + "value" : "Streamen" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Wybierz kraj do zablokowania" + "value" : "Streaming" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Streaming" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Selecione o país para bloquear" + "value" : "Streaming" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Selecione o país para bloquear" + "value" : "Streaming" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Выберите страну для блокировки" + "value" : "Стриминг" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Välj land att blockera" + "value" : "Streaming" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "เลือกประเทศที่จะบล็อก" + "value" : "สตรีมมิง" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Виберіть країну для блокування" + "value" : "Стримінг" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Chọn quốc gia để chặn" + "value" : "Phát trực tuyến" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "选择要屏蔽的国家" + "value" : "流媒体" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "選擇要屏蔽的國家" + "value" : "串流" } } } }, - "Selected hosts are routed locally and bypass the tunnel." : { - "comment" : "A description of the functionality of the split rule editor.", - "isCommentAutoGenerated" : true - }, - "Send" : { + "Strong Anonymization" : { + "comment" : "Guidance for someone who has no code yet. Deliberately PLAIN TEXT with no link and no button: app store rules forbid steering a user to an external purchase, but nothing stops us saying where codes come from. Without this, the sheet asks for a code and gives no way to obtain one.", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "إرسال" + "value" : "إخفاء الهوية القوي" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Odeslat" + "value" : "Silná anonymizace" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Senden" + "value" : "Starke Anonymisierung" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Αποστολή" + "value" : "Ισχυρή ανωνυμοποίηση" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Send" + "value" : "Strong Anonymization" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Enviar" + "value" : "Anonimización fuerte" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Enviar" + "value" : "Anonimización fuerte" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Anonimización fuerte" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Envoyer" + "value" : "Anonymisation renforcée" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "שלח" + "value" : "אנונימיזציה חזקה" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "भेजें" + "value" : "मजबूत गुमनामीकरण" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Kirim" + "value" : "Anonimisasi kuat" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Invia" + "value" : "Anonimizzazione avanzata" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "送信" + "value" : "強力な匿名化" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "보내기" + "value" : "강력한 익명화" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Verzenden" + "value" : "Sterke anonimisering" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Wyślij" + "value" : "Silna anonimizacja" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Anonimização forte" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Enviar" + "value" : "Anonimização forte" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Enviar" + "value" : "Anonimização forte" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Отправить" + "value" : "Усиленная анонимизация" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Skicka" + "value" : "Stark anonymisering" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "ส่ง" + "value" : "การทำให้ไม่สามารถระบุตัวตนได้แบบเข้มงวด" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Надіслати" + "value" : "Посилена анонімізація" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Gửi" + "value" : "Ẩn danh mạnh" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "发送" + "value" : "强匿名化" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "發送" + "value" : "強匿名化" } } } }, - "Send me product updates" : { + "Successfully claimed multiplier!" : { + "comment" : "Snackbar shown when the earnings multiplier is claimed successfully. Used in Main/Account/Settings/SettingsView.swift.", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "أرسل لي تحديثات المنتج" + "value" : "تمت المطالبة بالمضاعف بنجاح!" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Zasílejte mi aktualizace produktů" + "value" : "Násobitel byl úspěšně nárokován!" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Produkt-Updates zusenden" + "value" : "Multiplikator erfolgreich beansprucht!" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Αποστολή ενημερώσεων προϊόντος" + "value" : "Η αξίωση του πολλαπλασιαστή ολοκληρώθηκε με επιτυχία!" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Send me product updates" + "value" : "Successfully claimed multiplier!" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Enviarme actualizaciones del producto" + "value" : "¡Multiplicador reclamado correctamente!" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Enviarme actualizaciones del producto" + "value" : "¡Multiplicador reclamado correctamente!" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "¡Multiplicador reclamado correctamente!" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "M'envoyer les mises à jour" + "value" : "Multiplicateur réclamé avec succès !" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "שלח לי עדכוני מוצר" + "value" : "המכפיל נתבע בהצלחה!" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "मुझे प्रोडक्ट अपडेट भेजें" + "value" : "गुणक का दावा सफलतापूर्वक किया गया!" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Kirim pembaruan produk" + "value" : "Pengganda berhasil diklaim!" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Inviami aggiornamenti sul prodotto" + "value" : "Moltiplicatore richiesto con successo!" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "製品の更新情報を受け取る" + "value" : "乗数を受け取りました!" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "제품 업데이트 받기" + "value" : "배율을 성공적으로 받았습니다!" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Stuur mij productupdates" + "value" : "Vermenigvuldiger succesvol geclaimd!" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Wysyłaj mi aktualizacje produktu" + "value" : "Pomyślnie odebrano mnożnik!" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Multiplicador reivindicado com sucesso!" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Enviar atualizações do produto" + "value" : "Multiplicador reivindicado com sucesso!" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Enviar-me atualizações do produto" + "value" : "Multiplicador reivindicado com sucesso!" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Присылать обновления продукта" + "value" : "Множитель успешно получен!" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Skicka produktuppdateringar" + "value" : "Multiplikatorn har hämtats!" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "ส่งการอัปเดตผลิตภัณฑ์ถึงฉัน" + "value" : "รับสิทธิ์ตัวคูณสำเร็จแล้ว!" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Надсилати оновлення продукту" + "value" : "Множник успішно отримано!" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Gửi cập nhật sản phẩm cho tôi" + "value" : "Đã yêu cầu hệ số nhân thành công!" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "向我发送产品更新" + "value" : "已成功领取倍数!" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "向我發送產品更新" + "value" : "已成功申領倍數!" } } } }, - "Send reset link" : { + "Suggested remote DNS servers" : { + "comment" : "A heading for a list of suggested remote DNS servers.", + "isCommentAutoGenerated" : true, "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "إرسال رابط إعادة التعيين" + "value" : "خوادم DNS بعيدة مقترحة" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Odeslat odkaz na obnovení" + "value" : "Navrhované vzdálené servery DNS" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Reset-Link senden" + "value" : "Vorgeschlagene externe DNS-Server" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Αποστολή συνδέσμου επαναφοράς" + "value" : "Προτεινόμενοι απομακρυσμένοι διακομιστές DNS" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Send reset link" + "value" : "Suggested remote DNS servers" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Enviar enlace de restablecimiento" + "value" : "Servidores DNS remotos sugeridos" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Enviar enlace de restablecimiento" + "value" : "Servidores DNS remotos sugeridos" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Servidores DNS remotos sugeridos" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Envoyer le lien de réinitialisation" + "value" : "Serveurs DNS distants suggérés" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "שלח קישור לאיפוס" + "value" : "שרתי DNS מרוחקים מוצעים" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "रीसेट लिंक भेजें" + "value" : "सुझाए गए रिमोट DNS सर्वर" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Kirim tautan reset" + "value" : "Server DNS jarak jauh yang disarankan" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Invia link di reset" + "value" : "Server DNS remoti suggeriti" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "リセットリンクを送信" + "value" : "推奨リモートDNSサーバー" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "재설정 링크 보내기" + "value" : "추천 원격 DNS 서버" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Resetlink versturen" + "value" : "Voorgestelde externe DNS-servers" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Wyślij link do resetowania" + "value" : "Sugerowane zdalne serwery DNS" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Servidores DNS remotos sugeridos" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Enviar link de redefinição" + "value" : "Servidores DNS remotos sugeridos" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Enviar link de reposição" + "value" : "Servidores DNS remotos sugeridos" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Отправить ссылку для сброса" + "value" : "Рекомендуемые удалённые DNS-серверы" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Skicka återställningslänk" + "value" : "Föreslagna fjärr-DNS-servrar" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "ส่งลิงก์รีเซ็ต" + "value" : "เซิร์ฟเวอร์ DNS ระยะไกลที่แนะนำ" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Надіслати посилання для скидання" + "value" : "Рекомендовані віддалені DNS-сервери" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Gửi liên kết đặt lại" + "value" : "Máy chủ DNS từ xa được đề xuất" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "发送重置链接" + "value" : "推荐的远程 DNS 服务器" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "發送重設連結" + "value" : "建議的遠端 DNS 伺服器" } } } }, - "Send us your feedback directly or [join our Discord](https://discord.com/invite/RUNZXMwPRK) for direct support." : { + "Suggestions for the connected country are marked with its color. Turning one on adds it to the remote DNS servers." : { + "comment" : "A description of the suggested DNS servers.", + "isCommentAutoGenerated" : true, "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "أرسل لنا ملاحظاتك مباشرة أو [انضم إلى Discord الخاص بنا](https://discord.com/invite/RUNZXMwPRK) للحصول على دعم مباشر." + "value" : "الاقتراحات الخاصة بالدولة المتصل بها مميّزة بلونها. تشغيل أحدها يضيفه إلى خوادم DNS البعيدة." } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Pošlete nám svou zpětnou vazbu přímo nebo [se připojte k našemu Discordu](https://discord.com/invite/RUNZXMwPRK) pro přímou podporu." + "value" : "Návrhy pro připojenou zemi jsou označeny její barvou. Zapnutím se server přidá mezi vzdálené servery DNS." } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Senden Sie uns Ihr Feedback direkt oder [treten Sie unserem Discord bei](https://discord.com/invite/RUNZXMwPRK) für direkten Support." + "value" : "Vorschläge für das verbundene Land sind mit dessen Farbe markiert. Wenn du einen aktivierst, wird er zu den externen DNS-Servern hinzugefügt." } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Στείλτε μας τα σχόλιά σας απευθείας ή [εγγραφείτε στο Discord μας](https://discord.com/invite/RUNZXMwPRK) για άμεση υποστήριξη." + "value" : "Οι προτάσεις για τη συνδεδεμένη χώρα επισημαίνονται με το χρώμα της. Αν ενεργοποιήσετε μία, προστίθεται στους απομακρυσμένους διακομιστές DNS." } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Send us your feedback directly or [join our Discord](https://discord.com/invite/RUNZXMwPRK) for direct support." + "value" : "Suggestions for the connected country are marked with its color. Turning one on adds it to the remote DNS servers." } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Envíanos tus comentarios directamente o [únete a nuestro Discord](https://discord.com/invite/RUNZXMwPRK) para obtener soporte directo." + "value" : "Las sugerencias del país conectado están marcadas con su color. Al activar una, se añade a los servidores DNS remotos." } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Envíanos tus comentarios directamente o [únete a nuestro Discord](https://discord.com/invite/RUNZXMwPRK) para obtener soporte directo." + "value" : "Las sugerencias del país conectado están marcadas con su color. Al activar una, se agrega a los servidores DNS remotos." + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Las sugerencias del país conectado están marcadas con su color. Al activar una, se agrega a los servidores DNS remotos." } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Envoyez-nous vos commentaires directement ou [rejoignez notre Discord](https://discord.com/invite/RUNZXMwPRK) pour un support direct." + "value" : "Les suggestions pour le pays connecté sont marquées de sa couleur. En activer une l'ajoute aux serveurs DNS distants." } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "שלח לנו משוב ישירות או [הצטרף לדיסקורד שלנו](https://discord.com/invite/RUNZXMwPRK) לתמיכה ישירה." + "value" : "הצעות עבור המדינה המחוברת מסומנות בצבע שלה. הפעלת אחת מהן מוסיפה אותה לשרתי ה-DNS המרוחקים." } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "अपनी प्रतिक्रिया सीधे हमें भेजें या सीधे सहायता के लिए [हमारे Discord से जुड़ें](https://discord.com/invite/RUNZXMwPRK)।" + "value" : "जिस देश से आप कनेक्ट हैं, उसके सुझाव उसी के रंग से चिह्नित होते हैं। किसी को चालू करने पर वह रिमोट DNS सर्वर में जुड़ जाता है।" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Kirimkan masukan Anda langsung atau [bergabunglah dengan Discord kami](https://discord.com/invite/RUNZXMwPRK) untuk dukungan langsung." + "value" : "Saran untuk negara yang terhubung ditandai dengan warnanya. Mengaktifkan salah satunya akan menambahkannya ke server DNS jarak jauh." } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Inviaci direttamente i tuoi feedback o [unisciti al nostro Discord](https://discord.com/invite/RUNZXMwPRK) per un supporto diretto." + "value" : "I suggerimenti per il paese connesso sono contrassegnati con il suo colore. Attivandone uno, viene aggiunto ai server DNS remoti." } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "フィードバックを直接送信するか、直接サポートを受けるために[Discordに参加](https://discord.com/invite/RUNZXMwPRK)してください。" + "value" : "接続中の国の候補にはその国の色が付いています。オンにするとリモートDNSサーバーに追加されます。" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "직접 피드백을 보내거나 직접 지원을 받으려면 [Discord에 가입](https://discord.com/invite/RUNZXMwPRK)하세요." + "value" : "연결된 국가의 추천 항목은 해당 국가의 색상으로 표시됩니다. 켜면 원격 DNS 서버에 추가됩니다." } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Stuur on direct je feedback of word lid van onze [Discord](https://discord.com/invite/RUNZXMwPRK) voor directe ondersteuning." + "value" : "Suggesties voor het verbonden land zijn gemarkeerd met de kleur van dat land. Zet je er een aan, dan wordt die toegevoegd aan de externe DNS-servers." } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Wyślij nam swoją opinię bezpośrednio lub [dołącz do naszego Discorda](https://discord.com/invite/RUNZXMwPRK), aby uzyskać bezpośrednie wsparcie." + "value" : "Sugestie dla połączonego kraju są oznaczone jego kolorem. Włączenie jednej z nich dodaje ją do zdalnych serwerów DNS." + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "As sugestões para o país a que está ligado são marcadas com a respetiva cor. Ao ativar uma, esta é adicionada aos servidores DNS remotos." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Envie seu feedback diretamente ou [junte-se ao nosso Discord](https://discord.com/invite/RUNZXMwPRK) para suporte direto." + "value" : "As sugestões para o país conectado são marcadas com a cor dele. Ao ativar uma, ela é adicionada aos servidores DNS remotos." } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Envie-nos o seu feedback diretamente ou [junte-se ao nosso Discord](https://discord.com/invite/RUNZXMwPRK) para suporte direto." + "value" : "As sugestões para o país a que está ligado são marcadas com a respetiva cor. Ao ativar uma, esta é adicionada aos servidores DNS remotos." } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Отправьте нам свой отзыв напрямую или [присоединитесь к нашему Discord](https://discord.com/invite/RUNZXMwPRK) для прямой поддержки." + "value" : "Предложения для страны подключения отмечены её цветом. Включение добавляет сервер в список удалённых DNS." } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Skicka din feedback direkt till oss eller [gå med i vår Discord](https://discord.com/invite/RUNZXMwPRK) för direkt support." + "value" : "Förslag för det anslutna landet är markerade med dess färg. Att slå på ett lägger till det bland fjärr-DNS-servrarna." } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "ส่งความคิดเห็นของคุณให้เราโดยตรงหรือ[เข้าร่วม Discord ของเรา](https://discord.com/invite/RUNZXMwPRK) เพื่อรับการสนับสนุนโดยตรง" + "value" : "คำแนะนำสำหรับประเทศที่เชื่อมต่ออยู่จะทำเครื่องหมายด้วยสีของประเทศนั้น เมื่อเปิดใช้งานรายการใด รายการนั้นจะถูกเพิ่มเข้าไปในเซิร์ฟเวอร์ DNS ระยะไกล" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Надішліть нам свій відгук безпосередньо або [приєднайтеся до нашого Discord](https://discord.com/invite/RUNZXMwPRK) для прямої підтримки." + "value" : "Пропозиції для країни підключення позначені її кольором. Увімкнення додає сервер до списку віддалених DNS." } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Gửi phản hồi của bạn trực tiếp cho chúng tôi hoặc [tham gia Discord của chúng tôi](https://discord.com/invite/RUNZXMwPRK) để được hỗ trợ trực tiếp." + "value" : "Đề xuất cho quốc gia đang kết nối được đánh dấu bằng màu của quốc gia đó. Bật một đề xuất sẽ thêm nó vào máy chủ DNS từ xa." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "直接向我们发送反馈,或[加入我们的 Discord](https://discord.com/invite/RUNZXMwPRK) 获取直接支持。" + "value" : "适用于已连接国家的推荐项会以该国家的颜色标记。开启后会将其加入远程 DNS 服务器。" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "直接向我們發送反饋,或[加入我們的 Discord](https://discord.com/invite/RUNZXMwPRK) 獲取直接支持。" + "value" : "適用於已連接國家的建議項目會以該國家的顏色標示。開啟後會將其加入遠端 DNS 伺服器。" } } } }, - "Settings" : { + "Support" : { + "comment" : "Used in Shared/ViewModels/SubscriptionBalanceViewModel.swift.", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "الإعدادات" + "value" : "الدعم" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Nastavení" + "value" : "Podpora" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Einstellungen" + "value" : "Support" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Ρυθμίσεις" + "value" : "Υποστήριξη" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Settings" + "value" : "Support" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Ajustes" + "value" : "Soporte" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Configuración" + "value" : "Soporte" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Paramètres" + "value" : "Support" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "הגדרות" + "value" : "תמיכה" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "सेटिंग्स" + "value" : "सहायता" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Pengaturan" + "value" : "Dukungan" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Impostazioni" + "value" : "Supporto" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "設定" + "value" : "サポート" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "설정" + "value" : "지원" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Instellingen" + "value" : "ondersteuning" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Ustawienia" + "value" : "Wsparcie" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Configurações" + "value" : "Suporte" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Definições" + "value" : "Suporte" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Настройки" + "value" : "Поддержка" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Inställningar" + "value" : "Support" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "การตั้งค่า" + "value" : "สนับสนุน" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Налаштування" + "value" : "Підтримка" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Cài đặt" + "value" : "Hỗ trợ" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "设置" + "value" : "支持" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "設定" + "value" : "支援" } } } }, - "Share Logs" : { + "Support us in building a new kind of network that gives instead of takes." : { + "comment" : "Upgrade subscription overlay", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "مشاركة السجلات" + "value" : "ادعمنا في بناء نوع جديد من الشبكات التي تعطي بدلاً من أن تأخذ." } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Sdílet logy" + "value" : "Podpořte nás při budování nového druhu sítě, která dává, místo aby brala." } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Logs teilen" + "value" : "Unterstütze uns beim Aufbau eines neuen Netzwerktyps, der gibt, anstatt zu nehmen." } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Κοινοποίηση αρχείων καταγραφής" + "value" : "Υποστηρίξτε μας στην οικοδόμηση ενός νέου τύπου δικτύου που δίνει αντί να παίρνει." } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Share Logs" + "value" : "Support us in building a new kind of network that gives instead of takes." } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Compartir registros" + "value" : "Apóyanos en la construcción de un nuevo tipo de red que da en lugar de tomar." } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Compartir registros" + "value" : "Apóyanos en la construcción de un nuevo tipo de red que da en lugar de tomar." + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Apóyanos en la construcción de un nuevo tipo de red que da en lugar de tomar." } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Partager les journaux" + "value" : "Soutenez-nous dans la construction d'un nouveau type de réseau qui donne au lieu de prendre." } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "שתף לוגים" + "value" : "תמכו בנו בבניית רשת מסוג חדש שנותנת במקום לקחת." } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "लॉग्स साझा करें" + "value" : "एक नए प्रकार के नेटवर्क के निर्माण में हमारा समर्थन करें जो लेने के बजाय देता है।" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Bagikan log" + "value" : "Dukung kami membangun jaringan baru yang memberi, bukan mengambil." } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Condividi log" + "value" : "Supportaci nella costruzione di un nuovo tipo di rete che dà invece di prendere." } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "ログを共有" + "value" : "奪うのではなく与える新しい種類のネットワークの構築を支援してください。" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "로그 공유" + "value" : "취하지 않고 제공하는 새로운 종류의 네트워크를 구축하는 데 도움을 주세요." } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Logbestanden delen" + "value" : "Steun ons bij het bouwen van een nieuw soort netwerk dat geeft in plaats van neemt." } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Udostępnij logi" - } - }, - "pt-BR" : { - "stringUnit" : { - "state" : "translated", - "value" : "Compartilhar logs" - } - }, - "pt-PT" : { - "stringUnit" : { - "state" : "translated", - "value" : "Partilhar logs" - } - }, - "ru" : { - "stringUnit" : { - "state" : "translated", - "value" : "Поделиться логами" - } - }, - "sv" : { - "stringUnit" : { - "state" : "translated", - "value" : "Dela loggar" - } - }, - "th" : { - "stringUnit" : { - "state" : "translated", - "value" : "แชร์ล็อก" - } - }, - "uk" : { - "stringUnit" : { - "state" : "translated", - "value" : "Поділитися логами" - } - }, - "vi" : { - "stringUnit" : { - "state" : "translated", - "value" : "Chia sẻ nhật ký" - } - }, - "zh-Hans" : { - "stringUnit" : { - "state" : "translated", - "value" : "分享日志" - } - }, - "zh-HK" : { - "stringUnit" : { - "state" : "translated", - "value" : "分享日誌" - } - } - } - }, - "Share URnetwork" : { - "localizations" : { - "ar" : { - "stringUnit" : { - "state" : "translated", - "value" : "مشاركة URnetwork" - } - }, - "cs" : { - "stringUnit" : { - "state" : "translated", - "value" : "Sdílet URnetwork" - } - }, - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "URnetwork teilen" - } - }, - "el" : { - "stringUnit" : { - "state" : "translated", - "value" : "Κοινοποίηση URnetwork" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Share URnetwork" - } - }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Compartir URnetwork" - } - }, - "es-419" : { - "stringUnit" : { - "state" : "translated", - "value" : "Compartir URnetwork" - } - }, - "fr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Partager URnetwork" - } - }, - "he" : { - "stringUnit" : { - "state" : "translated", - "value" : "שתף URnetwork" - } - }, - "hi" : { - "stringUnit" : { - "state" : "translated", - "value" : "URnetwork शेयर करें" - } - }, - "id" : { - "stringUnit" : { - "state" : "translated", - "value" : "Bagikan URnetwork" - } - }, - "it" : { - "stringUnit" : { - "state" : "translated", - "value" : "Condividi URnetwork" - } - }, - "ja" : { - "stringUnit" : { - "state" : "translated", - "value" : "URnetworkを共有" - } - }, - "ko" : { - "stringUnit" : { - "state" : "translated", - "value" : "URnetwork 공유" - } - }, - "nl" : { - "stringUnit" : { - "state" : "translated", - "value" : "Deel URnetwork" + "value" : "Wesprzyj nas w budowaniu nowego rodzaju sieci, która daje zamiast brać." } }, - "pl" : { + "pt" : { "stringUnit" : { "state" : "translated", - "value" : "Udostępnij URnetwork" + "value" : "Apoie-nos na construção de um novo tipo de rede que dá em vez de receber." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Compartilhar URnetwork" + "value" : "Apoie-nos na construção de um novo tipo de rede que dá em vez de receber." } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Partilhar URnetwork" + "value" : "Apoie-nos na construção de um novo tipo de rede que dá em vez de tirar." } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Поделиться URnetwork" + "value" : "Поддержите нас в создании нового типа сети, которая дает, а не забирает." } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Dela URnetwork" + "value" : "Stöd oss i att bygga ett nytt slags nätverk som ger istället för att ta." } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "แชร์ URnetwork" + "value" : "สนับสนุนพวกเราเพื่อสร้างเครือข่ายที่ให้มากกว่ารับกัน" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Поділитися URnetwork" + "value" : "Підтримайте нас у створенні нового типу мережі, яка дає, а не бере." } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Chia sẻ URnetwork" + "value" : "Hãy hỗ trợ chúng tôi xây dựng một mạng lưới mới, biết cho đi thay vì nhận lại." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "分享 URnetwork" + "value" : "支持我们构建一种新的网络,它可以给予而不是索取。" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "分享 URnetwork" + "value" : "支持我們建立一個不是索取而是給予的新型網絡。" } } } }, - "Show" : { + "Supporter" : { + "comment" : "Used in Shared/ViewModels/SubscriptionBalanceViewModel.swift.", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "إظهار" + "value" : "داعم" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Ukázat" + "value" : "Podporovatel" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Anzeigen" + "value" : "Unterstützer" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Εμφάνιση" + "value" : "Υποστηρικτής" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Show" + "value" : "Supporter" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Mostrar" + "value" : "Colaborador" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Mostrar" + "value" : "Colaborador" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Afficher" + "value" : "Soutien" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "הצג" + "value" : "תומך" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "दिखाएं" + "value" : "समर्थक" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Tampilkan" + "value" : "Pendukung" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Mostra" + "value" : "Sostenitore" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "表示" + "value" : "サポーター" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "보기" + "value" : "서포터" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Toon" + "value" : "Supporter" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Pokaż" + "value" : "Wspierający" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Mostrar" + "value" : "Apoiador" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Mostrar" + "value" : "Apoiante" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Показать" + "value" : "Спонсор" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Visa" + "value" : "Supporter" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "แสดง" + "value" : "ผู้สนับสนุน" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Показати" + "value" : "Спонсор" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Hiện" + "value" : "Người ủng hộ" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "显示" + "value" : "支持者" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "顯示" + "value" : "支持者" } } } }, - "Show Provider List" : { + "System" : { + "comment" : "Used in Main/Account/Settings/SettingsForm-macOS.swift.", + "localizations" : { + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Systém" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "System" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Systeem" + } + } + } + }, + "Tap traffic below to route it locally, bypassing the tunnel." : { + "comment" : "A description of how to route traffic locally.", + "isCommentAutoGenerated" : true, "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "إظهار قائمة المزودين" + "value" : "انقر على حركة المرور أدناه لتوجيهها محليًا وتجاوز النفق." } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Zobrazit seznam poskytovatelů" + "value" : "Klepnutím na provoz níže jej nasměrujete lokálně, mimo tunel." } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Anbieter-Liste anzeigen" + "value" : "Tippe unten auf Datenverkehr, um ihn lokal zu leiten und den Tunnel zu umgehen." } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Εμφάνιση λίστας παρόχων" + "value" : "Πατήστε την κίνηση παρακάτω για να δρομολογηθεί τοπικά, παρακάμπτοντας το τούνελ." } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Show Provider List" + "value" : "Tap traffic below to route it locally, bypassing the tunnel." } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Mostrar lista de proveedores" + "value" : "Pulsa el tráfico de abajo para enrutarlo localmente, evitando el túnel." } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Mostrar lista de proveedores" + "value" : "Toca el tráfico de abajo para enrutarlo localmente, evitando el túnel." + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Toca el tráfico de abajo para enrutarlo localmente, evitando el túnel." } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Afficher la liste des fournisseurs" + "value" : "Appuyez sur le trafic ci-dessous pour le router localement, en contournant le tunnel." } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "הצג רשימת ספקים" + "value" : "הקש על התעבורה שלמטה כדי לנתב אותה מקומית ולעקוף את המנהרה." } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "प्रदाता सूची दिखाएं" + "value" : "नीचे किसी ट्रैफ़िक पर टैप करें ताकि वह टनल को बायपास कर स्थानीय रूप से रूट हो।" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Tampilkan Daftar Penyedia" + "value" : "Ketuk lalu lintas di bawah untuk merutekannya secara lokal, melewati terowongan." } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Mostra elenco provider" + "value" : "Tocca il traffico qui sotto per instradarlo localmente, bypassando il tunnel." } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "プロバイダーリストを表示" + "value" : "下のトラフィックをタップすると、トンネルを迂回してローカルにルーティングされます。" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "제공업체 목록 표시" + "value" : "아래 트래픽을 탭하면 터널을 우회하여 로컬로 라우팅됩니다." } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Toon Provider Lijst" + "value" : "Tik hieronder op verkeer om het lokaal te routeren, buiten de tunnel om." } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Pokaż listę dostawców" + "value" : "Dotknij ruchu poniżej, aby kierować go lokalnie, z pominięciem tunelu." + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Toque no tráfego abaixo para o encaminhar localmente, contornando o túnel." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Mostrar lista de provedores" + "value" : "Toque no tráfego abaixo para roteá-lo localmente, contornando o túnel." } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Mostrar lista de fornecedores" + "value" : "Toque no tráfego abaixo para o encaminhar localmente, contornando o túnel." } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Показать список провайдеров" + "value" : "Нажмите на трафик ниже, чтобы направить его локально, в обход туннеля." } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Visa leverantörslista" + "value" : "Tryck på trafik nedan för att dirigera den lokalt, förbi tunneln." } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "แสดงรายชื่อผู้ให้บริการ" + "value" : "แตะทราฟฟิกด้านล่างเพื่อส่งผ่านภายในเครื่องโดยไม่ผ่านอุโมงค์" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Показати список провайдерів" + "value" : "Торкніться трафіку нижче, щоб спрямувати його локально, в обхід тунелю." } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Hiển thị danh sách nhà cung cấp" + "value" : "Chạm vào lưu lượng bên dưới để định tuyến cục bộ, không qua đường hầm." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "显示供应商列表" + "value" : "点击下方流量,将其改走本地路由,绕过隧道。" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "顯示供應商列表" + "value" : "點擊下方流量,將其改走本地路由,繞過隧道。" } } } }, - "Sign in" : { + "Tell us who you really are. Enter the code we sent you to verify your identity." : { + "comment" : "Sub header text explaining why verification is needed.", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "تسجيل الدخول" + "value" : "أخبرنا من أنت حقاً. أدخل الرمز الذي أرسلناه إليك للتحقق من هويتك." } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Přihlásit se" + "value" : "Řekněte nám, kdo skutečně jste. Zadejte kód, který jsme vám zaslali k ověření vaší totožnosti." } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Anmelden" + "value" : "Sagen Sie uns, wer Sie wirklich sind. Geben Sie den Code ein, den wir Ihnen zur Identitätsprüfung gesendet haben." } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Είσοδος" + "value" : "Πείτε μας ποιοι είστε πραγματικά. Εισάγετε τον κωδικό που σας στείλαμε για να επιβεβαιώσετε την ταυτότητά σας." } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Sign in" + "value" : "Tell us who you really are. Enter the code we sent you to verify your identity." } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Iniciar sesión" + "value" : "Dinos quién eres realmente. Introduce el código que te enviamos para verificar tu identidad." } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Iniciar sesión" + "value" : "Dinos quién eres realmente. Ingresa el código que te enviamos para verificar tu identidad." + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dinos quién eres realmente. Ingresa el código que te enviamos para verificar tu identidad." } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Se connecter" + "value" : "Dites-nous qui vous êtes vraiment. Saisissez le code que nous vous avons envoyé pour vérifier votre identité." } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "התחבר" + "value" : "ספר לנו מי אתה באמת. הזן את הקוד ששלחנו לך כדי לאמת את זהותך." } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "साइन इन करें" + "value" : "हमें बताएं कि आप वास्तव में कौन हैं। अपनी पहचान सत्यापित करने के लिए हमारे द्वारा भेजा गया कोड दर्ज करें।" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Masuk" + "value" : "Beri tahu kami siapa Anda sebenarnya. Masukkan kode yang kami kirimkan untuk memverifikasi identitas Anda." } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Accedi" + "value" : "Dicci chi sei veramente. Inserisci il codice che ti abbiamo inviato per verificare la tua identità." } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "サインイン" + "value" : "本人確認をお願いします。送信した認証コードを入力して本人確認を完了してください。" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "로그인" + "value" : "실제 신원을 알려주세요. 본인 확인을 위해 보내드린 코드를 입력해주세요." } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Log in" + "value" : "Vertel ons wie je echt bent. Voer de code in die we je hebben verstuurd om je identiteit te verifiëren " } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Zaloguj się" + "value" : "Powiedz nam, kim naprawdę jesteś. Wprowadź kod, który wysłaliśmy, aby zweryfikować Twoją tożsamość." + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Conte-nos quem você realmente é. Digite o código que enviamos para verificar sua identidade." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Entrar" + "value" : "Diga-nos quem você realmente é. Digite o código que enviamos para verificar sua identidade." } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Iniciar sessão" + "value" : "Diga-nos quem é realmente. Introduza o código que enviámos para verificar a sua identidade." } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Войти" + "value" : "Скажите нам, кто вы на самом деле. Введите код, который мы отправили для подтверждения вашей личности." } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Logga in" + "value" : "Berätta för oss vem du verkligen är. Ange koden vi skickade för att verifiera din identitet." } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "ลงชื่อเข้าใช้" + "value" : "บอกเราว่าคุณคือใคร ป้อนรหัสที่เราส่งให้คุณเพื่อยืนยันตัวตนของคุณ" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Увійти" + "value" : "Скажіть нам, хто ви насправді. Введіть код, який ми надіслали для підтвердження вашої особи." } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Đăng nhập" + "value" : "Hãy cho chúng tôi biết bạn là ai. Nhập mã chúng tôi đã gửi để xác minh danh tính của bạn." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "登录" + "value" : "告诉我们你是谁。输入我们发送给你的验证码以验证你的身份。" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "登入" + "value" : "告訴我們你是誰。輸入我們發送給你的驗證碼以驗證你的身份。" } } } }, - "Sign in with Google" : { + "Thanks for building the new internet with us" : { + "comment" : "Used in Shared/Views/PurchaseSuccessView.swift.", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "تسجيل الدخول باستخدام Google" + "value" : "شكراً لمساعدتك في بناء الإنترنت الجديد معنا" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Přihlásit se pomocí Google" + "value" : "Děkujeme, že s námi budujete nový internet" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Mit Google anmelden" + "value" : "Danke, dass Sie mit uns das neue Internet aufbauen" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Σύνδεση με Google" + "value" : "Ευχαριστούμε που χτίζετε μαζί μας το νέο διαδίκτυο" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Sign in with Google" + "value" : "Thanks for building the new internet with us" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Iniciar sesión con Google" + "value" : "Gracias por construir el nuevo internet con nosotros" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Iniciar sesión con Google" + "value" : "Gracias por construir el nuevo internet con nosotros" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Se connecter avec Google" + "value" : "Merci de construire le nouvel internet avec nous" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "התחבר עם Google" + "value" : "תודה שאתם בונים איתנו את האינטרנט החדש" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "Google से साइन इन करें" + "value" : "हमारे साथ नया इंटरनेट बनाने के लिए धन्यवाद" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Masuk dengan Google" + "value" : "Terima kasih telah membangun internet baru bersama kami" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Accedi con Google" + "value" : "Grazie per costruire il nuovo internet con noi" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "Google でサインイン" + "value" : "新しいインターネットを私たちと共に構築していただきありがとうございます" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "Google로 로그인" + "value" : "저희와 함께 새로운 인터넷을 구축해 주셔서 감사합니다" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Log in met Google" + "value" : "Bedankt dat je samen met ons het nieuwe internet bouwt" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Zaloguj się przez Google" + "value" : "Dziękujemy za budowanie z nami nowego internetu" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Entrar com Google" + "value" : "Obrigado por construir a nova internet conosco" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Iniciar sessão com Google" + "value" : "Obrigado por construir a nova internet connosco" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Войти через Google" + "value" : "Спасибо, что строите новый интернет вместе с нами" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Logga in med Google" + "value" : "Tack för att du hjälper oss bygga det nya internet" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "เข้าสู่ระบบด้วย Google" + "value" : "ขอบคุณที่ช่วยกันสร้างอินเทอร์เน็ตใหม่กับเรา" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Увійти через Google" + "value" : "Дякуємо, що будуєте новий інтернет разом з нами" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Đăng nhập bằng Google" + "value" : "Cảm ơn bạn đã cùng chúng tôi xây dựng internet mới" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "使用 Google 登录" + "value" : "感谢您与我们一起构建新互联网" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "使用 Google 登入" + "value" : "感謝您與我們一起構建新互聯網" } } } }, - "Sign in with Solana" : { + "The leaderboard is the sum of the last 4 payments. It is updated each payment cycle." : { + "comment" : "Call to action", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "تسجيل الدخول باستخدام Solana" + "value" : "لوحة الصدارة هي مجموع آخر 4 دفعات. يتم تحديثها كل دورة دفع." } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Přihlásit se pomocí Solana" + "value" : "Žebříček je součtem posledních 4 plateb. Je aktualizován každý platební cyklus." } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Mit Solana anmelden" + "value" : "Die Bestenliste ist die Summe der letzten 4 Zahlungen. Sie wird bei jedem Zahlungszyklus aktualisiert." } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Σύνδεση με Solana" + "value" : "Ο πίνακας κατάταξης είναι το άθροισμα των τελευταίων 4 πληρωμών. Ενημερώνεται κάθε κύκλο πληρωμής." } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Sign in with Solana" + "value" : "The leaderboard is the sum of the last 4 payments. It is updated each payment cycle." } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Iniciar sesión con Solana" + "value" : "La tabla de clasificación es la suma de los últimos 4 pagos. Se actualiza en cada ciclo de pago." } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Iniciar sesión con Solana" + "value" : "La tabla de clasificación es la suma de los últimos 4 pagos. Se actualiza en cada ciclo de pago." + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "La tabla de clasificación es la suma de los últimos 4 pagos. Se actualiza en cada ciclo de pago." } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Se connecter avec Solana" + "value" : "Le classement est la somme des 4 derniers paiements. Il est mis à jour à chaque cycle de paiement." } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "התחבר עם Solana" + "value" : "טבלת המובילים היא סכום 4 התשלומים האחרונים. היא מתעדכנת בכל מחזור תשלומים." } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "Solana से साइन इन करें" + "value" : "लीडरबोर्ड पिछले 4 भुगतानों का योग है। यह प्रत्येक भुगतान चक्र में अपडेट किया जाता है।" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Masuk dengan Solana" + "value" : "Papan peringkat adalah jumlah dari 4 pembayaran terakhir. Ini diperbarui setiap siklus pembayaran." } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Accedi con Solana" + "value" : "La classifica è la somma degli ultimi 4 pagamenti. Viene aggiornata ad ogni ciclo di pagamento." } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "Solana でサインイン" + "value" : "リーダーボードは過去4回の支払いの合計です。支払いサイクルごとに更新されます。" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "Solana로 로그인" + "value" : "리더보드는 최근 4번의 결제 합계입니다. 각 결제 주기마다 업데이트됩니다." } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Log in met Solana" + "value" : "Het scorebord toont de som van de laatste 4 betalingen. Deze wordt elke betalingscyclus bijgewerkt." } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Zaloguj się przez Solana" + "value" : "Tablica wyników to suma ostatnich 4 płatności. Jest aktualizowana w każdym cyklu płatności." + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "O quadro de líderes é a soma dos últimos 4 pagamentos. É atualizado a cada ciclo de pagamento." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Entrar com Solana" + "value" : "O placar de líderes é a soma dos últimos 4 pagamentos. Ele é atualizado a cada ciclo de pagamento." } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Iniciar sessão com Solana" + "value" : "A tabela de classificação é a soma dos últimos 4 pagamentos. É atualizada a cada ciclo de pagamento." } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Войти через Solana" + "value" : "Таблица лидеров представляет собой сумму последних 4 платежей. Она обновляется в каждом платежном цикле." } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Logga in med Solana" + "value" : "Topplistan är summan av de 4 senaste betalningarna. Den uppdateras vid varje betalningscykel." } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "เข้าสู่ระบบด้วย Solana" + "value" : "กระดานจัดอันดับจะรวมยอดจาก 4 ยอดนำจ่ายล่าสุด และจะอัพเดตแต่ละรอบการนำจ่าย" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Увійти через Solana" + "value" : "Таблиця лідерів — це сума останніх 4 платежів. Вона оновлюється в кожному платіжному циклі." } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Đăng nhập bằng Solana" + "value" : "Bảng xếp hạng là tổng của 4 khoản thanh toán gần nhất. Nó được cập nhật sau mỗi chu kỳ thanh toán." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "使用 Solana 登录" + "value" : "排行榜是最近 4 次付款的总和。它会在每个付款周期更新。" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "使用 Solana 登入" + "value" : "排行榜是最近 4 次付款的總和。它會在每個付款週期更新。" } } } }, - "Sign out" : { + "The strongest security rules are known not to work in %@. There are less secure recommended DNS settings that work better." : { + "comment" : "dns settings", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "تسجيل الخروج" + "value" : "من المعروف أن أقوى قواعد الأمان لا تعمل في %@. هناك إعدادات DNS موصى بها أقل أمانًا تعمل بشكل أفضل." } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Odhlásit se" + "value" : "Nejpřísnější bezpečnostní pravidla v %@ nefungují. Existuje méně bezpečné doporučené nastavení DNS, které funguje lépe." } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Abmelden" + "value" : "Die strengsten Sicherheitsregeln funktionieren in %@ bekanntermaßen nicht. Es gibt weniger sichere, empfohlene DNS-Einstellungen, die besser funktionieren." } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Αποσύνδεση" + "value" : "%@: οι πιο ισχυροί κανόνες ασφαλείας είναι γνωστό ότι δεν λειτουργούν εδώ. Υπάρχουν λιγότερο ασφαλείς προτεινόμενες ρυθμίσεις DNS που λειτουργούν καλύτερα." } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Sign out" + "value" : "The strongest security rules are known not to work in %@. There are less secure recommended DNS settings that work better." } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Cerrar sesión" + "value" : "Se sabe que las reglas de seguridad más estrictas no funcionan en %@. Hay ajustes de DNS recomendados, menos seguros, que funcionan mejor." } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Cerrar sesión" + "value" : "Se sabe que las reglas de seguridad más estrictas no funcionan en %@. Hay una configuración de DNS recomendada, menos segura, que funciona mejor." + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Se sabe que las reglas de seguridad más estrictas no funcionan en %@. Hay una configuración de DNS recomendada, menos segura, que funciona mejor." } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Déconnexion" + "value" : "%@ — les règles de sécurité les plus strictes n'y fonctionnent pas. Des paramètres DNS recommandés, moins sécurisés, fonctionnent mieux." } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "התנתק" + "value" : "ידוע שכללי האבטחה החזקים ביותר אינם עובדים באזור %@. יש הגדרות DNS מומלצות, פחות מאובטחות, שעובדות טוב יותר." } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "साइन आउट" + "value" : "%@ में सबसे मज़बूत सुरक्षा नियम काम नहीं करते। कुछ कम सुरक्षित अनुशंसित DNS सेटिंग्स हैं जो बेहतर काम करती हैं।" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Keluar" + "value" : "Aturan keamanan terkuat diketahui tidak berfungsi di %@. Ada pengaturan DNS yang disarankan, kurang aman tetapi bekerja lebih baik." } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Esci" + "value" : "Le regole di sicurezza più rigide non funzionano in %@. Ci sono impostazioni DNS consigliate, meno sicure, che funzionano meglio." } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "サインアウト" + "value" : "最も強力なセキュリティルールは%@では機能しないことが知られています。安全性は下がりますが、より確実に動作する推奨DNS設定があります。" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "로그아웃" + "value" : "가장 강력한 보안 규칙은 %@에서 작동하지 않는 것으로 알려져 있습니다. 보안 수준은 낮지만 더 잘 작동하는 권장 DNS 설정이 있습니다." } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Log uit" + "value" : "Het is bekend dat de strengste beveiligingsregels niet werken in %@. Er zijn minder veilige aanbevolen DNS-instellingen die beter werken." } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Wyloguj" + "value" : "Najsilniejsze reguły bezpieczeństwa nie działają w %@. Istnieją mniej bezpieczne, zalecane ustawienia DNS, które sprawdzają się lepiej." + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sabe-se que as regras de segurança mais fortes não funcionam em %@. Existem definições de DNS recomendadas, menos seguras, que funcionam melhor." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Sair" + "value" : "Sabe-se que as regras de segurança mais fortes não funcionam em %@. Existem configurações de DNS recomendadas, menos seguras, que funcionam melhor." } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Terminar sessão" + "value" : "Sabe-se que as regras de segurança mais fortes não funcionam em %@. Existem definições de DNS recomendadas, menos seguras, que funcionam melhor." } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Выйти" + "value" : "Известно, что самые строгие правила безопасности не работают в %@. Есть менее безопасные рекомендованные настройки DNS, которые работают лучше." } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Logga ut" + "value" : "De starkaste säkerhetsreglerna fungerar inte i %@. Det finns rekommenderade DNS-inställningar som är mindre säkra men fungerar bättre." } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "ออกจากระบบ" + "value" : "เป็นที่ทราบกันว่ากฎความปลอดภัยที่เข้มงวดที่สุดใช้งานไม่ได้ใน %@ มีการตั้งค่า DNS ที่แนะนำซึ่งปลอดภัยน้อยกว่าแต่ใช้งานได้ดีกว่า" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Вийти" + "value" : "Відомо, що найсуворіші правила безпеки не працюють у %@. Є менш безпечні рекомендовані налаштування DNS, які працюють краще." } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Đăng xuất" + "value" : "Các quy tắc bảo mật mạnh nhất được biết là không hoạt động ở %@. Có những cài đặt DNS được khuyến nghị, kém an toàn hơn nhưng hoạt động tốt hơn." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "退出登录" + "value" : "已知最严格的安全规则在%@无法正常工作。可改用推荐的 DNS 设置,安全性稍低但更好用。" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "登出" + "value" : "已知最嚴格的安全規則在%@無法正常運作。可改用建議的 DNS 設定,安全性稍低但更好用。" } } } }, - "Solflare" : { - "localizations" : { - "cs" : { - "stringUnit" : { - "state" : "translated", - "value" : "Solflare" - } - }, - "nl" : { - "stringUnit" : { - "state" : "translated", - "value" : "Solflare" - } - } - }, - "shouldTranslate" : false - }, - "Something went wrong. Please try again later." : { + "There was an error checking the network name" : { + "comment" : "Used in Authenticate/CreateNetwork/CreateNetworkViewModel.swift.", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "حدث خطأ ما. يرجى المحاولة مرة أخرى لاحقًا." + "value" : "حدث خطأ أثناء التحقق من اسم الشبكة" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Něco se pokazilo. Zkuste to prosím později." + "value" : "Při kontrole názvu sítě došlo k chybě" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Etwas ist schief gelaufen. Bitte versuchen Sie es später noch einmal." + "value" : "Fehler beim Überprüfen des Netzwerknamens" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Κάτι πήγε στραβά. Παρακαλώ δοκιμάστε ξανά αργότερα." + "value" : "Παρουσιάστηκε σφάλμα κατά τον έλεγχο του ονόματος δικτύου" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Something went wrong. Please try again later." + "value" : "There was an error checking the network name" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Algo salió mal. Por favor, inténtalo de nuevo más tarde." + "value" : "Hubo un error al comprobar el nombre de la red" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Algo salió mal. Por favor, inténtalo de nuevo más tarde." + "value" : "Hubo un error al verificar el nombre de la red" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Une erreur s'est produite. Veuillez réessayer plus tard." + "value" : "Une erreur s'est produite lors de la vérification du nom du réseau" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "משהו השתבש. אנא נסה שוב מאוחר יותר." + "value" : "אירעה שגיאה בבדיקת שם הרשת" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "कुछ गलत हो गया। कृपया बाद में पुन: प्रयास करें।" + "value" : "नेटवर्क का नाम जांचने में त्रुटि हुई" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Terjadi kesalahan. Silakan coba lagi nanti." + "value" : "Terjadi kesalahan saat memeriksa nama jaringan" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Qualcosa è andato storto. Per favore riprova più tardi." + "value" : "Si è verificato un errore durante il controllo del nome della rete" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "問題が発生しました。後でもう一度お試しください。" + "value" : "ネットワーク名の確認中にエラーが発生しました" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "문제가 발생했습니다. 나중에 다시 시도해 주세요." + "value" : "네트워크 이름을 확인하는 중 오류가 발생했습니다" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Er ging iets mis. Probeer het later opnieuw." + "value" : "Er is een fout opgetreden bij het controleren van de netwerknaam" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Coś poszło nie tak. Spróbuj ponownie później." + "value" : "Wystąpił błąd podczas sprawdzania nazwy sieci" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Algo deu errado. Por favor, tente novamente mais tarde." + "value" : "Ocorreu um erro ao verificar o nome da rede" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Algo correu mal. Por favor, tente novamente mais tarde." + "value" : "Ocorreu um erro ao verificar o nome da rede" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Что-то пошло не так. Пожалуйста, попробуйте позже." + "value" : "Произошла ошибка при проверке имени сети" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Något gick fel. Försök igen senare." + "value" : "Det uppstod ett fel vid kontroll av nätverksnamnet" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "เกิดข้อผิดพลาด กรุณาลองใหม่อีกครั้งในภายหลัง" + "value" : "เกิดข้อผิดพลาดในการตรวจสอบชื่อเครือข่าย" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Щось пішло не так. Будь ласка, спробуйте пізніше." + "value" : "Виникла помилка під час перевірки назви мережі" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Đã xảy ra sự cố. Vui lòng thử lại sau." + "value" : "Đã xảy ra lỗi khi kiểm tra tên mạng" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "出现问题。请稍后再试。" + "value" : "检查网络名称时出错" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "發生錯誤。請稍後再試。" + "value" : "檢查網絡名稱時出錯" } } } }, - "Spec" : { - - }, - "Split rules" : { - - }, - "Start 2 week free trial" : { + "There was an error connecting your wallet: %@" : { + "comment" : "Snackbar shown when connecting a wallet fails, with the underlying error message. Used in Main/Account/Wallets/WalletsView.swift.", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "ابدأ تجربة مجانية لمدة أسبوعين" + "value" : "حدث خطأ أثناء ربط محفظتك: %@" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Začněte dvoutýdenní zkušební verzi zdarma" + "value" : "Při připojování peněženky došlo k chybě: %@" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Beginnen Sie die zweiwöchige kostenlose Testphase" + "value" : "Beim Verbinden deiner Wallet ist ein Fehler aufgetreten: %@" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Ξεκινήστε τη δωρεάν δοκιμή δύο εβδομάδων" + "value" : "Παρουσιάστηκε σφάλμα κατά τη σύνδεση του πορτοφολιού σας: %@" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Start 2 week free trial" + "value" : "There was an error connecting your wallet: %@" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Comienza la prueba gratuita de 2 semanas" + "value" : "Se ha producido un error al conectar tu billetera: %@" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Comienza la prueba gratuita de 2 semanas" + "value" : "Se produjo un error al conectar tu billetera: %@" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Se produjo un error al conectar tu cartera: %@" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Commencez l’essai gratuit de 2 semaines" + "value" : "Une erreur s'est produite lors de la connexion de votre portefeuille : %@" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "התחל ניסיון חינם לשבועיים" + "value" : "אירעה שגיאה בחיבור הארנק שלך: %@" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "2 सप्ताह की मुफ्त ट्रायल शुरू करें" + "value" : "आपका वॉलेट कनेक्ट करते समय त्रुटि हुई: %@" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Mulai uji coba gratis 2 minggu" + "value" : "Terjadi kesalahan saat menghubungkan dompet Anda: %@" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Inizia la prova gratuita di 2 settimane" + "value" : "Si è verificato un errore durante la connessione del tuo portafoglio: %@" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "2週間の無料トライアルを開始" + "value" : "ウォレットの接続中にエラーが発生しました:%@" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "2주 무료 체험 시작" + "value" : "지갑을 연결하는 중 오류가 발생했습니다: %@" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Begin 2 weken gratis proefperiode" + "value" : "Er is een fout opgetreden bij het verbinden van je wallet: %@" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Rozpocznij 2-tygodniowy bezpłatny okres próbny" + "value" : "Wystąpił błąd podczas łączenia portfela: %@" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ocorreu um erro ao ligar a sua carteira: %@" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Inicie o teste gratuito de 2 semanas" + "value" : "Ocorreu um erro ao conectar sua carteira: %@" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Inicie o teste gratuito de 2 semanas" + "value" : "Ocorreu um erro ao ligar a sua carteira: %@" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Начать двухнедельный бесплатный пробный период" + "value" : "Произошла ошибка при подключении кошелька: %@" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Starta 2 veckors gratis provperiod" + "value" : "Ett fel uppstod när din plånbok skulle anslutas: %@" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "เริ่มทดลองใช้งานฟรี 2 สัปดาห์" + "value" : "เกิดข้อผิดพลาดในการเชื่อมต่อกระเป๋าเงินของคุณ: %@" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Почати двотижневий безкоштовний пробний період" + "value" : "Сталася помилка під час підключення гаманця: %@" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Bắt đầu dùng thử miễn phí 2 tuần" + "value" : "Đã xảy ra lỗi khi kết nối ví của bạn: %@" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "开始2周免费试用" + "value" : "连接钱包时出错:%@" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "開始2星期免費試用" + "value" : "連接錢包時發生錯誤:%@" } } } }, - "Stay connected daily = Earn up to 100 GiB/Day" : { - "extractionState" : "stale", + "There was an error connecting your wallet." : { + "comment" : "Snackbar shown when connecting a wallet fails. Used in Main/Account/Wallets/WalletsView.swift.", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "ابق متصلاً يوميًا = اربح حتى 100 غيغابايت/يوم" + "value" : "حدث خطأ أثناء ربط محفظتك." } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Zůstaňte připojeni denně = Získejte až 100 GiB/den" + "value" : "Při připojování peněženky došlo k chybě." } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Täglich verbunden bleiben = Verdienen Sie bis zu 100 GiB/Tag" + "value" : "Beim Verbinden deiner Wallet ist ein Fehler aufgetreten." } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Μείνετε συνδεδεμένοι καθημερινά = Κερδίστε έως και 100 GiB/ημέρα" + "value" : "Παρουσιάστηκε σφάλμα κατά τη σύνδεση του πορτοφολιού σας." } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Stay connected daily = Earn up to 100 GiB/Day" + "value" : "There was an error connecting your wallet." } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Permanece conectado diariamente = Gana hasta 100 GiB/día" + "value" : "Se ha producido un error al conectar tu billetera." } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Permanece conectado diariamente = Gana hasta 100 GiB/día" + "value" : "Se produjo un error al conectar tu billetera." + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Se produjo un error al conectar tu cartera." } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Restez connecté chaque jour = Gagnez jusqu’à 100 Go/jour" + "value" : "Une erreur s'est produite lors de la connexion de votre portefeuille." } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "הישאר מחובר מדי יום = תרוויח עד 100 GiB/יום" + "value" : "אירעה שגיאה בחיבור הארנק שלך." } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "रोज़ाना जुड़े रहें = प्रति दिन 100 GiB तक कमाएँ" + "value" : "आपका वॉलेट कनेक्ट करते समय त्रुटि हुई।" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Tetap terhubung setiap hari = Dapatkan hingga 100 GiB/hari" + "value" : "Terjadi kesalahan saat menghubungkan dompet Anda." } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Rimani connesso ogni giorno = Guadagna fino a 100 GiB/giorno" + "value" : "Si è verificato un errore durante la connessione del tuo portafoglio." } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "毎日接続を維持 = 最大100 GiB/日を獲得" + "value" : "ウォレットの接続中にエラーが発生しました。" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "매일 연결 유지 = 하루 최대 100 GiB 획득" + "value" : "지갑을 연결하는 중 오류가 발생했습니다." } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Blijf dagelijks verbonden = Verdien tot 100 GiB/dag" + "value" : "Er is een fout opgetreden bij het verbinden van je wallet." } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Pozostań codziennie połączony = Zarabiaj do 100 GiB/dzień" + "value" : "Wystąpił błąd podczas łączenia portfela." + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ocorreu um erro ao ligar a sua carteira." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Fique conectado diariamente = Ganhe até 100 GiB/dia" + "value" : "Ocorreu um erro ao conectar sua carteira." } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Mantenha-se ligado diariamente = Ganhe até 100 GiB/dia" + "value" : "Ocorreu um erro ao ligar a sua carteira." } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Оставайтесь на связи ежедневно = Зарабатывайте до 100 ГиБ/день" + "value" : "Произошла ошибка при подключении кошелька." } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Var ansluten dagligen = Tjäna upp till 100 GiB/dag" + "value" : "Ett fel uppstod när din plånbok skulle anslutas." } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "เชื่อมต่อทุกวัน = รับสูงสุด 100 GiB/วัน" + "value" : "เกิดข้อผิดพลาดในการเชื่อมต่อกระเป๋าเงินของคุณ" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Залишайтеся на зв’язку щодня = Заробляйте до 100 ГіБ/день" + "value" : "Сталася помилка під час підключення гаманця." } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Kết nối hàng ngày = Kiếm tới 100 GiB/ngày" + "value" : "Đã xảy ra lỗi khi kết nối ví của bạn." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "每日保持连接 = 每天赚取高达100 GiB" + "value" : "连接钱包时出错。" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "每天保持連接 = 每日賺取高達100 GiB" + "value" : "連接錢包時發生錯誤。" } } } }, - "Stay in touch" : { + "There was an error creating your network. Please try again later." : { + "comment" : "Snackbar shown when creating the user's network fails. Used in ContentView.swift, Main/Account/AccountRootView.swift and Main/Connect/ConnectView-iOS.swift.", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "ابق على تواصل" + "value" : "حدث خطأ أثناء إنشاء شبكتك. يرجى المحاولة مرة أخرى لاحقًا." } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Zůstaňte v kontaktu" + "value" : "Při vytváření sítě došlo k chybě. Zkuste to prosím později." } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Bleiben Sie in Kontakt" + "value" : "Beim Erstellen deines Netzwerks ist ein Fehler aufgetreten. Bitte versuche es später erneut." } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Μείνετε σε επαφή" + "value" : "Παρουσιάστηκε σφάλμα κατά τη δημιουργία του δικτύου σας. Δοκιμάστε ξανά αργότερα." } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Stay in touch" + "value" : "There was an error creating your network. Please try again later." } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Mantente en contacto" + "value" : "Se ha producido un error al crear tu red. Inténtalo de nuevo más tarde." } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Mantente en contacto" + "value" : "Se produjo un error al crear tu red. Inténtalo de nuevo más tarde." + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Se produjo un error al crear tu red. Inténtalo de nuevo más tarde." } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Restons en contact" + "value" : "Une erreur s'est produite lors de la création de votre réseau. Veuillez réessayer plus tard." } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "שמור על קשר" + "value" : "אירעה שגיאה ביצירת הרשת שלך. אנא נסה שוב מאוחר יותר." } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "संपर्क में रहें" + "value" : "आपका नेटवर्क बनाते समय त्रुटि हुई। कृपया बाद में पुनः प्रयास करें।" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Tetap terhubung" + "value" : "Terjadi kesalahan saat membuat jaringan Anda. Silakan coba lagi nanti." } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Rimani in contatto" + "value" : "Si è verificato un errore durante la creazione della tua rete. Riprova più tardi." } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "最新情報を入手" + "value" : "ネットワークの作成中にエラーが発生しました。後でもう一度お試しください。" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "연락을 유지하세요" + "value" : "네트워크를 만드는 중 오류가 발생했습니다. 나중에 다시 시도해 주세요." } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Blijf in contact" + "value" : "Er is een fout opgetreden bij het aanmaken van je netwerk. Probeer het later opnieuw." } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Pozostań w kontakcie" + "value" : "Wystąpił błąd podczas tworzenia sieci. Spróbuj ponownie później." + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ocorreu um erro ao criar a sua rede. Tente novamente mais tarde." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Mantenha contato" + "value" : "Ocorreu um erro ao criar sua rede. Tente novamente mais tarde." } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Mantenha-se em contacto" + "value" : "Ocorreu um erro ao criar a sua rede. Tente novamente mais tarde." } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Оставайтесь на связи" + "value" : "Произошла ошибка при создании вашей сети. Пожалуйста, попробуйте позже." } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Håll kontakten" + "value" : "Ett fel uppstod när ditt nätverk skulle skapas. Försök igen senare." } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "ติดต่อกับเรา" + "value" : "เกิดข้อผิดพลาดในการสร้างเครือข่ายของคุณ กรุณาลองใหม่ภายหลัง" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Залишайтеся на зв'язку" + "value" : "Сталася помилка під час створення вашої мережі. Будь ласка, спробуйте пізніше." } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Giữ liên lạc" + "value" : "Đã xảy ra lỗi khi tạo mạng của bạn. Vui lòng thử lại sau." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "保持联系" + "value" : "创建网络时出错。请稍后再试。" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "保持聯繫" + "value" : "建立網絡時發生錯誤。請稍後再試。" } } } }, - "Step into the internet as it should be." : { + "There was an error logging in" : { + "comment" : "Used in Authenticate/LoginInitial/LoginInitialView.swift.", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "ادخل إلى الإنترنت كما ينبغي أن يكون." + "value" : "حدث خطأ أثناء تسجيل الدخول" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Vstupte na internet, jak má být." + "value" : "Při přihlašování došlo k chybě" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Erleben Sie das Internet, wie es sein sollte." + "value" : "Beim Anmelden ist ein Fehler aufgetreten" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Μπείτε στο διαδίκτυο όπως θα έπρεπε να είναι." + "value" : "Παρουσιάστηκε σφάλμα κατά τη σύνδεση" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "There was an error logging in" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Adéntrate en internet como debería ser." + "value" : "Se ha producido un error al iniciar sesión" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Ingresa al internet como debería ser." + "value" : "Se produjo un error al iniciar sesión" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Découvrez Internet tel qu'il devrait être." + "value" : "Une erreur s'est produite lors de la connexion" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "צעד אל האינטרנט כפי שהוא אמור להיות." + "value" : "אירעה שגיאה בהתחברות" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "इंटरनेट का अनुभव करें, जैसा कि होना चाहिए।" + "value" : "लॉग इन करते समय त्रुटि हुई" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Masuki internet sebagaimana seharusnya." + "value" : "Terjadi kesalahan saat masuk" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Entra in internet come dovrebbe essere." + "value" : "Si è verificato un errore durante l'accesso" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "本来あるべきインターネットへ一歩踏み出そう。" + "value" : "ログイン中にエラーが発生しました" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "본래의 모습이어야 할 인터넷을 경험하세요." + "value" : "로그인 중 오류가 발생했습니다" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Stap in het internet zoals het zou moeten zijn." + "value" : "Er is een fout opgetreden bij het inloggen" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Wkrocz do internetu takim, jakim powinien być." + "value" : "Wystąpił błąd podczas logowania" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Entre na internet como ela deveria ser." + "value" : "Ocorreu um erro ao fazer login" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Entre na internet como ela deveria ser." + "value" : "Ocorreu um erro ao iniciar sessão" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Войдите в интернет таким, каким он должен быть." + "value" : "Произошла ошибка при входе" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Kliv in i internet som det borde vara." + "value" : "Ett fel uppstod vid inloggningen" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "ก้าวสู่อินเทอร์เน็ตในแบบที่ควรจะเป็น" + "value" : "เกิดข้อผิดพลาดในการเข้าสู่ระบบ" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Увійдіть в інтернет таким, яким він має бути." + "value" : "Сталася помилка під час входу" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Bước vào internet như bản chất của nó." + "value" : "Đã xảy ra lỗi khi đăng nhập" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "体验应有的互联网。" + "value" : "登录时出错" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "體驗應有的互聯網。" + "value" : "登入時發生錯誤" } } } }, - "Streaming" : { - "comment" : "A choice for connecting to a tunnel in streaming mode.", - "isCommentAutoGenerated" : true - }, - "Strong Anonymization" : { + "There was an error sending your feedback. Please try again later." : { + "comment" : "Snackbar shown when sending feedback fails. Used in Main/Feedback/FeedbackView.swift.", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "إخفاء الهوية القوي" + "value" : "حدث خطأ أثناء إرسال ملاحظاتك. يرجى المحاولة مرة أخرى لاحقًا." } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Silná anonymizace" + "value" : "Při odesílání zpětné vazby došlo k chybě. Zkuste to prosím později." } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Starke Anonymisierung" + "value" : "Beim Senden deines Feedbacks ist ein Fehler aufgetreten. Bitte versuche es später erneut." } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Ισχυρή ανωνυμοποίηση" + "value" : "Παρουσιάστηκε σφάλμα κατά την αποστολή των σχολίων σας. Δοκιμάστε ξανά αργότερα." } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Strong Anonymization" + "value" : "There was an error sending your feedback. Please try again later." } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Anonimización fuerte" + "value" : "Se ha producido un error al enviar tus comentarios. Inténtalo de nuevo más tarde." } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Anonimización fuerte" + "value" : "Se produjo un error al enviar tus comentarios. Inténtalo de nuevo más tarde." + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Se produjo un error al enviar tus comentarios. Inténtalo de nuevo más tarde." } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Anonymisation renforcée" + "value" : "Une erreur s'est produite lors de l'envoi de votre retour. Veuillez réessayer plus tard." } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "אנונימיזציה חזקה" + "value" : "אירעה שגיאה בשליחת המשוב שלך. אנא נסה שוב מאוחר יותר." } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "मजबूत अनामीकरण" + "value" : "आपकी प्रतिक्रिया भेजते समय त्रुटि हुई। कृपया बाद में पुनः प्रयास करें।" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Anonimisasi kuat" + "value" : "Terjadi kesalahan saat mengirim umpan balik Anda. Silakan coba lagi nanti." } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Anonimizzazione avanzata" + "value" : "Si è verificato un errore durante l'invio del feedback. Riprova più tardi." } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "強力な匿名化" + "value" : "フィードバックの送信中にエラーが発生しました。後でもう一度お試しください。" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "강력한 익명화" + "value" : "피드백을 보내는 중 오류가 발생했습니다. 나중에 다시 시도해 주세요." } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Sterke anonimisering" + "value" : "Er is een fout opgetreden bij het verzenden van je feedback. Probeer het later opnieuw." } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Silna anonimizacja" + "value" : "Wystąpił błąd podczas wysyłania opinii. Spróbuj ponownie później." + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ocorreu um erro ao enviar o seu feedback. Tente novamente mais tarde." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Anonimização forte" + "value" : "Ocorreu um erro ao enviar seu feedback. Tente novamente mais tarde." } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Anonimização forte" + "value" : "Ocorreu um erro ao enviar o seu feedback. Tente novamente mais tarde." } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Усиленная анонимизация" + "value" : "Произошла ошибка при отправке отзыва. Пожалуйста, попробуйте позже." } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Stark anonymisering" + "value" : "Ett fel uppstod när din feedback skulle skickas. Försök igen senare." } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "การไม่เปิดเผยตัวตนที่แข็งแกร่ง" + "value" : "เกิดข้อผิดพลาดในการส่งข้อเสนอแนะของคุณ กรุณาลองใหม่ภายหลัง" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Посилена анонімізація" + "value" : "Сталася помилка під час надсилання відгуку. Будь ласка, спробуйте пізніше." } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Ẩn danh mạnh" + "value" : "Đã xảy ra lỗi khi gửi phản hồi của bạn. Vui lòng thử lại sau." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "强匿名化" + "value" : "发送反馈时出错。请稍后再试。" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "強匿名化" + "value" : "發送反饋時發生錯誤。請稍後再試。" } } } }, - "Suggested remote DNS servers" : { - "comment" : "A heading for a list of suggested remote DNS servers.", - "isCommentAutoGenerated" : true - }, - "Suggestions for the connected country are marked with its color. Turning one on adds it to the remote DNS servers." : { - "comment" : "A description of the suggested DNS servers.", - "isCommentAutoGenerated" : true - }, - "Support" : { + "There was an error updating the device name." : { + "comment" : "Snackbar shown when saving the device name fails. Used in Main/Account/Settings/SettingsView.swift.", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "الدعم" + "value" : "حدث خطأ أثناء تحديث اسم الجهاز." } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Podpora" + "value" : "Při aktualizaci názvu zařízení došlo k chybě." } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Support" + "value" : "Beim Aktualisieren des Gerätenamens ist ein Fehler aufgetreten." } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Υποστήριξη" + "value" : "Παρουσιάστηκε σφάλμα κατά την ενημέρωση του ονόματος της συσκευής." } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Support" + "value" : "There was an error updating the device name." } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Soporte" + "value" : "Se ha producido un error al actualizar el nombre del dispositivo." } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Soporte" + "value" : "Se produjo un error al actualizar el nombre del dispositivo." + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Se produjo un error al actualizar el nombre del dispositivo." } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Support" + "value" : "Une erreur s'est produite lors de la mise à jour du nom de l'appareil." } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "תמיכה" + "value" : "אירעה שגיאה בעדכון שם המכשיר." } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "सहायता" + "value" : "डिवाइस का नाम अपडेट करते समय त्रुटि हुई।" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Dukungan" + "value" : "Terjadi kesalahan saat memperbarui nama perangkat." } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Supporto" + "value" : "Si è verificato un errore durante l'aggiornamento del nome del dispositivo." } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "サポート" + "value" : "デバイス名の更新中にエラーが発生しました。" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "지원" + "value" : "기기 이름을 업데이트하는 중 오류가 발생했습니다." } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "ondersteuning" + "value" : "Er is een fout opgetreden bij het bijwerken van de apparaatnaam." } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Wsparcie" + "value" : "Wystąpił błąd podczas aktualizacji nazwy urządzenia." + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ocorreu um erro ao atualizar o nome do dispositivo." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Suporte" + "value" : "Ocorreu um erro ao atualizar o nome do dispositivo." } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Suporte" + "value" : "Ocorreu um erro ao atualizar o nome do dispositivo." } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Поддержка" + "value" : "Произошла ошибка при обновлении имени устройства." } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Support" + "value" : "Ett fel uppstod när enhetsnamnet skulle uppdateras." } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "สนับสนุน" + "value" : "เกิดข้อผิดพลาดในการอัปเดตชื่ออุปกรณ์" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Підтримка" + "value" : "Сталася помилка під час оновлення назви пристрою." } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Hỗ trợ" + "value" : "Đã xảy ra lỗi khi cập nhật tên thiết bị." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "支持" + "value" : "更新设备名称时出错。" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "支援" + "value" : "更新裝置名稱時發生錯誤。" } } } }, - "Support us in building a new kind of network that gives instead of takes." : { + "There was an error validating your auth code. Please try again or generate a new one" : { + "comment" : "Used in Authenticate/LoginInitial/AuthCodeLoginSheet.swift.", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "ادعمنا في بناء نوع جديد من الشبكات التي تعطي بدلاً من أن تأخذ." + "value" : "حدث خطأ أثناء التحقق من رمز المصادقة الخاص بك. يرجى المحاولة مرة أخرى أو إنشاء رمز جديد." } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Podpořte nás v budování nového typu sítě, která dává, místo aby brala." + "value" : "Došlo k chybě při ověřování vašeho ověřovacího kódu. Zkuste to prosím znovu nebo vygenerujte nový kód." } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Unterstützen Sie uns beim Aufbau eines neuartigen Netzwerks, das gibt statt nimmt." + "value" : "Beim Validieren Ihres Auth-Codes ist ein Fehler aufgetreten. Bitte versuchen Sie es erneut oder generieren Sie einen neuen." } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Υποστηρίξτε μας στη δημιουργία ενός νέου είδους δικτύου που δίνει αντί να παίρνει." + "value" : "Υπήρξε σφάλμα κατά την επικύρωση του κωδικού ταυτοποίησης. Παρακαλώ δοκιμάστε ξανά ή δημιουργήστε έναν νέο." } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Support us in building a new kind of network that gives instead of takes." + "value" : "There was an error validating your auth code. Please try again or generate a new one" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Apóyanos en la construcción de un nuevo tipo de red que da en lugar de tomar." + "value" : "Hubo un error al validar tu código de autenticación. Por favor, inténtalo de nuevo o genera uno nuevo." } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Apóyanos en la construcción de un nuevo tipo de red que da en lugar de tomar." + "value" : "Hubo un error al validar tu código de autenticación. Por favor, inténtalo de nuevo o genera uno nuevo." } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Soutenez-nous dans la création d'un nouveau type de réseau qui donne au lieu de prendre." + "value" : "Une erreur s’est produite lors de la validation de votre code d’authentification. Veuillez réessayer ou en générer un nouveau." } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "תמכו בנו בבניית רשת מסוג חדש שנותנת במקום לקחת." + "value" : "אירעה שגיאה באימות קוד האימות שלך. נסה שוב או צור קוד חדש." } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "एक नए प्रकार का नेटवर्क बनाने में हमारा समर्थन करें जो लेने के बजाय देता है।" + "value" : "आपके ऑथ कोड को सत्यापित करने में त्रुटि हुई। कृपया फिर से प्रयास करें या नया कोड जनरेट करें।" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Dukung kami membangun jaringan baru yang memberi, bukan mengambil." + "value" : "Terjadi kesalahan saat memvalidasi kode autentikasi Anda. Silakan coba lagi atau buat kode baru." } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Supportaci nella costruzione di un nuovo tipo di rete che dà invece di prendere." + "value" : "Si è verificato un errore durante la validazione del codice di autenticazione. Riprova o generane uno nuovo." } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "受け取るだけでなく与えることを大切にする、新しい形のネットワーク作りにご協力ください。" + "value" : "認証コードの検証中にエラーが発生しました。もう一度お試しください、または新しいコードを生成してください。" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "취하는 대신 주는 새로운 종류의 네트워크를 구축하는 데 동참해 주세요." + "value" : "인증 코드 확인 중 오류가 발생했습니다. 다시 시도하거나 새 코드를 생성하세요." } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Steun ons bij het bouwen van een nieuw soort netwerk dat geeft in plaats van neemt." + "value" : "Er is een fout opgetreden bij het valideren van uw auth-code. Probeer het opnieuw of genereer een nieuwe." } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Wesprzyj nas w budowaniu nowego rodzaju sieci, która daje zamiast brać." + "value" : "Wystąpił błąd podczas weryfikacji kodu autoryzacyjnego. Proszę spróbować ponownie lub wygenerować nowy kod." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Apoie-nos na construção de um novo tipo de rede que dá em vez de tirar." + "value" : "Ocorreu um erro ao validar seu código de autenticação. Tente novamente ou gere um novo." } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Apoie-nos na construção de um novo tipo de rede que dá em vez de tirar." + "value" : "Ocorreu um erro ao validar o seu código de autenticação. Tente novamente ou gere um novo." } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Поддержите нас в создании нового типа сети, которая дает, а не берет." + "value" : "Ошибка при проверке вашего кода авторизации. Пожалуйста, попробуйте снова или сгенерируйте новый." } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Stöd oss i att bygga ett nytt slags nätverk som ger istället för att ta." + "value" : "Det uppstod ett fel när din autentiseringskod validerades. Försök igen eller generera en ny." } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "สนับสนุนเราในการสร้างเครือข่ายรูปแบบใหม่ที่ให้แทนที่จะรับ" + "value" : "เกิดข้อผิดพลาดในการตรวจสอบรหัสยืนยันของคุณ โปรดลองอีกครั้งหรือสร้างรหัสใหม่" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Підтримайте нас у створенні нового типу мережі, яка дає, а не бере." + "value" : "Сталася помилка під час перевірки коду авторизації. Спробуйте ще раз або згенеруйте новий." } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Hãy hỗ trợ chúng tôi xây dựng một mạng lưới mới, biết cho đi thay vì nhận lại." + "value" : "Có lỗi khi xác thực mã của bạn. Vui lòng thử lại hoặc tạo mã mới." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "支持我们构建一个不是索取而是给予的新型网络。" + "value" : "验证您的认证码时出错。请重试或生成新的认证码。" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "支持我們建立一個不是索取而是給予的新型網絡。" + "value" : "驗證你的認證碼時發生錯誤。請再試一次或產生新的認證碼。" } } } }, - "Supporter" : { + "These wallets are not affiliated or controlled by URnetwork. We will send earnings into the connected wallet." : { + "comment" : "Used in Main/Account/Wallets/ConnectWalletSheet/ConnectWalletSheetView.swift.", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "داعم" + "value" : "هذه المحافظ غير مرتبطة أو متحكم بها من قبل URnetwork. سنقوم بإرسال الأرباح إلى المحفظة المتصلة." } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Podporovatel" + "value" : "Tyto peněženky nejsou přidružené ani kontrolované společností URnetwork. Výdělky budeme posílat do připojené peněženky." } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Unterstützer" + "value" : "Diese Wallets sind nicht mit URnetwork verbunden oder werden von URnetwork kontrolliert. Wir senden Einnahmen an die verbundene Wallet." } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Υποστηρικτής" + "value" : "Αυτά τα πορτοφόλια δεν συνδέονται ούτε ελέγχονται από το URnetwork. Θα στείλουμε τα κέρδη στο συνδεδεμένο πορτοφόλι." } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Supporter" + "value" : "These wallets are not affiliated or controlled by URnetwork. We will send earnings into the connected wallet." } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Colaborador" + "value" : "Estas carteras no están afiliadas ni controladas por URnetwork. Enviaremos las ganancias a la cartera conectada." } }, - "es-419" : { + "es-MX" : { "stringUnit" : { "state" : "translated", - "value" : "Colaborador" + "value" : "Estas billeteras no están afiliadas ni controladas por URnetwork. Enviaremos las ganancias a la billetera conectada." } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Soutien" + "value" : "Ces portefeuilles ne sont pas affiliés ni contrôlés par URnetwork. Nous enverrons les gains dans le portefeuille connecté." } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "תומך" + "value" : "ארנקים אלה אינם מסונפים או נשלטים על ידי URnetwork. נשלח את הרווחים לארנק המחובר." } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "समर्थक" - } - }, - "id" : { - "stringUnit" : { - "state" : "translated", - "value" : "Pendukung" + "value" : "ये वॉलेट URnetwork से संबद्ध या नियंत्रित नहीं हैं। हम कनेक्टेड वॉलेट में कमाई भेजेंगे।" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Sostenitore" + "value" : "Questi portafogli non sono affiliati né controllati da URnetwork. Invieremo i guadagni nel portafoglio collegato." } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "サポーター" + "value" : "これらのウォレットはURnetworkと提携しておらず、URnetworkによって管理されていません。収益は接続されたウォレットに送金されます。" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "서포터" + "value" : "이 지갑들은 URnetwork와 제휴되거나 통제되지 않습니다. 수익금은 연결된 지갑으로 보내드립니다." } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Supporter" + "value" : "Deze wallets zijn niet gelieerd aan of gecontroleerd door URnetwork. We zullen verdiensten naar de verbonden wallet sturen." } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Wspierający" + "value" : "Te portfele nie są powiązane ani kontrolowane przez URnetwork. Wyślemy zarobki do połączonego portfela." } }, - "pt-BR" : { + "pt" : { "stringUnit" : { "state" : "translated", - "value" : "Apoiador" + "value" : "Estas carteiras não são afiliadas nem controladas pela URnetwork. Enviaremos os ganhos para a carteira conectada." } }, - "pt-PT" : { + "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Apoiante" + "value" : "Estas carteiras não são afiliadas nem controladas pela URnetwork. Enviaremos os ganhos para a carteira conectada." } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Спонсор" + "value" : "Эти кошельки не связаны и не контролируются URnetwork. Мы отправим заработок в подключенный кошелек." } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Supporter" + "value" : "Dessa plånböcker är inte anslutna till eller kontrollerade av URnetwork. Vi kommer att skicka intäkter till den anslutna plånboken." } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "ผู้สนับสนุน" + "value" : "กระเป๋าเงินเหล่านี้ไม่ได้เป็นพันธมิตรหรือควบคุมโดย URnetwork เราจะส่งรายได้ไปยังกระเป๋าเงินที่เชื่อมต่อ" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Спонсор" + "value" : "Ці гаманці не пов'язані та не контролюються URnetwork. Ми надішлемо заробіток на підключений гаманець." } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Người ủng hộ" + "value" : "Các ví này không liên kết hoặc được kiểm soát bởi URnetwork. Chúng tôi sẽ gửi thu nhập vào ví đã kết nối." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "支持者" + "value" : "这些钱包与URnetwork无关,也不受URnetwork控制。我们将收益发送到连接的钱包中。" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "支持者" + "value" : "這些錢包與URnetwork無關,也不受URnetwork控制。我們將收益發送到已連接的錢包中。" } } } }, - "System" : { + "This code has been used up" : { + "comment" : "Call to action", "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "تم استخدام هذا الرمز بالكامل" + } + }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Systém" + "value" : "Tento kód byl již vyčerpán" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dieser Code wurde bereits verwendet" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Αυτός ο κωδικός έχει εξαντληθεί" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "This code has been used up" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Este código ya ha sido utilizado" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "Este código ya ha sido utilizado" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Este código ya ha sido utilizado" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ce code a déjà été utilisé" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "הקוד הזה נוצל במלואו" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "यह कोड पहले ही उपयोग हो चुका है" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kode ini sudah digunakan" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Questo codice è già stato utilizzato" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "このコードはすでに使われています" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "이 코드는 이미 모두 사용되었습니다" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Systeem" + "value" : "Deze code is al gebruikt" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ten kod został już wykorzystany" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Este código já foi utilizado" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Este código já foi utilizado" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Este código já foi utilizado" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Этот код уже был использован" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Denna kod har redan använts" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "โค้ดนี้ถูกใช้หมดแล้ว" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Цей код вже використано" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mã này đã được sử dụng hết" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "此代码已被用完" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "此代碼已被用完" } } } }, - "Tap traffic below to route it locally, bypassing the tunnel." : { - "comment" : "A description of how to route traffic locally.", - "isCommentAutoGenerated" : true - }, - "Tell us who you really are. Enter the code we sent you to verify your identity." : { + "This code is not valid" : { + "comment" : "Call to action", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "أخبرنا من أنت حقاً. أدخل الرمز الذي أرسلناه إليك للتحقق من هويتك." + "value" : "هذا الرمز غير صالح" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Řekněte nám, kdo skutečně jste. Zadejte kód, který jsme vám zaslali k ověření vaší totožnosti." + "value" : "Tento kód není platný" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Sagen Sie uns, wer Sie wirklich sind. Geben Sie den Code ein, den wir Ihnen zur Identitätsprüfung gesendet haben." + "value" : "Dieser Code ist ungültig" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Πείτε μας ποιοι είστε πραγματικά. Εισάγετε τον κωδικό που σας στείλαμε για να επιβεβαιώσετε την ταυτότητά σας." + "value" : "Αυτός ο κωδικός δεν είναι έγκυρος" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Tell us who you really are. Enter the code we sent you to verify your identity." + "value" : "This code is not valid" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Dinos quién eres realmente. Introduce el código que te enviamos para verificar tu identidad." + "value" : "Este código no es válido" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Dinos quién eres realmente. Ingresa el código que te enviamos para verificar tu identidad." + "value" : "Este código no es válido" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Este código no es válido" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Dites-nous qui vous êtes vraiment. Saisissez le code que nous vous avons envoyé pour vérifier votre identité." + "value" : "Ce code n'est pas valide" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "ספר לנו מי אתה באמת. הזן את הקוד ששלחנו לך כדי לאמת את זהותך." + "value" : "קוד זה אינו תקף" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "हमें बताएं कि आप वास्तव में कौन हैं। अपनी पहचान सत्यापित करने के लिए हमारे द्वारा भेजा गया कोड दर्ज करें।" + "value" : "यह कोड मान्य नहीं है" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Beri tahu kami siapa Anda sebenarnya. Masukkan kode yang kami kirimkan untuk memverifikasi identitas Anda." + "value" : "Kode ini tidak valid" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Dicci chi sei veramente. Inserisci il codice che ti abbiamo inviato per verificare la tua identità." + "value" : "Questo codice non è valido" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "本人確認をお願いします。送信した認証コードを入力して本人確認を完了してください。" + "value" : "このコードは無効です" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "실제 신원을 알려주세요. 본인 확인을 위해 보내드린 코드를 입력해주세요." + "value" : "이 코드는 유효하지 않습니다" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Vertel ons wie je echt bent. Voer de code in die we je hebben verstuurd om je identiteit te verifiëren " + "value" : "Deze code is niet geldig" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Powiedz nam, kim naprawdę jesteś. Wprowadź kod, który wysłaliśmy, aby zweryfikować Twoją tożsamość." + "value" : "Ten kod jest nieprawidłowy" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Este código não é válido" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Diga-nos quem você realmente é. Digite o código que enviamos para verificar sua identidade." + "value" : "Este código não é válido" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Diga-nos quem é realmente. Introduza o código que enviámos para verificar a sua identidade." + "value" : "Este código não é válido" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Скажите нам, кто вы на самом деле. Введите код, который мы отправили для подтверждения вашей личности." + "value" : "Этот код недействителен" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Berätta för oss vem du verkligen är. Ange koden vi skickade för att verifiera din identitet." + "value" : "Den här koden är inte giltig" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "บอกเราว่าคุณคือใคร ป้อนรหัสที่เราส่งให้คุณเพื่อยืนยันตัวตนของคุณ" + "value" : "โค้ดนี้ไม่ถูกต้อง" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Скажіть нам, хто ви насправді. Введіть код, який ми надіслали для підтвердження вашої особи." + "value" : "Цей код недійсний" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Hãy cho chúng tôi biết bạn là ai. Nhập mã chúng tôi đã gửi để xác minh danh tính của bạn." + "value" : "Mã này không hợp lệ" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "告诉我们你是谁。输入我们发送给你的验证码以验证你的身份。" + "value" : "此代码无效" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "告訴我們你是誰。輸入我們發送給你的驗證碼以驗證你的身份。" + "value" : "此代碼無效" } } } }, - "Thanks for building the new internet with us" : { + "This network name is already taken" : { + "comment" : "Error for network name input to display when a network name is already taken.", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "شكراً لمساعدتك في بناء الإنترنت الجديد معنا" + "value" : "اسم الشبكة مستخدم بالفعل" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Děkujeme, že s námi budujete nový internet" + "value" : "Tento název sítě je již obsazen" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Danke, dass Sie mit uns das neue Internet aufbauen" + "value" : "Dieser Netzwerkname ist bereits vergeben" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Ευχαριστούμε που χτίζετε μαζί μας το νέο διαδίκτυο" + "value" : "Αυτό το όνομα δικτύου χρησιμοποιείται ήδη" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Thanks for building the new internet with us" + "value" : "This network name is already taken" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Gracias por construir el nuevo internet con nosotros" + "value" : "Este nombre de red ya está en uso" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Gracias por construir el nuevo internet con nosotros" + "value" : "Este nombre de red ya está en uso" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Este nombre de red ya está en uso" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Merci de construire le nouvel internet avec nous" + "value" : "Ce nom de réseau est déjà pris" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "תודה שאתם בונים איתנו את האינטרנט החדש" + "value" : "שם הרשת הזה כבר תפוס" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "हमारे साथ नया इंटरनेट बनाने के लिए धन्यवाद" + "value" : "यह नेटवर्क नाम पहले से ही लिया जा चुका है" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Terima kasih telah membangun internet baru bersama kami" + "value" : "Nama jaringan ini sudah digunakan" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Grazie per costruire il nuovo internet con noi" + "value" : "Questo nome di rete è già in uso" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "新しいインターネットを私たちと共に構築していただきありがとうございます" + "value" : "このネットワーク名は既に使用されています" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "저희와 함께 새로운 인터넷을 구축해 주셔서 감사합니다" + "value" : "이 네트워크 이름은 이미 사용 중입니다" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Bedankt dat je samen met ons het nieuwe internet bouwt" + "value" : "Deze netwerknaam is al in gebruik" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Dziękujemy za budowanie z nami nowego internetu" + "value" : "Ta nazwa sieci jest już zajęta" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Este nome de rede já está em uso" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Obrigado por construir a nova internet conosco" + "value" : "Este nome de rede já está em uso" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Obrigado por construir a nova internet connosco" + "value" : "Este nome de rede já está em uso" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Спасибо, что строите новый интернет вместе с нами" + "value" : "Это имя сети уже занято" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Tack för att du hjälper oss bygga det nya internet" + "value" : "Detta nätverksnamn är redan taget" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "ขอบคุณที่ช่วยกันสร้างอินเทอร์เน็ตใหม่กับเรา" + "value" : "ชื่อเครือข่ายนี้ถูกใช้ไปแล้ว" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Дякуємо, що будуєте новий інтернет разом з нами" + "value" : "Ця назва мережі вже зайнята" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Cảm ơn bạn đã cùng chúng tôi xây dựng internet mới" + "value" : "Tên mạng này đã được sử dụng" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "感谢您与我们一起构建新互联网" + "value" : "此网络名称已被使用" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "感謝您與我們一起構建新互聯網" + "value" : "此網絡名稱已被使用" } } } }, - "The leaderboard is the sum of the last 4 payments. It is updated each payment cycle." : { + "To start earning, connect your Solana wallet to URnetwork." : { + "comment" : "Used in Main/Account/Wallets/EmptyWallets/EmptyWalletsView.swift.", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "لوحة المتصدرين هي مجموع آخر 4 مدفوعات. يتم تحديثها في كل دورة دفع." + "value" : "للبدء في الكسب، قم بربط محفظة Solana الخاصة بك مع URnetwork." } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Žebříček je součtem posledních 4 plateb. Je aktualizován každý platební cyklus." + "value" : "Chcete-li začít vydělávat, připojte svou peněženku Solana k síti URnetwork." } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Die Rangliste ist die Summe der letzten 4 Zahlungen. Sie wird mit jedem Zahlungszyklus aktualisiert." + "value" : "Um Geld zu verdienen, verbinden Sie Ihre Solana Wallet mit URnetwork." } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Ο πίνακας κατάταξης είναι το άθροισμα των τελευταίων 4 πληρωμών. Ενημερώνεται σε κάθε κύκλο πληρωμών." + "value" : "Για να αρχίσετε να κερδίζετε, συνδέστε το πορτοφόλι Solana σας στο URnetwork." } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "The leaderboard is the sum of the last 4 payments. It is updated each payment cycle." + "value" : "To start earning, connect your Solana wallet to URnetwork." } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "La tabla de clasificación es la suma de los últimos 4 pagos. Se actualiza en cada ciclo de pago." + "value" : "Para empezar a ganar, conecta tu cartera Solana a URnetwork." } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "La tabla de clasificación es la suma de los últimos 4 pagos. Se actualiza en cada ciclo de pago." + "value" : "Para comenzar a ganar, conecta tu billetera Solana a URnetwork." } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Le classement est la somme des 4 derniers paiements. Il est mis à jour à chaque cycle de paiement." + "value" : "Pour commencer à gagner, connectez votre portefeuille Solana à URnetwork." } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "טבלת המובילים היא סכום 4 התשלומים האחרונים. היא מתעדכנת בכל מחזור תשלומים." + "value" : "כדי להתחיל להרוויח, חבר את ארנק Solana שלך ל-URnetwork." } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "लीडरबोर्ड पिछले 4 भुगतानों का योग है. यह प्रत्येक भुगतान चक्र में अपडेट किया जाता है." + "value" : "कमाई शुरू करने के लिए, अपना Solana वॉलेट URnetwork से कनेक्ट करें।" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Papan peringkat adalah jumlah dari 4 pembayaran terakhir. Ini diperbarui setiap siklus pembayaran." + "value" : "Untuk mulai menghasilkan, hubungkan dompet Solana Anda ke URnetwork." } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "La classifica è la somma degli ultimi 4 pagamenti. Viene aggiornata ad ogni ciclo di pagamento." + "value" : "Per iniziare a guadagnare, collega il tuo portafoglio Solana a URnetwork." } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "ランキングボードは、過去 4 回の支払いの合計です。支払いサイクルごとに更新されます。" + "value" : "収益を得るには、SolanaウォレットをURnetworkに接続してください。" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "리더보드는 최근 4번의 결제 합계입니다. 각 결제 주기마다 업데이트됩니다." + "value" : "수익을 얻으려면 Solana 월렛을 URnetwork에 연결하세요." } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Het scorebord toont de som van de laatste 4 betalingen. Het wordt bijgewerkt na elke betaalcyclus." + "value" : "Om te beginnen met verdienen, verbind je Solana wallet met URnetwork" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Ranking to suma 4 ostatnich płatności. Jest aktualizowany w każdym cyklu płatności." + "value" : "Aby zacząć zarabiać, połącz swój portfel Solana z URnetwork." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "A tabela de classificação é a soma dos últimos 4 pagamentos. Ela é atualizada a cada ciclo de pagamento." + "value" : "Para começar a ganhar, conecte sua carteira Solana ao URnetwork." } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "A tabela de classificação é a soma dos últimos 4 pagamentos. É atualizada a cada ciclo de pagamento." + "value" : "Para começar a ganhar, ligue a sua carteira Solana ao URnetwork." } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Таблица лидеров — это сумма последних 4 платежей. Она обновляется в каждом платежном цикле." + "value" : "Чтобы начать зарабатывать, подключите ваш кошелек Solana к URnetwork." } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Topplistan är summan av de 4 senaste betalningarna. Den uppdateras vid varje betalningscykel." + "value" : "För att börja tjäna, anslut din Solana-plånbok till URnetwork." } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "ลีดเดอร์บอร์ดคือผลรวมของการชำระเงิน 4 ครั้งล่าสุด โดยจะมีการอัปเดตในแต่ละรอบการชำระเงิน" + "value" : "เพื่อเริ่มรับรายได้ เชื่อมต่อกระเป๋าเงิน Solana ของคุณกับ URnetwork" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Таблиця лідерів — це сума останніх 4 платежів. Вона оновлюється в кожному платіжному циклі." + "value" : "Щоб почати заробляти, підключіть свій гаманець Solana до URnetwork." } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Bảng xếp hạng là tổng của 4 khoản thanh toán gần nhất. Nó được cập nhật sau mỗi chu kỳ thanh toán." + "value" : "Để bắt đầu kiếm tiền, kết nối ví Solana của bạn với URnetwork." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "排行榜是最近 4 次付款的总和。它会在每个付款周期更新。" + "value" : "要开始赚取收益,请将您的 Solana 钱包连接到 URnetwork。" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "排行榜是最近 4 次付款的總和。它會在每個付款週期更新。" + "value" : "要開始賺取收益,請將您的 Solana 錢包連接到 URnetwork。" } } } }, - "The strongest security rules are known not to work in %@. There are less secure recommended DNS settings that work better." : { - - }, - "There was an error checking the network name" : { + "total payouts" : { + "comment" : "Wallets Screen", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "حدث خطأ أثناء التحقق من اسم الشبكة" + "value" : "إجمالي المدفوعات" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Při kontrole názvu sítě došlo k chybě" + "value" : "celkových výplat" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Fehler beim Überprüfen des Netzwerknamens" + "value" : "Gesamtauszahlungen" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Παρουσιάστηκε σφάλμα κατά τον έλεγχο του ονόματος δικτύου" + "value" : "συνολικές πληρωμές" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "There was an error checking the network name" + "value" : "total payouts" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Hubo un error al comprobar el nombre de la red" + "value" : "Pagos totales" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Hubo un error al verificar el nombre de la red" + "value" : "pagos totales" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Total de pagos" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Une erreur s'est produite lors de la vérification du nom du réseau" + "value" : "total des paiements" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "אירעה שגיאה בבדיקת שם הרשת" + "value" : "סך התשלומים" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "नेटवर्क का नाम जांचने में त्रुटि हुई" + "value" : "कुल भुगतान" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Terjadi kesalahan saat memeriksa nama jaringan" + "value" : "total pembayaran" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Si è verificato un errore durante il controllo del nome della rete" + "value" : "pagamenti totali" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "ネットワーク名の確認中にエラーが発生しました" + "value" : "総支払額" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "네트워크 이름을 확인하는 중 오류가 발생했습니다" + "value" : "총 지급액" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Er is een fout opgetreden bij het controleren van de netwerknaam" + "value" : "totale uitbetalingen" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Wystąpił błąd podczas sprawdzania nazwy sieci" + "value" : "łączne wypłaty" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "pagamentos totais" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Ocorreu um erro ao verificar o nome da rede" + "value" : "pagamentos totais" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Ocorreu um erro ao verificar o nome da rede" + "value" : "pagamentos totais" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Произошла ошибка при проверке имени сети" + "value" : "всего выплат" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Det uppstod ett fel vid kontroll av nätverksnamnet" + "value" : "totala utbetalningar" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "เกิดข้อผิดพลาดในการตรวจสอบชื่อเครือข่าย" + "value" : "ยอดรายจ่ายทั้งหมด" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Виникла помилка під час перевірки назви мережі" + "value" : "всього виплат" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Đã xảy ra lỗi khi kiểm tra tên mạng" + "value" : "tổng thanh toán" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "检查网络名称时出错" + "value" : "总支出" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "檢查網絡名稱時出錯" + "value" : "總支付額" } } } }, - "There was an error validating your auth code. Please try again or generate a new one" : { + "Total referrals" : { + "comment" : "Call to action", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "حدث خطأ أثناء التحقق من رمز المصادقة الخاص بك. يرجى المحاولة مرة أخرى أو إنشاء رمز جديد." + "value" : "إجمالي الإحالات" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Došlo k chybě při ověřování vašeho ověřovacího kódu. Zkuste to prosím znovu nebo vygenerujte nový kód." + "value" : "Celkový počet doporučení" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Beim Validieren Ihres Auth-Codes ist ein Fehler aufgetreten. Bitte versuchen Sie es erneut oder generieren Sie einen neuen." + "value" : "Gesamtzahl der Empfehlungen" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Υπήρξε σφάλμα κατά την επικύρωση του κωδικού ταυτοποίησης. Παρακαλώ δοκιμάστε ξανά ή δημιουργήστε έναν νέο." + "value" : "Συνολικές παραπομπές" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "There was an error validating your auth code. Please try again or generate a new one" + "value" : "Total referrals" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Hubo un error al validar tu código de autenticación. Por favor, inténtalo de nuevo o genera uno nuevo." + "value" : "Referencias totales" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Hubo un error al validar tu código de autenticación. Por favor, inténtalo de nuevo o genera uno nuevo." + "value" : "Referencias totales" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Referencias totales" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Une erreur s’est produite lors de la validation de votre code d’authentification. Veuillez réessayer ou en générer un nouveau." + "value" : "Nombre total de parrainages" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "אירעה שגיאה באימות קוד האימות שלך. נסה שוב או צור קוד חדש." + "value" : "סך הפניות" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "आपके ऑथ कोड को सत्यापित करने में त्रुटि हुई। कृपया फिर से प्रयास करें या नया कोड जनरेट करें।" + "value" : "कुल रेफरल" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Terjadi kesalahan saat memvalidasi kode autentikasi Anda. Silakan coba lagi atau buat kode baru." + "value" : "Total rujukan" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Si è verificato un errore durante la validazione del codice di autenticazione. Riprova o generane uno nuovo." + "value" : "Totale referral" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "認証コードの検証中にエラーが発生しました。もう一度お試しください、または新しいコードを生成してください。" + "value" : "合計紹介数" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "인증 코드 확인 중 오류가 발생했습니다. 다시 시도하거나 새 코드를 생성하세요." + "value" : "총 추천 수" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Er is een fout opgetreden bij het valideren van uw auth-code. Probeer het opnieuw of genereer een nieuwe." + "value" : "Totaal aantal doorverwijzingen" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Wystąpił błąd podczas weryfikacji kodu autoryzacyjnego. Proszę spróbować ponownie lub wygenerować nowy kod." + "value" : "Liczba wszystkich poleceń" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Total de referências" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Ocorreu um erro ao validar seu código de autenticação. Tente novamente ou gere um novo." + "value" : "Total de indicações" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Ocorreu um erro ao validar o seu código de autenticação. Tente novamente ou gere um novo." + "value" : "Total de referências" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Ошибка при проверке вашего кода авторизации. Пожалуйста, попробуйте снова или сгенерируйте новый." + "value" : "Всего рефералов" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Det uppstod ett fel när din autentiseringskod validerades. Försök igen eller generera en ny." + "value" : "Totalt antal hänvisningar" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "เกิดข้อผิดพลาดในการตรวจสอบรหัสยืนยันของคุณ โปรดลองอีกครั้งหรือสร้างรหัสใหม่" + "value" : "ยอดแนะนำทั้งหมด" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Сталася помилка під час перевірки коду авторизації. Спробуйте ще раз або згенеруйте новий." + "value" : "Загальна кількість рефералів" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Có lỗi khi xác thực mã của bạn. Vui lòng thử lại hoặc tạo mã mới." + "value" : "Tổng số lượt giới thiệu" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "验证您的认证码时出错。请重试或生成新的认证码。" + "value" : "推荐总数" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "驗證你的認證碼時發生錯誤。請再試一次或產生新的認證碼。" + "value" : "推薦總數" } } } }, - "These wallets are not affiliated or controlled by URnetwork. We will send earnings into the connected wallet." : { + "Total referrals: %lld" : { + "comment" : "Call to action", "localizations" : { "ar" : { - "stringUnit" : { - "state" : "translated", - "value" : "هذه المحافظ غير مرتبطة أو متحكم بها من قبل URnetwork. سنقوم بإرسال الأرباح إلى المحفظة المتصلة." + "variations" : { + "plural" : { + "zero" : { + "stringUnit" : { + "state" : "translated", + "value" : "إجمالي الإحالات: %lld" + } + }, + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "إجمالي الإحالات: %lld" + } + }, + "two" : { + "stringUnit" : { + "state" : "translated", + "value" : "إجمالي الإحالات: %lld" + } + }, + "few" : { + "stringUnit" : { + "state" : "translated", + "value" : "إجمالي الإحالات: %lld" + } + }, + "many" : { + "stringUnit" : { + "state" : "translated", + "value" : "إجمالي الإحالات: %lld" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "إجمالي الإحالات: %lld" + } + } + } } }, "cs" : { - "stringUnit" : { - "state" : "translated", - "value" : "Tyto peněženky nejsou přidružené ani kontrolované společností URnetwork. Výdělky budeme posílat do připojené peněženky." + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "Celkem doporučení: %lld" + } + }, + "few" : { + "stringUnit" : { + "state" : "translated", + "value" : "Celkem doporučení: %lld" + } + }, + "many" : { + "stringUnit" : { + "state" : "translated", + "value" : "Celkem doporučení: %lld" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "Celkem doporučení: %lld" + } + } + } } }, "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Diese Wallets sind nicht mit URnetwork verbunden oder werden von URnetwork kontrolliert. Wir senden Einnahmen an die verbundene Wallet." + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gesamtanzahl Empfehlungen: %lld" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gesamtanzahl Empfehlungen: %lld" + } + } + } } }, "el" : { - "stringUnit" : { - "state" : "translated", - "value" : "Αυτά τα πορτοφόλια δεν συνδέονται ούτε ελέγχονται από το URnetwork. Θα στείλουμε τα κέρδη στο συνδεδεμένο πορτοφόλι." + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "Συνολικές παραπομπές: %lld" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "Συνολικές παραπομπές: %lld" + } + } + } + } + }, + "en" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "Total referrals: %lld" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "Total referrals: %lld" + } + } + } } }, "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Estas carteras no están afiliadas ni controladas por URnetwork. Enviaremos las ganancias a la cartera conectada." + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "Total de referidos: %lld" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "Total de referidos: %lld" + } + } + } + } + }, + "es-419" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "Total de referidos: %lld" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "Total de referidos: %lld" + } + } + } } }, "es-MX" : { - "stringUnit" : { - "state" : "translated", - "value" : "Estas billeteras no están afiliadas ni controladas por URnetwork. Enviaremos las ganancias a la billetera conectada." + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "Total de referidos: %lld" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "Total de referidos: %lld" + } + } + } } }, "fr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Ces portefeuilles ne sont pas affiliés ni contrôlés par URnetwork. Nous enverrons les gains dans le portefeuille connecté." + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nombre total de parrainages : %lld" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nombre total de parrainages : %lld" + } + } + } } }, "he" : { - "stringUnit" : { - "state" : "translated", - "value" : "ארנקים אלה אינם מסונפים או נשלטים על ידי URnetwork. נשלח את הרווחים לארנק המחובר." + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "סך ההפניות: %lld" + } + }, + "two" : { + "stringUnit" : { + "state" : "translated", + "value" : "סך ההפניות: %lld" + } + }, + "many" : { + "stringUnit" : { + "state" : "translated", + "value" : "סך ההפניות: %lld" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "סך ההפניות: %lld" + } + } + } } }, "hi" : { - "stringUnit" : { - "state" : "translated", - "value" : "ये वॉलेट URnetwork से संबद्ध या नियंत्रित नहीं हैं। हम कनेक्टेड वॉलेट में कमाई भेजेंगे।" + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "कुल रेफ़रल: %lld" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "कुल रेफ़रल: %lld" + } + } + } + } + }, + "id" : { + "variations" : { + "plural" : { + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "Total referensi: %lld" + } + } + } } }, "it" : { - "stringUnit" : { - "state" : "translated", - "value" : "Questi portafogli non sono affiliati né controllati da URnetwork. Invieremo i guadagni nel portafoglio collegato." + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "Totale inviti: %lld" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "Totale inviti: %lld" + } + } + } } }, "ja" : { - "stringUnit" : { - "state" : "translated", - "value" : "これらのウォレットはURnetworkと提携しておらず、URnetworkによって管理されていません。収益は接続されたウォレットに送金されます。" + "variations" : { + "plural" : { + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "紹介合計: %lld" + } + } + } } }, "ko" : { - "stringUnit" : { - "state" : "translated", - "value" : "이 지갑들은 URnetwork와 제휴되거나 통제되지 않습니다. 수익금은 연결된 지갑으로 보내드립니다." + "variations" : { + "plural" : { + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "총 추천 수: %lld" + } + } + } } }, "nl" : { - "stringUnit" : { - "state" : "translated", - "value" : "Deze wallets zijn niet gelieerd aan of gecontroleerd door URnetwork. We zullen verdiensten naar de verbonden wallet sturen." + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "Totaal aantal verwijzingen: %lld" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "Totaal aantal verwijzingen: %lld" + } + } + } } }, "pl" : { - "stringUnit" : { - "state" : "translated", - "value" : "Te portfele nie są powiązane ani kontrolowane przez URnetwork. Wyślemy zarobki do połączonego portfela." + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "Łączna liczba poleceń: %lld" + } + }, + "few" : { + "stringUnit" : { + "state" : "translated", + "value" : "Łączna liczba poleceń: %lld" + } + }, + "many" : { + "stringUnit" : { + "state" : "translated", + "value" : "Łączna liczba poleceń: %lld" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "Łączna liczba poleceń: %lld" + } + } + } } }, "pt" : { - "stringUnit" : { - "state" : "translated", - "value" : "Estas carteiras não são afiliadas nem controladas pela URnetwork. Enviaremos os ganhos para a carteira conectada." + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "Total de referências: %lld" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "Total de referências: %lld" + } + } + } } }, "pt-BR" : { - "stringUnit" : { - "state" : "translated", - "value" : "Estas carteiras não são afiliadas nem controladas pela URnetwork. Enviaremos os ganhos para a carteira conectada." + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "Total de indicações: %lld" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "Total de indicações: %lld" + } + } + } + } + }, + "pt-PT" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "Total de referências: %lld" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "Total de referências: %lld" + } + } + } } }, "ru" : { - "stringUnit" : { - "state" : "translated", - "value" : "Эти кошельки не связаны и не контролируются URnetwork. Мы отправим заработок в подключенный кошелек." + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "Всего рефералов: %lld" + } + }, + "few" : { + "stringUnit" : { + "state" : "translated", + "value" : "Всего рефералов: %lld" + } + }, + "many" : { + "stringUnit" : { + "state" : "translated", + "value" : "Всего рефералов: %lld" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "Всего рефералов: %lld" + } + } + } } }, "sv" : { - "stringUnit" : { - "state" : "translated", - "value" : "Dessa plånböcker är inte anslutna till eller kontrollerade av URnetwork. Vi kommer att skicka intäkter till den anslutna plånboken." + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "Totalt antal rekommendationer: %lld" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "Totalt antal rekommendationer: %lld" + } + } + } } }, "th" : { - "stringUnit" : { - "state" : "translated", - "value" : "กระเป๋าเงินเหล่านี้ไม่ได้เป็นพันธมิตรหรือควบคุมโดย URnetwork เราจะส่งรายได้ไปยังกระเป๋าเงินที่เชื่อมต่อ" + "variations" : { + "plural" : { + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "จำนวนการแนะนำทั้งหมด: %lld" + } + } + } } }, "uk" : { - "stringUnit" : { - "state" : "translated", - "value" : "Ці гаманці не пов'язані та не контролюються URnetwork. Ми надішлемо заробіток на підключений гаманець." + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "Всього рефералів: %lld" + } + }, + "few" : { + "stringUnit" : { + "state" : "translated", + "value" : "Всього рефералів: %lld" + } + }, + "many" : { + "stringUnit" : { + "state" : "translated", + "value" : "Всього рефералів: %lld" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "Всього рефералів: %lld" + } + } + } } }, "vi" : { - "stringUnit" : { - "state" : "translated", - "value" : "Các ví này không liên kết hoặc được kiểm soát bởi URnetwork. Chúng tôi sẽ gửi thu nhập vào ví đã kết nối." + "variations" : { + "plural" : { + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tổng số giới thiệu: %lld" + } + } + } } }, "zh-Hans" : { - "stringUnit" : { - "state" : "translated", - "value" : "这些钱包与URnetwork无关,也不受URnetwork控制。我们将收益发送到连接的钱包中。" + "variations" : { + "plural" : { + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "总推荐数:%lld" + } + } + } } }, "zh-HK" : { - "stringUnit" : { - "state" : "translated", - "value" : "這些錢包與URnetwork無關,也不受URnetwork控制。我們將收益發送到已連接的錢包中。" + "variations" : { + "plural" : { + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "推薦總數:%lld" + } + } + } } } } }, - "This code has been used up" : { + "Total Referrals: %lld" : { + "comment" : "Usage-bar referral row: the number of referrals the network has made. Was Shared/Views/UsageBar.swift on apple; now the windows usage bar.", + "extractionState" : "stale", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "تم استخدام هذا الرمز بالكامل" + "value" : "إجمالي الإحالات: %lld" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Tento kód byl již vyčerpán" + "value" : "Celkem doporučení: %lld" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Dieser Code wurde bereits verwendet" + "value" : "Gesamtanzahl Empfehlungen: %lld" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Αυτός ο κωδικός έχει εξαντληθεί" + "value" : "Σύνολο παραπομπών: %lld" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "This code has been used up" + "value" : "Total Referrals: %lld" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Este código ya ha sido utilizado" + "value" : "Total de referidos: %lld" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Este código ya ha sido utilizado" + "value" : "Total de referidos: %lld" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Total de referidos: %lld" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Ce code a déjà été utilisé" + "value" : "Total des parrainages : %lld" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "הקוד הזה נוצל במלואו" + "value" : "סך ההפניות: %lld" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "यह कोड उपयोग हो चुका है" + "value" : "कुल रेफ़रल: %lld" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Kode ini sudah digunakan" + "value" : "Total Referensi: %lld" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Questo codice è già stato utilizzato" + "value" : "Totale inviti: %lld" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "このコードはすでに使用されています" + "value" : "総紹介数: %lld" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "이 코드는 이미 사용되었습니다" + "value" : "총 추천: %lld" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Deze code is al gebruikt" + "value" : "Totaal aantal verwijzingen: %lld" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Ten kod został już wykorzystany" + "value" : "Łączna liczba poleceń: %lld" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Total de referências: %lld" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Este código já foi utilizado" + "value" : "Total de indicações: %lld" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Este código já foi utilizado" + "value" : "Total de referências: %lld" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Этот код уже использован" + "value" : "Всего рефералов: %lld" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Denna kod har redan använts" + "value" : "Totalt antal rekommendationer: %lld" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "โค้ดนี้ถูกใช้หมดแล้ว" + "value" : "จำนวนการแนะนำทั้งหมด: %lld" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Цей код вже використано" + "value" : "Всього рефералів: %lld" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Mã này đã được sử dụng hết" + "value" : "Tổng số giới thiệu: %lld" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "此代码已被用完" + "value" : "推荐总数:%lld" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "此代碼已被用完" + "value" : "推薦總數:%lld" } } } }, - "This code is not valid" : { + "Transaction" : { + "comment" : "Call to action", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "هذا الرمز غير صالح" + "value" : "المعاملة" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Tento kód není platný" + "value" : "Transakce" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Dieser Code ist ungültig" + "value" : "Transaktion" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Αυτός ο κωδικός δεν είναι έγκυρος" + "value" : "Συναλλαγή" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "This code is not valid" + "value" : "Transaction" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Este código no es válido" + "value" : "Transacción" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Este código no es válido" + "value" : "Transacción" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Transacción" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Ce code n'est pas valide" + "value" : "Transaction" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "קוד זה אינו תקף" + "value" : "עסקה" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "यह कोड मान्य नहीं है" + "value" : "लेन-देन" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Kode ini tidak valid" + "value" : "Transaksi" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Questo codice non è valido" + "value" : "Transazione" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "このコードは無効です" + "value" : "トランザクション" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "이 코드는 유효하지 않습니다" + "value" : "트랜잭션" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "De code is niet geldig" + "value" : "Transactie" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Ten kod jest nieważny" + "value" : "Transakcja" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Transação" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Este código não é válido" + "value" : "Transação" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Este código não é válido" + "value" : "Transação" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Этот код недействителен" + "value" : "Транзакция" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Den här koden är inte giltig" + "value" : "Transaktion" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "รหัสนี้ไม่ถูกต้อง" + "value" : "รายการ" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Цей код недійсний" + "value" : "Транзакція" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Mã này không hợp lệ" + "value" : "Giao dịch" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "此代码无效" + "value" : "交易" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "此代碼無效" + "value" : "交易" } } } }, - "This network name is already taken" : { + "Trusted by over 100,000 private networks" : { + "comment" : "Used in Shared/Views/Introduction/IntroductionView.swift.", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "اسم الشبكة مستخدم بالفعل" + "value" : "موثوق به من قبل أكثر من 100,000 شبكة خاصة" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Tento název sítě je již obsazen" + "value" : "Důvěřuje mu více než 100 000 soukromých sítí" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Dieser Netzwerkname ist bereits vergeben" + "value" : "Vertraut von über 100.000 privaten Netzwerken" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Αυτό το όνομα δικτύου χρησιμοποιείται ήδη" + "value" : "Εμπιστεύεται από πάνω από 100.000 ιδιωτικά δίκτυα" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "This network name is already taken" + "value" : "Trusted by over 100,000 private networks" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Este nombre de red ya está en uso" + "value" : "Confiado por más de 100,000 redes privadas" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Este nombre de red ya está en uso" + "value" : "Confiado por más de 100,000 redes privadas" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Ce nom de réseau est déjà pris" + "value" : "Fait confiance à plus de 100 000 réseaux privés" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "שם הרשת הזה כבר תפוס" + "value" : "נמצא באמון של מעל 100,000 רשתות פרטיות" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "यह नेटवर्क नाम पहले से ही लिया जा चुका है" + "value" : "100,000 से अधिक निजी नेटवर्क द्वारा विश्वसनीय" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Nama jaringan ini sudah digunakan" + "value" : "Dipercaya oleh lebih dari 100.000 jaringan pribadi" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Questo nome di rete è già in uso" + "value" : "Fidato da oltre 100.000 reti private" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "このネットワーク名は既に使用されています" + "value" : "10万以上のプライベートネットワークに信頼されています" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "이 네트워크 이름은 이미 사용 중입니다" + "value" : "10만 개 이상의 프라이빗 네트워크에서 신뢰받음" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Deze netwerknaam is al in gebruik" + "value" : "Vertrouwd door meer dan 100.000 privé-netwerken" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Ta nazwa sieci jest już zajęta" + "value" : "Zaufany przez ponad 100 000 prywatnych sieci" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Este nome de rede já está em uso" + "value" : "Confiado por mais de 100.000 redes privadas" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Este nome de rede já está em uso" + "value" : "Confiado por mais de 100.000 redes privadas" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Это имя сети уже занято" + "value" : "Доверяют более 100 000 частных сетей" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Detta nätverksnamn är redan taget" + "value" : "Betrodd av över 100 000 privata nätverk" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "ชื่อเครือข่ายนี้ถูกใช้ไปแล้ว" + "value" : "ได้รับความไว้วางใจจากเครือข่ายส่วนตัวกว่า 100,000 เครือข่าย" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Ця назва мережі вже зайнята" + "value" : "Довіряють понад 100 000 приватних мереж" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Tên mạng này đã được sử dụng" + "value" : "Được hơn 100.000 mạng riêng tin tưởng" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "此网络名称已被使用" + "value" : "获得超过100,000个私人网络的信任" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "此網絡名稱已被使用" + "value" : "獲超過100,000個私人網絡信任" } } } }, - "To start earning, connect your Solana wallet to URnetwork." : { + "Try guest mode" : { + "comment" : "Used in Authenticate/LoginInitial/GuestModeSheet.swift.", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "للبدء في الكسب، قم بربط محفظة Solana الخاصة بك مع URnetwork." + "value" : "جرب وضع الضيف" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Chcete-li začít vydělávat, připojte svou peněženku Solana k síti URnetwork." + "value" : "Vyzkoušejte režim hosta" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Um Geld zu verdienen, verbinden Sie Ihre Solana Wallet mit URnetwork." + "value" : "Gastmodus testen" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Για να αρχίσετε να κερδίζετε, συνδέστε το πορτοφόλι Solana σας στο URnetwork." + "value" : "Δοκιμάστε τη λειτουργία επισκέπτη" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "To start earning, connect your Solana wallet to URnetwork." + "value" : "Try guest mode" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Para empezar a ganar, conecta tu cartera Solana a URnetwork." + "value" : "Prueba el modo invitado" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Para comenzar a ganar, conecta tu billetera Solana a URnetwork." + "value" : "Prueba el modo invitado" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Pour commencer à gagner, connectez votre portefeuille Solana à URnetwork." + "value" : "Essayer le mode invité" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "כדי להתחיל להרוויח, חבר את ארנק Solana שלך ל-URnetwork." + "value" : "נסה מצב אורח" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "कमाई शुरू करने के लिए, अपना Solana वॉलेट URnetwork से कनेक्ट करें।" + "value" : "गेस्ट मोड आज़माएं" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Untuk mulai menghasilkan, hubungkan dompet Solana Anda ke URnetwork." + "value" : "Coba mode tamu" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Per iniziare a guadagnare, collega il tuo portafoglio Solana a URnetwork." + "value" : "Prova la modalità ospite" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "収益を得るには、SolanaウォレットをURnetworkに接続してください。" + "value" : "ゲストモードを試す" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "수익을 얻으려면 Solana 월렛을 URnetwork에 연결하세요." + "value" : "게스트 모드 시도" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Om te beginnen met verdienen, verbind je Solana wallet met URnetwork" + "value" : "Probeer de gastmodus" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Aby zacząć zarabiać, połącz swój portfel Solana z URnetwork." + "value" : "Wypróbuj tryb gościa" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Para começar a ganhar, conecte sua carteira Solana ao URnetwork." + "value" : "Experimentar modo convidado" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Para começar a ganhar, ligue a sua carteira Solana ao URnetwork." + "value" : "Experimente o modo convidado" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Чтобы начать зарабатывать, подключите ваш кошелек Solana к URnetwork." + "value" : "Попробовать гостевой режим" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "För att börja tjäna, anslut din Solana-plånbok till URnetwork." + "value" : "Prova gästläge" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "เพื่อเริ่มรับรายได้ เชื่อมต่อกระเป๋าเงิน Solana ของคุณกับ URnetwork" + "value" : "ลองใช้โหมดผู้เยี่ยมชม" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Щоб почати заробляти, підключіть свій гаманець Solana до URnetwork." + "value" : "Спробувати гостьовий режим" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Để bắt đầu kiếm tiền, kết nối ví Solana của bạn với URnetwork." + "value" : "Thử chế độ khách" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "要开始赚取收益,请将您的 Solana 钱包连接到 URnetwork。" + "value" : "试用访客模式" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "要開始賺取收益,請將您的 Solana 錢包連接到 URnetwork。" + "value" : "試用訪客模式" } } } }, - "total payouts" : { + "Try Guest Mode" : { + "comment" : "Login initial", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "إجمالي المدفوعات" + "value" : "جرب وضع الضيف" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "celkové výplaty" + "value" : "Zkuste režim hosta" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Gesamtauszahlungen" + "value" : "Gastmodus testen" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "συνολικές πληρωμές" + "value" : "Δοκιμάστε τη λειτουργία επισκέπτη" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "total payouts" + "value" : "Try Guest Mode" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "pagos totales" + "value" : "Probar el modo invitado" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "pagos totales" + "value" : "Probar Modo Invitado" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Probar el modo invitado" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "paiements totaux" + "value" : "Essayer le mode invité" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "סך התשלומים" + "value" : "נסה מצב אורח" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "कुल भुगतान" + "value" : "गेस्ट मोड आज़माएँ" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "total pembayaran" + "value" : "Coba Mode Tamu" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "pagamenti totali" + "value" : "Prova Modalità Ospite" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "合計支払額" + "value" : "ゲストモードを試す" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "총 지급액" + "value" : "게스트 모드 시도하기" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Totale uitbetalingen" + "value" : "Probeer de gastmodus" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "łączne wypłaty" + "value" : "Wypróbuj tryb gościa" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Experimentar o Modo Convidado" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "pagamentos totais" + "value" : "Experimentar o Modo Convidado" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "pagamentos totais" + "value" : "Experimente o Modo Convidado" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "всего выплат" + "value" : "Попробуйте гостевой режим" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "totala utbetalningar" + "value" : "Prova Gästläge" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "การจ่ายเงินทั้งหมด" + "value" : "ลองใช้ในโหมดผู้เยี่ยมชม" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "всього виплат" + "value" : "Спробувати Гостьовий Режим" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "tổng thanh toán" + "value" : "Thử Chế Độ Khách" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "总支付额" + "value" : "尝试访客模式" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "總支付額" + "value" : "試用訪客模式" } } } }, - "Total referrals" : { + "Unencrypted DNS" : { + "comment" : "A label displayed in the custom dns settings section.", + "isCommentAutoGenerated" : true, "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "إجمالي الإحالات" + "value" : "DNS غير مشفر" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Celkový počet doporučení" + "value" : "Nešifrovaný DNS" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Gesamtzahl der Empfehlungen" + "value" : "Unverschlüsseltes DNS" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Σύνολο παραπομπών" + "value" : "Μη κρυπτογραφημένο DNS" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Total referrals" + "value" : "Unencrypted DNS" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Referencias totales" + "value" : "DNS sin cifrar" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Referencias totales" + "value" : "DNS sin cifrar" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "DNS sin cifrar" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Nombre total de parrainages" + "value" : "DNS non chiffré" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "סך הפניות" + "value" : "DNS לא מוצפן" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "कुल रेफ़रल" + "value" : "अनएन्क्रिप्टेड DNS" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Total rujukan" + "value" : "DNS tanpa enkripsi" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Totale referral" + "value" : "DNS non crittografato" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "合計紹介数" + "value" : "暗号化なしのDNS" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "총 추천 수" + "value" : "암호화되지 않은 DNS" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Totaal aantal verwijzingen" + "value" : "Onversleutelde DNS" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Liczba wszystkich poleceń" + "value" : "Niezaszyfrowany DNS" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "DNS não encriptado" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Total de indicações" + "value" : "DNS não criptografado" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Total de referências" + "value" : "DNS não encriptado" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Всего рефералов" + "value" : "Незашифрованный DNS" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Totalt antal hänvisningar" + "value" : "Okrypterad DNS" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "การอ้างอิงทั้งหมด" + "value" : "DNS ที่ไม่เข้ารหัส" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Загальна кількість рефералів" + "value" : "Незашифрований DNS" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Tổng số lượt giới thiệu" + "value" : "DNS không mã hóa" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "推荐总数" + "value" : "未加密 DNS" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "推薦總數" + "value" : "未加密 DNS" } } } }, - "Total Referrals: %lld" : { + "Unlink" : { + "comment" : "Call to action", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "إجمالي الإحالات: %lld" + "value" : "فك الارتباط" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Celkem doporučení: %lld" + "value" : "Odpojení" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Gesamtanzahl Empfehlungen: %lld" + "value" : "Trennen" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Σύνολο παραπομπών: %lld" + "value" : "Αποσύνδεση" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Total Referrals: %lld" + "value" : "Unlink" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Total de referidos: %lld" + "value" : "Desvincular" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Total de referidos: %lld" + "value" : "Desvincular" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Desvincular" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Total des parrainages : %lld" + "value" : "Dissocier" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "סך ההפניות: %lld" + "value" : "נתק" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "कुल रेफ़रल: %lld" + "value" : "अनलिंक करें" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Total Referensi: %lld" + "value" : "Putuskan tautan" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Totale inviti: %lld" + "value" : "Scollega" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "総紹介数: %lld" + "value" : "リンク解除" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "총 추천: %lld" + "value" : "연결 해제" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Totaal aantal verwijzingen: %lld" + "value" : "Ontkoppelen" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Łączna liczba poleceń: %lld" + "value" : "Odłącz" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Desvincular" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Total de indicações: %lld" + "value" : "Desvincular" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Total de referências: %lld" + "value" : "Desvincular" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Всего рефералов: %lld" + "value" : "Отвязать" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Totalt antal rekommendationer: %lld" + "value" : "Koppla från" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "จำนวนการแนะนำทั้งหมด: %lld" + "value" : "ยกเลิกการเชื่อม" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Всього рефералів: %lld" + "value" : "Від'єднати" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Tổng số giới thiệu: %lld" + "value" : "Hủy liên kết" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "推荐总数:%lld" + "value" : "解除关联" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "推薦總數:%lld" + "value" : "取消連結" } } } }, - "Transaction" : { + "Unlink referral network" : { + "comment" : "Call to action", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "المعاملة" + "value" : "فك ارتباط شبكة الإحالة" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Transakce" + "value" : "Odpojení od referenční sítě" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Transaktion" + "value" : "Empfehlungsnetzwerk trennen" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Συναλλαγή" + "value" : "Αποσύνδεση δικτύου παραπομπών" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Transaction" + "value" : "Unlink referral network" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Transacción" + "value" : "Desvincular red de referidos" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Transacción" + "value" : "Desvincular red de referidos" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Desvincular red de referidos" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Transaction" + "value" : "Dissocier le réseau de parrainage" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "עסקה" + "value" : "נתק רשת הפניות" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "लेन-देन" + "value" : "रेफ़रल नेटवर्क को अनलिंक करें" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Transaksi" + "value" : "Putuskan tautan jaringan referensi" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Transazione" + "value" : "Scollega rete di riferimento" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "トランザクション" + "value" : "紹介ネットワークのリンクを解除する" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "트랜잭션" + "value" : "추천 네트워크 연결 해제" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Transactie" + "value" : "Referentienetwerk ontkoppelen" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Transakcja" + "value" : "Odłącz sieć poleceń" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Desvincular rede de referências" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Transação" + "value" : "Desvincular rede de indicações" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Transação" + "value" : "Desvincular rede de recomendações" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Транзакция" + "value" : "Отвязать реферальную сеть" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Transaktion" + "value" : "Koppla från referensnätverk" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "ธุรกรรม" + "value" : "ยกเลิกเชื่อมรหัสการแนะนำเครือข่าย" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Транзакція" + "value" : "Від'єднати реферальну мережу" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Giao dịch" + "value" : "Hủy liên kết mạng giới thiệu" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "交易" + "value" : "解除推荐网络关联" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "交易" + "value" : "取消連結推薦網絡" } } } }, - "Trusted by over 100,000 private networks" : { + "Unpaid data provided" : { + "comment" : "Wallet Screen", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "موثوق به من قبل أكثر من 100,000 شبكة خاصة" + "value" : "تم توفير بيانات غير مدفوعة" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Důvěřuje mu více než 100 000 soukromých sítí" + "value" : "Poskytnuté neplacené data" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Vertraut von über 100.000 privaten Netzwerken" + "value" : "Unbezahlte Daten bereitgestellt" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Εμπιστεύεται από πάνω από 100.000 ιδιωτικά δίκτυα" + "value" : "Παρέχονται μη πληρωμένα δεδομένα" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Trusted by over 100,000 private networks" + "value" : "Unpaid data provided" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Confiado por más de 100,000 redes privadas" + "value" : "Datos no pagados proporcionados" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Confiado por más de 100,000 redes privadas" + "value" : "Datos proporcionados sin pagar" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Datos no pagados proporcionados" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Fait confiance à plus de 100 000 réseaux privés" + "value" : "Données non payées fournies" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "נמצא באמון של מעל 100,000 רשתות פרטיות" + "value" : "נתונים שסופקו וטרם שולמו" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "100,000 से अधिक निजी नेटवर्क द्वारा विश्वसनीय" + "value" : "बिना भुगतान का डेटा प्रदान किया गया" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Dipercaya oleh lebih dari 100.000 jaringan pribadi" + "value" : "Data yang disediakan belum dibayar" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Fidato da oltre 100.000 reti private" + "value" : "Dati forniti non pagati" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "10万以上のプライベートネットワークに信頼されています" + "value" : "未払いのデータが提供されました" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "10만 개 이상의 프라이빗 네트워크에서 신뢰받음" + "value" : "미지급 데이터가 제공됨" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Vertrouwd door meer dan 100.000 privé-netwerken" + "value" : "Onbetaalde data geleverd" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Zaufany przez ponad 100 000 prywatnych sieci" + "value" : "Dostarczono nieopłacone dane" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dados não pagos fornecidos" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Confiado por mais de 100.000 redes privadas" + "value" : "Dados não pagos fornecidos" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Confiado por mais de 100.000 redes privadas" + "value" : "Dados fornecidos não pagos" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Доверяют более 100 000 частных сетей" + "value" : "Предоставленные неоплаченные данные" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Betrodd av över 100 000 privata nätverk" + "value" : "Obetalda data tillhandahållen" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "ได้รับความไว้วางใจจากเครือข่ายส่วนตัวกว่า 100,000 เครือข่าย" + "value" : "ยอด data ที่ยังไม่ได้จ่าย" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Довіряють понад 100 000 приватних мереж" + "value" : "неоплачені надані дані" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Được hơn 100.000 mạng riêng tin tưởng" + "value" : "Dữ liệu đã cung cấp chưa thanh toán" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "获得超过100,000个私人网络的信任" + "value" : "提供了未付费数据" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "獲超過100,000個私人網絡信任" + "value" : "未支付的已提供數據" } } } }, - "Try guest mode" : { + "Update" : { + "comment" : "Call to action", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "جرب وضع الضيف" + "value" : "تحديث" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Vyzkoušejte režim hosta" + "value" : "Aktualizace" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Gastmodus testen" + "value" : "Aktualisieren" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Δοκιμάστε τη λειτουργία επισκέπτη" + "value" : "Ενημέρωση" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Try guest mode" + "value" : "Update" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Prueba el modo invitado" + "value" : "Actualizar" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Prueba el modo invitado" + "value" : "Actualizar" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Actualizar" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Essayer le mode invité" + "value" : "Mettre à jour" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "נסה מצב אורח" + "value" : "עדכן" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "गेस्ट मोड आज़माएं" + "value" : "अपडेट करें" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Coba mode tamu" + "value" : "Perbarui" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Prova la modalità ospite" + "value" : "Aggiorna" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "ゲストモードを試す" + "value" : "更新" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "게스트 모드 시도" + "value" : "업데이트" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Probeer de gastmodus" + "value" : "Bijwerken" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Wypróbuj tryb gościa" + "value" : "Aktualizuj" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Atualizar" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Experimentar modo convidado" + "value" : "Atualizar" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Experimente o modo convidado" + "value" : "Atualizar" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Попробовать гостевой режим" + "value" : "Обновить" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Prova gästläge" + "value" : "Uppdatera" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "ลองใช้โหมดผู้เยี่ยมชม" + "value" : "อัพเดต" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Спробувати гостьовий режим" + "value" : "Оновити" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Thử chế độ khách" + "value" : "Cập nhật" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "试用访客模式" + "value" : "更新" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "試用訪客模式" + "value" : "更新" } } } }, - "Try Guest Mode" : { + "Update password" : { + "comment" : "Used in Main/Account/Profile/ProfileView.swift.", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "جرب وضع الضيف" + "value" : "تحديث كلمة المرور" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Vyzkoušejte režim pro hosty" + "value" : "Aktualizace hesla" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Gastmodus Testen" + "value" : "Passwort aktualisieren" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Δοκιμή Λειτουργίας Επισκέπτη" + "value" : "Ενημέρωση κωδικού" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Try Guest Mode" + "value" : "Update password" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Probar Modo Invitado" + "value" : "Actualizar contraseña" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Probar Modo Invitado" + "value" : "Actualizar contraseña" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Essayer le Mode Invité" + "value" : "Mettre à jour le mot de passe" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "נסה מצב אורח" + "value" : "עדכן סיסמה" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "गेस्ट मोड आज़माएं" + "value" : "पासवर्ड अपडेट करें" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Coba Mode Tamu" + "value" : "Perbarui kata sandi" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Prova Modalità Ospite" + "value" : "Aggiorna password" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "ゲストモードを試す" + "value" : "パスワードを更新" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "게스트 모드 시도" + "value" : "비밀번호 업데이트" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Probeer De Gastmodus" + "value" : "Update Wachtwoord" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Wypróbuj Tryb Gościa" + "value" : "Aktualizuj hasło" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Experimentar Modo Convidado" + "value" : "Atualizar senha" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Experimente o Modo Convidado" + "value" : "Atualizar palavra-passe" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Попробовать Гостевой Режим" + "value" : "Обновить пароль" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Prova Gästläge" + "value" : "Uppdatera lösenord" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "ลองใช้โหมดผู้เยี่ยมชม" + "value" : "อัปเดตรหัสผ่าน" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Спробувати Гостьовий Режим" + "value" : "Оновити пароль" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Thử Chế Độ Khách" + "value" : "Cập nhật mật khẩu" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "试用访客模式" + "value" : "更新密码" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "試用訪客模式" + "value" : "更新密碼" } } } }, - "Unencrypted DNS" : { - "comment" : "A label displayed in the custom dns settings section.", - "isCommentAutoGenerated" : true - }, - "Unlink" : { + "Update referral network" : { + "comment" : "Call to action", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "إلغاء الربط" + "value" : "تحديث شبكة الإحالة" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Odpojení" + "value" : "Aktualizace referenční sítě" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Verknüpfung aufheben" + "value" : "Empfehlungsnetzwerk aktualisieren" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Αποσύνδεση" + "value" : "Ενημέρωση δικτύου παραπομπών" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Unlink" + "value" : "Update referral network" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Desvincular" + "value" : "Actualizar red de referidos" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Desvincular" + "value" : "Actualizar red de referidos" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Actualizar red de referidos" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Dissocier" + "value" : "Mettre à jour le réseau de parrainage" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "נתק" + "value" : "עדכן רשת הפניות" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "अनलिंक करें" + "value" : "रेफ़रल नेटवर्क अपडेट करें" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Putuskan tautan" + "value" : "Perbarui jaringan referensi" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Scollega" + "value" : "Aggiorna rete di riferimento" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "リンク解除" + "value" : "紹介ネットワークを更新する" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "연결 해제" + "value" : "추천 네트워크 업데이트" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Ontkoppelen" + "value" : "Referentienetwerk bijwerken" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Odłącz" + "value" : "Zaktualizuj sieć poleceń" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Atualizar rede de referências" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Desvincular" + "value" : "Atualizar rede de indicações" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Desvincular" + "value" : "Atualizar rede de recomendações" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Отключить" + "value" : "Обновить реферальную сеть" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Koppla från" + "value" : "Uppdatera referensnätverk" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "ยกเลิกการเชื่อมโยง" + "value" : "อัพเดตรหัสการแนะนำเครือข่าย" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Від'єднати" + "value" : "Оновити реферальну мережу" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Hủy liên kết" + "value" : "Cập nhật mạng giới thiệu" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "取消关联" + "value" : "更新推荐网络" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "取消連結" + "value" : "更新推薦網絡" } } } }, - "Unlink referral network" : { + "Upgrade" : { + "comment" : "Used in Main/Account/AccountRootView.swift.", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "إلغاء ربط شبكة الإحالة" + "value" : "ترقية" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Odpojit referální síť" + "value" : "Upgrade" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Empfehlungsnetzwerk trennen" + "value" : "Upgrade" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Αποσύνδεση δικτύου παραπομπών" + "value" : "Αναβάθμιση" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Unlink referral network" + "value" : "Upgrade" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Desvincular red de referidos" + "value" : "Actualizar" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Desvincular red de referidos" + "value" : "Actualizar" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Dissocier le réseau de parrainage" + "value" : "Mettre à niveau" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "נתק רשת הפניות" + "value" : "שדרג" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "रेफ़रल नेटवर्क अनलिंक करें" + "value" : "अपग्रेड करें" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Putuskan tautan jaringan referensi" + "value" : "Tingkatkan" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Scollega rete di riferimento" + "value" : "Aggiorna" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "リファラルネットワークのリンク解除" + "value" : "アップグレード" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "추천 네트워크 연결 해제" + "value" : "업그레이드" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Verwijzingsnetwerk Ontkoppelen" + "value" : "Upgraden" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Odłącz sieć poleceń" + "value" : "Ulepsz" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Desvincular rede de indicações" + "value" : "Fazer upgrade" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Desvincular rede de recomendações" + "value" : "Fazer upgrade" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Отключить реферальную сеть" + "value" : "Улучшить" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Koppla från referensnätverk" + "value" : "Uppgradera" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "ยกเลิกการเชื่อมโยงเครือข่ายผู้อ้างอิง" + "value" : "อัปเกรด" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Від'єднати реферальну мережу" + "value" : "Оновити" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Hủy liên kết mạng giới thiệu" + "value" : "Nâng cấp" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "取消关联推荐网络" + "value" : "升级" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "取消連結推薦網絡" + "value" : "升級" } } } }, - "Unpaid data provided" : { + "URnetwork is disconnected" : { + "comment" : "Used in Shared/Intents/DisconnectIntent.swift.", + "localizations" : { + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "URnetwork je odpojen" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "URnetwork is disconnected" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "URnetwork is niet verbonden" + } + } + } + }, + "URnetwork is powered by the world's largest, safest,\nand most decentralized ingress and egress protocol." : { + "comment" : "Call to action", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "بيانات غير مدفوعة مقدمة" + "value" : "تعمل URnetwork ببروتوكول الدخول والخروج الأكبر\nوالأكثر أمانًا ولامركزية في العالم." } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Poskytnuté neproplacené data" + "value" : "URnetwork stojí na největším, nejbezpečnějším a nejvíce\ndecentralizovaném protokolu na světě pro příchozí a odchozí provoz." } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Unbezahlte bereitgestellte Daten" + "value" : "URnetwork basiert auf dem weltweit größten, sichersten\nund am stärksten dezentralisierten Ingress- und Egress-Protokoll." } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "απλήρωτα δεδομένα που παρασχέθηκαν" + "value" : "Το URnetwork τροφοδοτείται από το μεγαλύτερο, ασφαλέστερο\nκαι πιο αποκεντρωμένο πρωτόκολλο εισόδου και εξόδου στον κόσμο." } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Unpaid data provided" + "value" : "URnetwork is powered by the world's largest, safest,\nand most decentralized ingress and egress protocol." } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Datos proporcionados sin pagar" + "value" : "URnetwork funciona con el protocolo de entrada y salida\nmás grande, seguro y descentralizado del mundo." } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Datos proporcionados sin pagar" + "value" : "URnetwork funciona con el protocolo de entrada y salida\nmás grande, seguro y descentralizado del mundo." + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "URnetwork funciona con el protocolo de entrada y salida\nmás grande, seguro y descentralizado del mundo." } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Données fournies non payées" + "value" : "URnetwork repose sur le protocole d'entrée et de sortie\nle plus vaste, le plus sûr et le plus décentralisé au monde." } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "נתונים שסופקו וטרם שולמו" + "value" : "URnetwork מבוססת על פרוטוקול הכניסה והיציאה\nהגדול, הבטוח והמבוזר ביותר בעולם." } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "प्रदान किए गए अवैतनिक डेटा" + "value" : "URnetwork दुनिया के सबसे बड़े, सबसे सुरक्षित\nऔर सबसे विकेंद्रीकृत प्रवेश-निकास प्रोटोकॉल पर चलता है।" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Data yang disediakan belum dibayar" + "value" : "URnetwork didukung oleh protokol ingress dan egress\nterbesar, teraman, dan paling terdesentralisasi di dunia." } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Dati forniti non pagati" + "value" : "URnetwork si basa sul protocollo di ingresso e uscita\npiù grande, sicuro e decentralizzato al mondo." } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "未払いの提供データ" + "value" : "URnetworkは、世界最大で最も安全、最も分散化された\nイングレス・エグレスプロトコルを基盤としています。" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "제공된 미지급 데이터" + "value" : "URnetwork는 세계에서 가장 크고 안전하며 가장 분산화된\n인그레스·이그레스 프로토콜로 구동됩니다." } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Onbetaalde data Aangeleverd" + "value" : "URnetwork draait op het grootste, veiligste en meest\ngedecentraliseerde ingress- en egress-protocol ter wereld." } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Dostarczone nieopłacone dane" + "value" : "URnetwork opiera się na największym, najbezpieczniejszym i najbardziej\nzdecentralizowanym na świecie protokole wejścia i wyjścia." + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "A URnetwork é alimentada pelo maior, mais seguro\ne mais descentralizado protocolo de entrada e saída do mundo." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Dados fornecidos não pagos" + "value" : "A URnetwork é alimentada pelo maior, mais seguro\ne mais descentralizado protocolo de entrada e saída do mundo." } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Dados fornecidos não pagos" + "value" : "A URnetwork é alimentada pelo maior, mais seguro\ne mais descentralizado protocolo de entrada e saída do mundo." } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "неоплаченные предоставленные данные" + "value" : "В основе URnetwork — самый крупный, безопасный\nи децентрализованный в мире протокол входа и выхода трафика." } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Obetalda data tillhandahållen" + "value" : "URnetwork drivs av världens största, säkraste\noch mest decentraliserade in- och utgångsprotokoll." } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "ข้อมูลที่ให้บริการแต่ยังไม่ได้ชำระ" + "value" : "URnetwork ขับเคลื่อนด้วยโปรโตคอลทางเข้าออกที่ใหญ่ที่สุด\nปลอดภัยที่สุด และกระจายศูนย์มากที่สุดในโลก" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "неоплачені надані дані" + "value" : "В основі URnetwork — найбільший, найбезпечніший\nі найдецентралізованіший у світі протокол входу й виходу трафіку." } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Dữ liệu đã cung cấp chưa thanh toán" + "value" : "URnetwork được vận hành bởi giao thức ra vào lớn nhất,\nan toàn nhất và phi tập trung nhất thế giới." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "未支付的已提供数据" + "value" : "URnetwork 由全球最大、最安全、\n最去中心化的出入口协议驱动。" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "未支付的已提供數據" + "value" : "URnetwork 由全球最大、最安全、\n最去中心化的出入口協議驅動。" } } } }, - "Update" : { + "URnetwork is the most local and most private network on the planet." : { + "comment" : "Call to action", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "تحديث" + "value" : "URnetwork هي الشبكة الأكثر محلية والأكثر خصوصية على الكوكب." } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Aktualizace" + "value" : "URnetwork je nejlokálnější a nejvíce soukromá síť na světě." } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Aktualisieren" + "value" : "URnetwork ist das lokalste und privateste Netzwerk der Welt." } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Ενημέρωση" + "value" : "Το URnetwork είναι το πιο τοπικό και πιο ιδιωτικό δίκτυο στον πλανήτη." } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Update" + "value" : "URnetwork is the most local and most private network on the planet." } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Actualizar" + "value" : "URnetwork es la red más local y más privada del planeta." } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Actualizar" + "value" : "URnetwork es la red más local y privada del planeta." + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "URnetwork es la red más local y más privada del planeta." } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Mettre à jour" + "value" : "URnetwork est le réseau le plus local et le plus privé au monde." } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "עדכן" + "value" : "URnetwork היא הרשת המקומית והפרטית ביותר בעולם." } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "अपडेट करें" + "value" : "URnetwork ग्रह का सबसे स्थानीय और सबसे निजी नेटवर्क है।" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Perbarui" + "value" : "URnetwork adalah jaringan paling lokal dan paling privat di dunia." } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Aggiorna" + "value" : "URnetwork è la rete più locale e più privata al mondo." } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "更新" + "value" : "URnetworkは世界で最もローカルで最もプライベートなネットワークです。" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "업데이트" + "value" : "URnetwork는 지구상에서 가장 지역적이고 가장 프라이빗한 네트워크입니다." } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Update" + "value" : "URnetwork is het meest lokale en meest privé netwerk ter wereld." } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Aktualizuj" + "value" : "URnetwork to najbardziej lokalna i najbardziej prywatna sieć na świecie." + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "URnetwork é a rede mais local e mais privada do planeta." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Atualizar" + "value" : "URnetwork é a rede mais local e mais privada do planeta." } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Atualizar" + "value" : "URnetwork é a rede mais local e mais privada do planeta." } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Обновить" + "value" : "URnetwork — самая локальная и самая приватная сеть на планете." } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Uppdatera" + "value" : "URnetwork är det mest lokala och mest privata nätverket på planeten." } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "อัปเดต" + "value" : "URnetwork เป็นเครือข่ายที่มีความเป็นท้องถิ่นและเป็นส่วนตัวมากที่สุดในโลก" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Оновити" + "value" : "URnetwork — це найлокальніша та найприватніша мережа на планеті." } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Cập nhật" + "value" : "URnetwork là mạng lưới địa phương và riêng tư nhất trên hành tinh." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "更新" + "value" : "URnetwork 是全球最本地化、最私密的网络。拥有比其他主流 VPN 多 20 倍的可用城市,以及每个 IP 地址少 100 倍的用户。尽情享受世界的乐趣和无与伦比的隐私。" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "更新" + "value" : "URnetwork 是全球最本地化及最私密的網絡。" } } } }, - "Update password" : { + "URnetwork Referral Code" : { + "comment" : "Used in Shared/Views/Introduction/ParticipateReferView.swift.", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "تحديث كلمة المرور" + "value" : "رمز إحالة URnetwork" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Aktualizace hesla" + "value" : "URnetwork referální kód" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Passwort aktualisieren" + "value" : "URnetwork Empfehlungscode" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Ενημέρωση κωδικού" + "value" : "Κωδικός παραπομπής URnetwork" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Update password" + "value" : "URnetwork Referral Code" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Actualizar contraseña" + "value" : "Código de referencia URnetwork" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Actualizar contraseña" + "value" : "Código de referencia URnetwork" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Mettre à jour le mot de passe" + "value" : "Code de parrainage URnetwork" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "עדכן סיסמה" + "value" : "קוד הפניה URnetwork" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "पासवर्ड अपडेट करें" + "value" : "URnetwork रेफ़रल कोड" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Perbarui kata sandi" + "value" : "Kode Referral URnetwork" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Aggiorna password" + "value" : "Codice di riferimento URnetwork" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "パスワードを更新" + "value" : "URnetwork 紹介コード" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "비밀번호 업데이트" + "value" : "URnetwork 추천 코드" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Update Wachtwoord" + "value" : "URnetwork Verwijzingscode" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Aktualizuj hasło" + "value" : "Kod polecający URnetwork" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Atualizar senha" + "value" : "Código de indicação URnetwork" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Atualizar palavra-passe" + "value" : "Código de referência URnetwork" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Обновить пароль" + "value" : "Реферальный код URnetwork" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Uppdatera lösenord" + "value" : "URnetwork referenskod" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "อัปเดตรหัสผ่าน" + "value" : "รหัสแนะนำ URnetwork" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Оновити пароль" + "value" : "Реферальний код URnetwork" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Cập nhật mật khẩu" + "value" : "Mã giới thiệu URnetwork" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "更新密码" + "value" : "URnetwork 推荐码" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "更新密碼" + "value" : "URnetwork 推薦碼" } } } }, - "Update referral network" : { + "URnetwork Status" : { + "comment" : "Used in NetworkApp.swift.", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "تحديث شبكة الإحالة" + "value" : "حالة URnetwork" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Aktualizace referální sítě" + "value" : "Status URnetwork" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Empfehlungsnetzwerk aktualisieren" + "value" : "URnetwork Status" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Ενημέρωση δικτύου παραπομπών" + "value" : "Κατάσταση URnetwork" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Update referral network" + "value" : "URnetwork Status" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Actualizar red de referidos" + "value" : "Estado de URnetwork" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Actualizar red de referidos" + "value" : "Estado de URnetwork" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Mettre à jour le réseau de parrainage" + "value" : "État URnetwork" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "עדכן רשת הפניות" + "value" : "סטטוס URnetwork" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "रेफ़रल नेटवर्क अपडेट करें" + "value" : "URnetwork स्थिति" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Perbarui jaringan referensi" + "value" : "Status URnetwork" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Aggiorna rete di riferimento" + "value" : "Stato URnetwork" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "リファラルネットワークを更新" + "value" : "URnetworkステータス" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "추천 네트워크 업데이트" + "value" : "URnetwork 상태" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Verwijzingsnetwerk bijwerken" + "value" : "URnetwork Status" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Zaktualizuj sieć poleceń" + "value" : "Status URnetwork" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Atualizar rede de indicações" + "value" : "Status URnetwork" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Atualizar rede de recomendações" + "value" : "Estado URnetwork" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Обновить реферальную сеть" + "value" : "Статус URnetwork" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Uppdatera referensnätverk" + "value" : "URnetwork Status" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "อัปเดตเครือข่ายผู้อ้างอิง" + "value" : "สถานะ URnetwork" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Оновити реферальну мережу" + "value" : "Статус URnetwork" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Cập nhật mạng giới thiệu" + "value" : "Trạng thái URnetwork" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "更新推荐网络" + "value" : "URnetwork 状态" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "更新推薦網絡" + "value" : "URnetwork 狀態" } } } }, - "Upgrade" : { + "URnetwork Supporter" : { + "comment" : "Used in Shared/Views/UpgradeSubscriptionSheet.swift.", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "ترقية" + "value" : "داعم URnetwork" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Upgrade" + "value" : "Podporovatel URnetwork" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Upgrade" + "value" : "URnetwork-Unterstützer" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Αναβάθμιση" + "value" : "Υποστηρικτής URnetwork" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Upgrade" + "value" : "URnetwork Supporter" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Actualizar" + "value" : "Colaborador de URnetwork" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Actualizar" + "value" : "Colaborador de URnetwork" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Mettre à niveau" + "value" : "Supporter URnetwork" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "שדרג" + "value" : "תומך URnetwork" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "अपग्रेड करें" + "value" : "URnetwork समर्थक" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Tingkatkan" + "value" : "Pendukung URnetwork" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Aggiorna" + "value" : "Sostenitore URnetwork" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "アップグレード" + "value" : "URnetworkサポーター" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "업그레이드" + "value" : "URnetwork 후원자" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Upgraden" + "value" : "URnetwork-supporter" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Ulepsz" + "value" : "Wspierający URnetwork" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Fazer upgrade" + "value" : "Apoiador URnetwork" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Fazer upgrade" + "value" : "Apoiador URnetwork" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Улучшить" + "value" : "Поддерживающий URnetwork" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Uppgradera" + "value" : "URnetwork-supporter" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "อัปเกรด" + "value" : "ผู้สนับสนุน URnetwork" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Оновити" + "value" : "Підтримувач URnetwork" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Nâng cấp" + "value" : "Người ủng hộ URnetwork" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "升级" + "value" : "URnetwork 支持者" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "升級" + "value" : "URnetwork 支持者" } } } }, - "URid" : { + "URnetwork System Menu" : { + "comment" : "Used in NetworkApp.swift.", "localizations" : { "cs" : { "stringUnit" : { "state" : "translated", - "value" : "URid" + "value" : "Systémové menu URnetwork" } - } - }, - "shouldTranslate" : false - }, - "URnetwork is disconnected" : { - "localizations" : { - "cs" : { + }, + "en" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork je odpojen" + "value" : "URnetwork System Menu" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork is niet verbonden" + "value" : "URnetwork Systeem Menu" } } } }, - "URnetwork is powered by a patented protocol\nthat keeps everyone safe and secure." : { + "URnetwork VPN already started" : { + "comment" : "Used in Shared/Intents/ConnectIntent.swift.", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "يعمل URnetwork ببروتوكول حاصل على براءة اختراع\nيحافظ على أمان الجميع وسلامتهم." + "value" : "URnetwork VPN قد بدأ بالفعل" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork je poháněn patentovaným protokolem\nkterý zajišťuje bezpečnost všech." + "value" : "URnetwork VPN již byla spuštěna" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork wird von einem patentierten Protokoll betrieben,\ndas für die Sicherheit aller sorgt." + "value" : "URnetwork VPN bereits gestartet" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Το URnetwork λειτουργεί με ένα κατοχυρωμένο πρωτόκολλο\nπου διασφαλίζει την ασφάλεια όλων." + "value" : "Το URnetwork VPN έχει ήδη ξεκινήσει" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork is powered by a patented protocol\nthat keeps everyone safe and secure." + "value" : "URnetwork VPN already started" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork funciona con un protocolo patentado\nque mantiene a todos seguros y protegidos." + "value" : "URnetwork VPN ya iniciada" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork funciona con un protocolo patentado\nque mantiene a todos seguros y protegidos." + "value" : "URnetwork VPN ya iniciada" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork fonctionne grâce à un protocole breveté\nqui garantit la sécurité de tous." + "value" : "URnetwork VPN déjà démarré" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork מופעל על ידי פרוטוקול רשום\nשמבטיח שכולם יהיו בטוחים ומוגנים." + "value" : "URnetwork VPN כבר פועל" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork एक पेटेंटेड प्रोटोकॉल द्वारा संचालित है\nजो सभी को सुरक्षित और संरक्षित रखता है।" + "value" : "URnetwork VPN पहले से चालू है" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork didukung oleh protokol yang dipatenkan\nyang menjaga semua orang tetap aman dan terlindungi." + "value" : "URnetwork VPN sudah dimulai" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork è alimentato da un protocollo brevettato\nche mantiene tutti al sicuro." + "value" : "URnetwork VPN già avviata" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "URnetworkは特許取得済みプロトコルで動作しています\nすべての人の安全とセキュリティを守ります。" + "value" : "URnetwork VPNはすでに起動しています" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork는 특허받은 프로토콜로 운영됩니다\n모두의 안전과 보안을 지켜줍니다." + "value" : "URnetwork VPN이 이미 시작됨" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork wordt aangedreven door een gepatenteerd protocol\ndat iedereen veilig houdt." + "value" : "URnetwork VPN is al gestart" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork działa w oparciu o opatentowany protokół\nktóry zapewnia bezpieczeństwo wszystkim." + "value" : "URnetwork VPN już uruchomiona" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork é alimentado por um protocolo patenteado\nque mantém todos seguros e protegidos." + "value" : "URnetwork VPN já iniciada" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork é alimentado por um protocolo patenteado\nque mantém todos seguros e protegidos." + "value" : "URnetwork VPN já iniciada" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork работает на запатентованном протоколе,\nкоторый обеспечивает безопасность всех." + "value" : "URnetwork VPN уже запущен" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork drivs av ett patenterat protokoll\nsom håller alla säkra och trygga." + "value" : "URnetwork VPN redan startad" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork ขับเคลื่อนด้วยโปรโตคอลที่จดสิทธิบัตร\nซึ่งทำให้ทุกคนปลอดภัยและมั่นใจ" + "value" : "URnetwork VPN เริ่มทำงานแล้ว" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork працює на запатентованому протоколі\nякий забезпечує безпеку всіх." + "value" : "URnetwork VPN вже запущено" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork được vận hành bởi giao thức đã được cấp bằng sáng chế\ngiúp mọi người an toàn và bảo mật." + "value" : "URnetwork VPN đã được khởi động" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork由获得专利的协议驱动\n保障每个人的安全。" + "value" : "URnetwork VPN 已启动" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork 由獲得專利的協議驅動\n保障每個人的安全。" + "value" : "URnetwork VPN 已啟動" } } } }, - "URnetwork is the most local and most private network on the planet." : { + "URnetwork VPN disconnected" : { + "comment" : "Used in Shared/Intents/DisconnectIntent.swift.", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork هي الشبكة الأكثر محلية والأكثر خصوصية على الكوكب." + "value" : "URnetwork VPN غير متصل" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork je nejlokálnější a nejvíce soukromá síť na světě." + "value" : "URnetwork VPN odpojena" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork ist das lokalste und privateste Netzwerk der Welt." + "value" : "URnetwork VPN getrennt" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Το URnetwork είναι το πιο τοπικό και πιο ιδιωτικό δίκτυο στον πλανήτη." + "value" : "Το URnetwork VPN αποσυνδέθηκε" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork is the most local and most private network on the planet." + "value" : "URnetwork VPN disconnected" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork es la red más local y privada del planeta." + "value" : "URnetwork VPN desconectada" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork es la red más local y privada del planeta." + "value" : "URnetwork VPN desconectada" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork est le réseau le plus local et le plus privé au monde." + "value" : "URnetwork VPN déconnecté" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork היא הרשת המקומית והפרטית ביותר בעולם." + "value" : "URnetwork VPN מנותק" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork ग्रह की सबसे स्थानीय और सबसे निजी नेटवर्क है।" + "value" : "URnetwork VPN डिस्कनेक्ट हो गया" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork adalah jaringan paling lokal dan paling privat di dunia." + "value" : "URnetwork VPN terputus" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork è la rete più locale e più privata al mondo." + "value" : "URnetwork VPN disconnessa" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "URnetworkは世界で最もローカルで最もプライベートなネットワークです。" + "value" : "URnetwork VPN が切断されました" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork는 지구상에서 가장 지역적이고 가장 프라이빗한 네트워크입니다." + "value" : "URnetwork VPN 연결 해제됨" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork is het meest lokale en meest privé netwerk ter wereld." + "value" : "URnetwork VPN verbinding verbroken" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork to najbardziej lokalna i prywatna sieć na świecie." + "value" : "URnetwork VPN rozłączona" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork é a rede mais local e mais privada do planeta." + "value" : "URnetwork VPN desconectada" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork é a rede mais local e mais privada do planeta." + "value" : "URnetwork VPN desligada" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork — самая локальная и самая приватная сеть на планете." + "value" : "URnetwork VPN отключен" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork är det mest lokala och mest privata nätverket på planeten." + "value" : "URnetwork VPN frånkopplad" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork คือเครือข่ายที่เป็นท้องถิ่นและเป็นส่วนตัวมากที่สุดในโลก" + "value" : "URnetwork VPN ถูกตัดการเชื่อมต่อ" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork — це найлокальніша та найприватніша мережа на планеті." + "value" : "URnetwork VPN відключено" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork là mạng lưới địa phương và riêng tư nhất trên hành tinh." + "value" : "URnetwork VPN đã ngắt kết nối" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork是全球最本地化、最私密的网络。" + "value" : "URnetwork VPN 已断开连接" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork 是全球最本地化及最私密的網絡。" + "value" : "URnetwork VPN 已斷開連接" } } } }, - "URnetwork Referral Code" : { + "URnetwork VPN started" : { + "comment" : "Used in Shared/Intents/ConnectIntent.swift.", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "رمز إحالة URnetwork" + "value" : "تم تشغيل URnetwork VPN" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork referální kód" + "value" : "URnetwork VPN zapnutá" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork Empfehlungscode" + "value" : "URnetwork VPN gestartet" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Κωδικός παραπομπής URnetwork" + "value" : "Το URnetwork VPN ξεκίνησε" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork Referral Code" + "value" : "URnetwork VPN started" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Código de referencia URnetwork" + "value" : "URnetwork VPN iniciada" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Código de referencia URnetwork" + "value" : "URnetwork VPN iniciada" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Code de parrainage URnetwork" + "value" : "URnetwork VPN démarré" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "קוד הפניה URnetwork" + "value" : "URnetwork VPN הופעל" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork रेफ़रल कोड" + "value" : "URnetwork VPN शुरू हो गया" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Kode Referral URnetwork" + "value" : "URnetwork VPN dimulai" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Codice di riferimento URnetwork" + "value" : "URnetwork VPN avviata" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork 紹介コード" + "value" : "URnetwork VPN が起動しました" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork 추천 코드" + "value" : "URnetwork VPN 시작됨" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork Verwijzingscode" + "value" : "URnetwork VPN gestart" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Kod polecający URnetwork" + "value" : "URnetwork VPN uruchomiona" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Código de indicação URnetwork" + "value" : "URnetwork VPN iniciada" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Código de referência URnetwork" + "value" : "URnetwork VPN iniciada" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Реферальный код URnetwork" + "value" : "URnetwork VPN запущен" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork referenskod" + "value" : "URnetwork VPN startad" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "รหัสแนะนำ URnetwork" + "value" : "URnetwork VPN เริ่มทำงานแล้ว" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Реферальний код URnetwork" + "value" : "URnetwork VPN запущено" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Mã giới thiệu URnetwork" + "value" : "URnetwork VPN đã khởi động" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork 推荐码" + "value" : "URnetwork VPN 已启动" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork 推薦碼" + "value" : "URnetwork VPN 已啟動" } } } }, - "URnetwork Status" : { + "USDC" : { + "comment" : "Used in Main/Account/AccountRootView.swift.", + "shouldTranslate" : false + }, + "USDC addresses on Solana are currently supported" : { "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "حالة URnetwork" + "value" : "حاليًا يتم دعم عناوين USDC على Solana" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Status URnetwork" + "value" : "V současné době jsou podporovány adresy USDC v síti Solana" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork Status" + "value" : "USDC-Adressen auf Solana werden derzeit unterstützt" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Κατάσταση URnetwork" + "value" : "Υποστηρίζονται αυτήν τη στιγμή διευθύνσεις USDC στο Solana" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "USDC addresses on Solana are currently supported" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Estado de URnetwork" + "value" : "Actualmente se admiten direcciones USDC en Solana" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Estado de URnetwork" + "value" : "Actualmente se admiten direcciones USDC en Solana" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "État URnetwork" + "value" : "Les adresses USDC sur Solana sont actuellement prises en charge" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "סטטוס URnetwork" + "value" : "כתובות USDC ב-Solana נתמכות כרגע" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork स्थिति" + "value" : "Solana पर USDC पतों का वर्तमान में समर्थन किया जाता है" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Status URnetwork" + "value" : "Alamat USDC di Solana saat ini didukung" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Stato URnetwork" + "value" : "Gli indirizzi USDC su Solana sono attualmente supportati" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "URnetworkステータス" + "value" : "Solana上のUSDCアドレスは現在サポートされています" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork 상태" + "value" : "Solana의 USDC 주소가 현재 지원됩니다" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork Status" + "value" : "USDC-adressen op Solana worden momenteel ondersteund" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Status URnetwork" + "value" : "Adresy USDC w sieci Solana są obecnie obsługiwane" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Status URnetwork" + "value" : "Endereços USDC na Solana são atualmente suportados" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Estado URnetwork" + "value" : "Endereços USDC na Solana são atualmente suportados" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Статус URnetwork" + "value" : "В настоящее время поддерживаются адреса USDC в сети Solana" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork Status" + "value" : "USDC-adresser på Solana stöds för närvarande" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "สถานะ URnetwork" + "value" : "ขณะนี้รองรับที่อยู่ USDC บน Solana" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Статус URnetwork" + "value" : "Адреси USDC на Solana наразі підтримуються" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Trạng thái URnetwork" + "value" : "Hiện tại hỗ trợ địa chỉ USDC trên Solana" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork 状态" + "value" : "目前支持 Solana 上的 USDC 地址" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork 狀態" + "value" : "目前支援 Solana 上的 USDC 地址" } } } }, - "URnetwork Supporter" : { + "USDC wallet address" : { "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "داعم URnetwork" + "value" : "عنوان محفظة USDC" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Podporovatel URnetwork" + "value" : "Adresa peněženky USDC" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork-Unterstützer" + "value" : "USDC-Wallet-Adresse" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Υποστηρικτής URnetwork" + "value" : "Διεύθυνση πορτοφολιού USDC" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork Supporter" + "value" : "USDC wallet address" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Colaborador de URnetwork" + "value" : "Dirección del monedero USDC" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Colaborador de URnetwork" + "value" : "Dirección de la billetera USDC" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Supporter URnetwork" + "value" : "Adresse du portefeuille USDC" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "תומך URnetwork" + "value" : "כתובת ארנק USDC" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork समर्थक" + "value" : "USDC वॉलेट पता" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Pendukung URnetwork" + "value" : "Alamat dompet USDC" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Sostenitore URnetwork" + "value" : "Indirizzo portafoglio USDC" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "URnetworkサポーター" + "value" : "USDCウォレットアドレス" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork 후원자" + "value" : "USDC 월렛 주소" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork-supporter" + "value" : "USDC wallet address" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Wspierający URnetwork" + "value" : "Adres portfela USDC" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Apoiador URnetwork" + "value" : "Endereço da carteira USDC" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Apoiador URnetwork" + "value" : "Endereço da carteira USDC" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Поддерживающий URnetwork" + "value" : "Адрес кошелька USDC" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork-supporter" + "value" : "USDC-plånboksadress" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "ผู้สนับสนุน URnetwork" + "value" : "ที่อยู่กระเป๋าเงิน USDC" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Підтримувач URnetwork" + "value" : "Адреса гаманця USDC" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Người ủng hộ URnetwork" + "value" : "Địa chỉ ví USDC" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork 支持者" + "value" : "USDC 钱包地址" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork 支持者" + "value" : "USDC 錢包地址" } } } }, - "URnetwork System Menu" : { + "Use recommended settings" : { + "comment" : "dns settings", "localizations" : { - "cs" : { + "ar" : { "stringUnit" : { "state" : "translated", - "value" : "Systémové menu URnetwork" + "value" : "استخدام الإعدادات الموصى بها" } }, - "nl" : { + "cs" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork Systeem Menu" + "value" : "Použít doporučené nastavení" } - } - } - }, - "URnetwork VPN already started" : { - "localizations" : { - "ar" : { + }, + "de" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork VPN قد بدأ بالفعل" + "value" : "Empfohlene Einstellungen verwenden" } }, - "cs" : { + "el" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork VPN již byla spuštěna" + "value" : "Χρήση προτεινόμενων ρυθμίσεων" } }, - "de" : { + "en" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork VPN bereits gestartet" + "value" : "Use recommended settings" } }, - "el" : { + "es" : { "stringUnit" : { "state" : "translated", - "value" : "Το URnetwork VPN έχει ήδη ξεκινήσει" + "value" : "Usar los ajustes recomendados" } }, - "es" : { + "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork VPN ya iniciada" + "value" : "Usar la configuración recomendada" } }, - "es-419" : { + "es-MX" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork VPN ya iniciada" + "value" : "Usar la configuración recomendada" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork VPN déjà démarré" + "value" : "Utiliser les paramètres recommandés" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork VPN כבר פועל" + "value" : "השתמש בהגדרות המומלצות" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork VPN पहले से चालू है" + "value" : "अनुशंसित सेटिंग्स का उपयोग करें" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork VPN sudah dimulai" + "value" : "Gunakan pengaturan yang disarankan" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork VPN già avviata" + "value" : "Usa le impostazioni consigliate" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork VPNはすでに起動しています" + "value" : "推奨設定を使用" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork VPN이 이미 시작됨" + "value" : "권장 설정 사용" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork VPN is al gestart" + "value" : "Aanbevolen instellingen gebruiken" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork VPN już uruchomiona" + "value" : "Użyj zalecanych ustawień" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Usar definições recomendadas" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork VPN já iniciada" + "value" : "Usar configurações recomendadas" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork VPN já iniciada" + "value" : "Usar definições recomendadas" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork VPN уже запущен" + "value" : "Использовать рекомендованные настройки" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork VPN redan startad" + "value" : "Använd rekommenderade inställningar" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork VPN เริ่มทำงานแล้ว" + "value" : "ใช้การตั้งค่าที่แนะนำ" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork VPN вже запущено" + "value" : "Використати рекомендовані налаштування" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork VPN đã được khởi động" + "value" : "Dùng cài đặt được khuyến nghị" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork VPN 已启动" + "value" : "使用推荐设置" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork VPN 已啟動" + "value" : "使用建議設定" } } } }, - "URnetwork VPN disconnected" : { + "Uses the UR Protocol" : { + "comment" : "Call to action", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork VPN غير متصل" + "value" : "يستخدم UR Protocol" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork VPN odpojena" + "value" : "Používá UR Protocol" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork VPN getrennt" + "value" : "Nutzt das UR Protocol" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Το URnetwork VPN αποσυνδέθηκε" + "value" : "Χρησιμοποιεί το UR Protocol" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Uses the UR Protocol" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork VPN desconectada" + "value" : "Usa el UR Protocol" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork VPN desconectada" + "value" : "Usa el UR Protocol" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Usa el UR Protocol" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork VPN déconnecté" + "value" : "Utilise UR Protocol" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork VPN מנותק" + "value" : "משתמש ב-UR Protocol" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork VPN डिस्कनेक्ट हो गया" + "value" : "UR Protocol का उपयोग करता है" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork VPN terputus" + "value" : "Menggunakan UR Protocol" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork VPN disconnessa" + "value" : "Utilizza UR Protocol" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork VPN が切断されました" + "value" : "UR Protocolを使用" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork VPN 연결 해제됨" + "value" : "UR Protocol 사용" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork VPN verbinding verbroken" + "value" : "Gebruikt het UR Protocol" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork VPN rozłączona" + "value" : "Korzysta z UR Protocol" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Utiliza o UR Protocol" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork VPN desconectada" + "value" : "Usa o UR Protocol" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork VPN desligada" + "value" : "Utiliza o UR Protocol" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork VPN отключен" + "value" : "Использует UR Protocol" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork VPN frånkopplad" + "value" : "Använder UR Protocol" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork VPN ถูกตัดการเชื่อมต่อ" + "value" : "ใช้ UR Protocol" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork VPN відключено" + "value" : "Використовує UR Protocol" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork VPN đã ngắt kết nối" + "value" : "Sử dụng UR Protocol" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork VPN 已断开连接" + "value" : "使用 UR Protocol" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork VPN 已斷開連接" + "value" : "使用 UR Protocol" } } } }, - "URnetwork VPN started" : { + "Using most secure default settings" : { + "comment" : "dns settings", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "تم تشغيل URnetwork VPN" + "value" : "استخدام الإعدادات الافتراضية الأكثر أمانًا" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork VPN zapnutá" + "value" : "Používáte nejbezpečnější výchozí nastavení" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork VPN gestartet" + "value" : "Sicherste Standardeinstellungen aktiv" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Το URnetwork VPN ξεκίνησε" + "value" : "Χρήση των πιο ασφαλών προεπιλογών" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Using most secure default settings" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork VPN iniciada" + "value" : "Usando los ajustes predeterminados más seguros" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork VPN iniciada" + "value" : "Usando la configuración predeterminada más segura" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Usando la configuración predeterminada más segura" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork VPN démarré" + "value" : "Utilisation des paramètres par défaut les plus sécurisés" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork VPN הופעל" + "value" : "משתמש בהגדרות ברירת המחדל המאובטחות ביותר" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork VPN शुरू हो गया" + "value" : "सबसे सुरक्षित डिफ़ॉल्ट सेटिंग्स का उपयोग हो रहा है" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork VPN dimulai" + "value" : "Menggunakan pengaturan default paling aman" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork VPN avviata" + "value" : "Impostazioni predefinite più sicure in uso" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork VPN が起動しました" + "value" : "最も安全なデフォルト設定を使用中" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork VPN 시작됨" + "value" : "가장 안전한 기본 설정 사용 중" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork VPN gestart" + "value" : "Veiligste standaardinstellingen actief" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork VPN uruchomiona" + "value" : "Używasz najbezpieczniejszych ustawień domyślnych" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "A usar as definições predefinidas mais seguras" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork VPN iniciada" + "value" : "Usando as configurações padrão mais seguras" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork VPN iniciada" + "value" : "A usar as definições predefinidas mais seguras" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork VPN запущен" + "value" : "Используются самые безопасные настройки по умолчанию" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork VPN startad" + "value" : "Använder de säkraste standardinställningarna" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork VPN เริ่มทำงานแล้ว" + "value" : "กำลังใช้การตั้งค่าเริ่มต้นที่ปลอดภัยที่สุด" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork VPN запущено" + "value" : "Використовуються найбезпечніші налаштування за замовчуванням" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork VPN đã khởi động" + "value" : "Đang dùng cài đặt mặc định an toàn nhất" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork VPN 已启动" + "value" : "正在使用最安全的默认设置" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "URnetwork VPN 已啟動" + "value" : "正在使用最安全的預設設定" } } } }, - "URnode" : { - - }, - "USDC" : { - "localizations" : { - "cs" : { - "stringUnit" : { - "state" : "translated", - "value" : "USDC" - } - } - }, - "shouldTranslate" : false - }, - "USDC addresses on Solana are currently supported" : { + "Using recommended regional settings" : { + "comment" : "dns settings", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "حاليًا يتم دعم عناوين USDC على سولانا" + "value" : "استخدام الإعدادات الإقليمية الموصى بها" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "V současné době jsou podporovány adresy USDC na Solaně" + "value" : "Používáte doporučené regionální nastavení" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "USDC-Adressen auf Solana werden derzeit unterstützt" + "value" : "Empfohlene regionale Einstellungen aktiv" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Υποστηρίζονται αυτήν τη στιγμή διευθύνσεις USDC στο Solana" + "value" : "Χρήση προτεινόμενων ρυθμίσεων περιοχής" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "USDC addresses on Solana are currently supported" + "value" : "Using recommended regional settings" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Actualmente se admiten direcciones USDC en Solana" + "value" : "Usando los ajustes regionales recomendados" } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Actualmente se admiten direcciones USDC en Solana" + "value" : "Usando la configuración regional recomendada" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Usando la configuración regional recomendada" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Les adresses USDC sur Solana sont actuellement prises en charge" + "value" : "Utilisation des paramètres régionaux recommandés" } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "כתובות USDC ב-Solana נתמכות כרגע" + "value" : "משתמש בהגדרות אזוריות מומלצות" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "Solana पर USDC पतों का वर्तमान में समर्थन किया जाता है" + "value" : "अनुशंसित क्षेत्रीय सेटिंग्स का उपयोग हो रहा है" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Alamat USDC di Solana saat ini didukung" + "value" : "Menggunakan pengaturan regional yang disarankan" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Gli indirizzi USDC su Solana sono attualmente supportati" + "value" : "Impostazioni regionali consigliate in uso" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "Solana上のUSDCアドレスは現在サポートされています" + "value" : "地域の推奨設定を使用中" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "Solana의 USDC 주소가 현재 지원됩니다" + "value" : "지역 권장 설정 사용 중" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "USDC-adressen op Solana worden momenteel ondersteund" + "value" : "Aanbevolen regionale instellingen actief" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Adresy USDC na Solanie są obecnie obsługiwane" + "value" : "Używasz zalecanych ustawień regionalnych" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "A usar as definições regionais recomendadas" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Endereços USDC na Solana são atualmente suportados" + "value" : "Usando as configurações regionais recomendadas" } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Endereços USDC na Solana são atualmente suportados" + "value" : "A usar as definições regionais recomendadas" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "В настоящее время поддерживаются адреса USDC в сети Solana" + "value" : "Используются рекомендованные региональные настройки" } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "USDC-adresser på Solana stöds för närvarande" + "value" : "Använder rekommenderade regionala inställningar" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "ขณะนี้รองรับที่อยู่ USDC บน Solana" + "value" : "กำลังใช้การตั้งค่าที่แนะนำสำหรับภูมิภาค" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Адреси USDC на Solana наразі підтримуються" + "value" : "Використовуються рекомендовані регіональні налаштування" } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Hiện tại hỗ trợ địa chỉ USDC trên Solana" + "value" : "Đang dùng cài đặt khuyến nghị theo khu vực" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "目前支持 Solana 上的 USDC 地址" + "value" : "正在使用推荐的地区设置" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "目前支援 Solana 上的 USDC 地址" + "value" : "正在使用建議的地區設定" } } } }, - "USDC wallet address" : { + "Verification code sent." : { + "comment" : "Snackbar shown after the verification code is resent. Used in Authenticate/CreateNetworkVerify/CreateNetworkVerifyView.swift.", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "عنوان محفظة USDC" + "value" : "تم إرسال رمز التحقق." } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Adresa peněženky USDC" + "value" : "Ověřovací kód byl odeslán." } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "USDC-Wallet-Adresse" + "value" : "Bestätigungscode gesendet." } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Διεύθυνση πορτοφολιού USDC" + "value" : "Ο κωδικός επαλήθευσης στάλθηκε." } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "USDC wallet address" + "value" : "Verification code sent." } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Dirección del monedero USDC" + "value" : "Código de verificación enviado." } }, "es-419" : { "stringUnit" : { "state" : "translated", - "value" : "Dirección de la billetera USDC" + "value" : "Código de verificación enviado." + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Código de verificación enviado." } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Adresse du portefeuille USDC" + "value" : "Code de vérification envoyé." } }, "he" : { "stringUnit" : { "state" : "translated", - "value" : "כתובת ארנק USDC" + "value" : "קוד האימות נשלח." } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "USDC वॉलेट पता" + "value" : "सत्यापन कोड भेजा गया।" } }, "id" : { "stringUnit" : { "state" : "translated", - "value" : "Alamat dompet USDC" + "value" : "Kode verifikasi terkirim." } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Indirizzo portafoglio USDC" + "value" : "Codice di verifica inviato." } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "USDCウォレットアドレス" + "value" : "確認コードを送信しました。" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "USDC 월렛 주소" + "value" : "인증 코드를 보냈습니다." } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "USDC wallet address" + "value" : "Verificatiecode verzonden." } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Adres portfela USDC" + "value" : "Kod weryfikacyjny został wysłany." + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Código de verificação enviado." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Endereço da carteira USDC" + "value" : "Código de verificação enviado." } }, "pt-PT" : { "stringUnit" : { "state" : "translated", - "value" : "Endereço da carteira USDC" + "value" : "Código de verificação enviado." } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Адрес кошелька USDC" + "value" : "Код подтверждения отправлен." } }, "sv" : { "stringUnit" : { "state" : "translated", - "value" : "USDC-plånboksadress" + "value" : "Verifieringskod skickad." } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "ที่อยู่กระเป๋าเงิน USDC" + "value" : "ส่งรหัสยืนยันแล้ว" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Адреса гаманця USDC" + "value" : "Код підтвердження надіслано." } }, "vi" : { "stringUnit" : { "state" : "translated", - "value" : "Địa chỉ ví USDC" + "value" : "Đã gửi mã xác minh." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "USDC 钱包地址" + "value" : "验证码已发送。" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "USDC 錢包地址" + "value" : "驗證碼已發送。" } } } - }, - "Use recommended settings" : { - - }, - "Using most secure default settings" : { - - }, - "Using recommended regional settings" : { - }, "Verified project on [DePIN Hub](https://depinhub.io/projects/urnetwork)" : { + "comment" : "Used in Main/Account/Settings/DePinHubSettingsLinkRow.swift.", "localizations" : { "ar" : { "stringUnit" : { @@ -37544,6 +62299,7 @@ } }, "Verify" : { + "comment" : "Used in Main/Account/Settings/SettingsForm-iOS.swift.", "localizations" : { "ar" : { "stringUnit" : { @@ -37698,6 +62454,7 @@ } }, "Version and Build info" : { + "comment" : "Used in Main/Account/Settings/SettingsForm-iOS.swift.", "localizations" : { "cs" : { "stringUnit" : { @@ -37705,6 +62462,12 @@ "value" : "Informace o verzi a sestavení" } }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Version and Build info" + } + }, "nl" : { "stringUnit" : { "state" : "translated", @@ -37714,6 +62477,7 @@ } }, "VPN tunnel disconnected 😓" : { + "comment" : "Call to action", "localizations" : { "ar" : { "stringUnit" : { @@ -37736,7 +62500,7 @@ "el" : { "stringUnit" : { "state" : "translated", - "value" : "Αποσυνδέθηκε η σήραγγα VPN 😓" + "value" : "Το τούνελ VPN αποσυνδέθηκε 😓" } }, "en" : { @@ -37757,6 +62521,12 @@ "value" : "Túnel VPN desconectado 😓" } }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Túnel VPN desconectado 😓" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -37796,19 +62566,25 @@ "ko" : { "stringUnit" : { "state" : "translated", - "value" : "VPN 터널 연결이 끊어졌습니다 😓" + "value" : "VPN 터널 연결 해제됨 😓" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "VPN tunnel verbroken 😓" + "value" : "VPN-tunnel verbroken 😓" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Tunel VPN został rozłączony 😓" + "value" : "Tunel VPN rozłączony 😓" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Túnel VPN desconectado 😓" } }, "pt-BR" : { @@ -37838,7 +62614,7 @@ "th" : { "stringUnit" : { "state" : "translated", - "value" : "อุโมงค์ VPN ถูกตัดการเชื่อมต่อ 😓" + "value" : "VPN ถูกตัดการเชื่อมต่อ" } }, "uk" : { @@ -37856,7 +62632,7 @@ "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "VPN 隧道已断开 😓" + "value" : "VPN 隧道已断开连接 😓" } }, "zh-HK" : { @@ -37867,12 +62643,168 @@ } } }, + "Waiting for approval" : { + "comment" : "Used in Shared/Views/UpgradeSubscriptionSheet.swift.", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "في انتظار الموافقة" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Čeká se na schválení" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Warten auf Genehmigung" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Αναμονή έγκρισης" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Waiting for approval" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Esperando aprobación" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "Esperando aprobación" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "En attente d'approbation" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "ממתין לאישור" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "अनुमोदन की प्रतीक्षा है" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Menunggu persetujuan" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "In attesa di approvazione" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "承認待ち" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "승인 대기 중" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Wachten op goedkeuring" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Oczekiwanie na zatwierdzenie" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aguardando aprovação" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "A aguardar aprovação" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ожидание подтверждения" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Väntar på godkännande" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "รอการอนุมัติ" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Очікування підтвердження" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Đang chờ phê duyệt" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "等待批准" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "等待批准" + } + } + } + }, "Wallet" : { + "comment" : "Account Screen", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "محفظة" + "value" : "المحفظة" } }, "cs" : { @@ -37911,6 +62843,12 @@ "value" : "Billetera" } }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cartera" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -37965,6 +62903,12 @@ "value" : "Portfel" } }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Carteira" + } + }, "pt-BR" : { "stringUnit" : { "state" : "translated", @@ -37992,7 +62936,7 @@ "th" : { "stringUnit" : { "state" : "translated", - "value" : "กระเป๋าเงิน" + "value" : "กระเป๋า" } }, "uk" : { @@ -38021,7 +62965,175 @@ } } }, + "Wallet address" : { + "comment" : "Wallets Screen", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "عنوان المحفظة" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Adresa peněženky" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Wallet-Adresse" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Διεύθυνση πορτοφολιού" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Wallet address" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dirección de la billetera" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dirección de la billetera" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dirección de la cartera" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Adresse du portefeuille" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "כתובת ארנק" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "वॉलेट पता" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Alamat dompet" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Indirizzo del portafoglio" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "ウォレットアドレス" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "지갑 주소" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Wallet adres" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Adres portfela" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Endereço da carteira" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Endereço da carteira" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Endereço da carteira" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Адрес кошелька" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Plånboksadress" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "ที่อยู่กระเป๋าเงิน" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Адреса гаманця" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Địa chỉ ví" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "钱包地址" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "錢包地址" + } + } + } + }, "Wallet Address" : { + "comment" : "Call to action", "localizations" : { "ar" : { "stringUnit" : { @@ -38065,6 +63177,12 @@ "value" : "Dirección de la billetera" } }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dirección de la billetera" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -38110,7 +63228,7 @@ "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Wallet Adress" + "value" : "Walletadres" } }, "pl" : { @@ -38119,6 +63237,12 @@ "value" : "Adres portfela" } }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Endereço da carteira" + } + }, "pt-BR" : { "stringUnit" : { "state" : "translated", @@ -38176,17 +63300,18 @@ } }, "Wallets" : { + "comment" : "Wallets Screen", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "المحافظ" + "value" : "محافظ" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Penězěnky" + "value" : "Peněženky" } }, "de" : { @@ -38210,7 +63335,7 @@ "es" : { "stringUnit" : { "state" : "translated", - "value" : "Monederos" + "value" : "Billeteras" } }, "es-419" : { @@ -38219,6 +63344,12 @@ "value" : "Billeteras" } }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Carteras" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -38258,7 +63389,7 @@ "ko" : { "stringUnit" : { "state" : "translated", - "value" : "월렛" + "value" : "지갑" } }, "nl" : { @@ -38273,6 +63404,12 @@ "value" : "Portfele" } }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Carteiras" + } + }, "pt-BR" : { "stringUnit" : { "state" : "translated", @@ -38300,7 +63437,7 @@ "th" : { "stringUnit" : { "state" : "translated", - "value" : "กระเป๋าเงิน" + "value" : "รายชื่อกระเป๋า" } }, "uk" : { @@ -38331,9 +63468,174 @@ }, "Web" : { "comment" : "A button label that says \"Web\".", - "isCommentAutoGenerated" : true + "isCommentAutoGenerated" : true, + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "الويب" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Web" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Web" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ιστός" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Web" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Web" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "Web" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Web" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Web" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "גלישה" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "वेब" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Web" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Web" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "ウェブ" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "웹" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Web" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "WWW" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Web" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Web" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Web" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Веб" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Webb" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "เว็บ" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Веб" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Web" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "网页" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "網頁" + } + } + } }, "Welcome to URnetwork" : { + "comment" : "Call to action", "localizations" : { "ar" : { "stringUnit" : { @@ -38377,6 +63679,12 @@ "value" : "Bienvenido a URnetwork" } }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bienvenido a URnetwork" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -38428,7 +63736,13 @@ "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Witamy w URnetwork" + "value" : "Witaj w URnetwork" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bem-vindo ao URnetwork" } }, "pt-BR" : { @@ -38476,7 +63790,7 @@ "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "欢迎来到URnetwork" + "value" : "欢迎来到 URnetwork" } }, "zh-HK" : { @@ -38488,6 +63802,7 @@ } }, "When unlinking your referral network, you will no longer be able to earn points from %@." : { + "comment" : "Used in Main/Account/Settings/UpdateReferralNetworkSheet/UpdateReferralNetworkSheet.swift.", "localizations" : { "ar" : { "stringUnit" : { @@ -38642,11 +63957,12 @@ } }, "When you refer a friend:" : { + "comment" : "Call to action", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "عند توصيتك لصديق:" + "value" : "عندما تقوم بإحالة صديق:" } }, "cs" : { @@ -38658,13 +63974,13 @@ "de" : { "stringUnit" : { "state" : "translated", - "value" : "Wenn Sie einen Freund empfehlen:" + "value" : "Wenn du einen Freund empfiehlst:" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Όταν συστήνετε έναν φίλο:" + "value" : "Όταν προτείνετε έναν φίλο:" } }, "en" : { @@ -38685,6 +64001,12 @@ "value" : "Cuando recomiendas a un amigo:" } }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cuando recomiendas a un amigo:" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -38700,7 +64022,7 @@ "hi" : { "stringUnit" : { "state" : "translated", - "value" : "जब आप मित्र को रेफ़र करते हैं:" + "value" : "जब आप किसी मित्र को रेफ़र करते हैं:" } }, "id" : { @@ -38718,7 +64040,7 @@ "ja" : { "stringUnit" : { "state" : "translated", - "value" : "友達を紹介すると:" + "value" : "友達を紹介するとき:" } }, "ko" : { @@ -38739,6 +64061,12 @@ "value" : "Gdy polecisz znajomego:" } }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Quando recomenda um amigo:" + } + }, "pt-BR" : { "stringUnit" : { "state" : "translated", @@ -38754,7 +64082,7 @@ "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Когда вы рекомендуете друга:" + "value" : "Когда вы приглашаете друга:" } }, "sv" : { @@ -38951,6 +64279,7 @@ } }, "You can also allow the provider to use cell network data, which works great if you have an unlimited plan." : { + "comment" : "Call to action", "localizations" : { "ar" : { "stringUnit" : { @@ -38961,19 +64290,19 @@ "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Můžete také povolit poskytovateli používat data mobilní sítě, což je skvělé, pokud máte neomezený tarif." + "value" : "Poskytovateli můžete také povolit využívat data z mobilní sítě, což je skvělé, pokud máte neomezený tarif." } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Sie können dem Anbieter auch erlauben, Mobilfunkdaten zu verwenden, was besonders gut funktioniert, wenn Sie eine unbegrenzte Datenflat haben." + "value" : "Du kannst dem Anbieter auch erlauben, Mobilfunkdaten zu verwenden – das funktioniert super, wenn du eine unbegrenzte Datenflat hast." } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Μπορείτε επίσης να επιτρέψετε στον πάροχο να χρησιμοποιεί δεδομένα κινητού δικτύου, κάτι που λειτουργεί άψογα αν έχετε απεριόριστο πρόγραμμα." + "value" : "Μπορείτε επίσης να επιτρέψετε στον πάροχο να χρησιμοποιεί δεδομένα κινητού δικτύου, κάτι που λειτουργεί τέλεια αν έχετε απεριόριστο πρόγραμμα." } }, "en" : { @@ -38985,7 +64314,7 @@ "es" : { "stringUnit" : { "state" : "translated", - "value" : "También puedes permitir que el proveedor use datos de la red celular, lo cual funciona muy bien si tienes un plan ilimitado." + "value" : "También puedes permitir que el proveedor use datos de red celular, lo cual funciona muy bien si tienes un plan ilimitado." } }, "es-419" : { @@ -38994,10 +64323,16 @@ "value" : "También puedes permitir que el proveedor use datos de la red celular, lo cual funciona muy bien si tienes un plan ilimitado." } }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "También puedes permitir que el proveedor use datos de red celular, lo cual funciona muy bien si tienes un plan ilimitado." + } + }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Vous pouvez aussi autoriser le fournisseur à utiliser les données du réseau cellulaire, ce qui fonctionne très bien si vous avez un forfait illimité." + "value" : "Vous pouvez également autoriser le fournisseur à utiliser les données du réseau cellulaire, ce qui fonctionne très bien si vous avez un forfait illimité." } }, "he" : { @@ -39033,19 +64368,25 @@ "ko" : { "stringUnit" : { "state" : "translated", - "value" : "공급자가 셀 네트워크 데이터를 사용하도록 허용할 수도 있습니다. 무제한 요금제가 있다면 아주 잘 작동합니다." + "value" : "제공자가 셀 네트워크 데이터를 사용하도록 허용할 수도 있습니다. 무제한 요금제를 사용한다면 아주 좋습니다." } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Je kunt de provider ook toestaan om mobiele netwerkdata te gebruiken, wat ideaal is als je een onbeperkt abonnement hebt." + "value" : "Je kunt de provider ook toestaan om mobiele data te gebruiken, wat ideaal is als je een onbeperkt abonnement hebt." } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Możesz także pozwolić dostawcy na używanie danych sieci komórkowej, co świetnie działa, jeśli masz nielimitowany abonament." + "value" : "Możesz także pozwolić dostawcy na korzystanie z danych sieci komórkowej, co świetnie działa, jeśli masz nielimitowany plan." + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Também pode permitir que o fornecedor utilize dados da rede móvel, o que funciona muito bem se tiver um plano ilimitado." } }, "pt-BR" : { @@ -39075,7 +64416,7 @@ "th" : { "stringUnit" : { "state" : "translated", - "value" : "คุณสามารถอนุญาตให้ผู้ให้บริการใช้ข้อมูลเครือข่ายมือถือได้ ซึ่งเหมาะมากหากคุณมีแพ็กเกจไม่จำกัด" + "value" : "คุณยังสามารถอนุญาตให้ผู้ให้บริการใช้ข้อมูลเครือข่ายมือถือ ซึ่งเหมาะมากหากคุณมีแพ็กเกจแบบไม่จำกัด" } }, "uk" : { @@ -39093,7 +64434,7 @@ "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "您还可以允许提供者使用蜂窝网络数据,如果您有不限流量套餐,这非常适用。" + "value" : "您还可以允许提供者使用蜂窝网络数据,如果您有无限流量套餐,这非常适用。" } }, "zh-HK" : { @@ -39105,6 +64446,7 @@ } }, "You get 30 GiB/month for life" : { + "comment" : "Used in Shared/Views/Introduction/ParticipateReferView.swift.", "localizations" : { "ar" : { "stringUnit" : { @@ -39260,9 +64602,174 @@ }, "You get free data every month, and can earn more by referring friends." : { "comment" : "A description of how users can earn more data by referring friends.", - "isCommentAutoGenerated" : true + "isCommentAutoGenerated" : true, + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "من خلال المشاركة في الشبكة وتوفير عرض النطاق الترددي، يمكنك كسب المكافآت." + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Účastí v síti a poskytováním šířky pásma můžete získat odměny." + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Durch die Teilnahme am Netzwerk und das Bereitstellen von Bandbreite kannst du Belohnungen verdienen." + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Συμμετέχοντας στο δίκτυο και παρέχοντας εύρος ζώνης μπορείτε να κερδίσετε ανταμοιβές." + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "You get free data every month, and can earn more by referring friends." + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Al participar en la red y proporcionar ancho de banda puedes ganar recompensas." + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cada mes recibes datos gratis y puedes ganar más recomendando a amigos." + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Al participar en la red y proporcionar ancho de banda puedes ganar recompensas." + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "En participant au réseau et en fournissant de la bande passante, vous pouvez gagner des récompenses." + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "אתה מקבל נתונים חינם בכל חודש, ואפשר להרוויח עוד על ידי המלצה על חברים." + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "नेटवर्क में भाग लेकर और बैंडविड्थ प्रदान करके आप पुरस्कार कमा सकते हैं।" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Anda mendapat data gratis setiap bulan, dan bisa mendapatkan lebih banyak dengan mereferensikan teman." + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ricevi dati gratuiti ogni mese e puoi guadagnarne di più invitando amici." + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "ネットワークに参加し、帯域幅を提供することで報酬を得ることができます。" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "네트워크에 참여하고 대역폭을 제공하면 보상을 받을 수 있습니다." + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Door deel te nemen aan het netwerk en bandbreedte te leveren kun je beloningen verdienen." + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Uczestnicząc w sieci i udostępniając przepustowość, możesz zdobywać nagrody." + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ao participar na rede e fornecer largura de banda, pode ganhar recompensas." + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ao participar da rede e fornecer largura de banda, você pode ganhar recompensas." + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Recebe dados gratuitos todos os meses e pode ganhar mais ao recomendar amigos." + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Участвуя в сети и предоставляя пропускную способность, вы можете получать награды." + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Du får gratis data varje månad och kan tjäna mer genom att tipsa vänner." + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "โดยการเข้าร่วมเครือข่ายและให้แบนด์วิดท์ คุณสามารถรับรางวัลได้" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Щомісяця ви отримуєте безкоштовні дані та можете заробити більше, запрошуючи друзів." + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bạn nhận dữ liệu miễn phí mỗi tháng và có thể kiếm thêm bằng cách giới thiệu bạn bè." + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "通过参与网络并提供带宽,您可以获得奖励。" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "你每月都能獲得免費數據,推薦朋友還可賺取更多。" + } + } + } }, "You may need to your check spam folder or unblock no-reply@ur.io" : { + "comment" : "Used in Authenticate/ResetPassword/ResetPasswordView.swift.", "localizations" : { "ar" : { "stringUnit" : { @@ -39417,29 +64924,30 @@ } }, "You share with others, we share with you. Earn a share of revenue when you provide data to others in the network." : { + "comment" : "Setup wallet", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "أنت تشارك مع الآخرين، ونحن نشارك معك. اكسب حصة من العائدات عندما تقدم البيانات للآخرين في الشبكة." + "value" : "أنت تشارك مع الآخرين، ونحن نشارك معك. اكسب حصة من الإيرادات عندما تقدم بيانات للآخرين على الشبكة." } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Vy sdílíte s ostatními, my s vámi. Získejte podíl na příjmech, když poskytnete data ostatním v síti." + "value" : "Sdílíte s ostatními, my sdílíme s vámi. Získejte podíl na výnosech, když poskytujete data ostatním v síti." } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Sie teilen mit anderen, wir teilen mit Ihnen. Verdienen Sie einen Anteil am Umsatz, wenn Sie anderen im Netzwerk Daten zur Verfügung stellen." + "value" : "Du teilst mit anderen, wir teilen mit dir. Verdiene einen Teil der Einnahmen, wenn du Daten an andere im Netzwerk weitergibst." } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Εσείς μοιράζεστε με άλλους, εμείς μοιραζόμαστε μαζί σας. Κερδίστε μερίδιο των εσόδων όταν παρέχετε δεδομένα σε άλλους στο δίκτυο." + "value" : "Μοιράζεστε με άλλους, μοιραζόμαστε μαζί σας. Κερδίστε ένα μερίδιο εσόδων όταν παρέχετε δεδομένα σε άλλους στο δίκτυο." } }, "en" : { @@ -39460,10 +64968,16 @@ "value" : "Tú compartes con otros, nosotros compartimos contigo. Gana una parte de los ingresos cuando proporcionas datos a otros en la red." } }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tú compartes con otros, nosotros compartimos contigo. Gana una parte de los ingresos cuando proporcionas datos a otros en la red." + } + }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Vous partagez avec les autres, nous partageons avec vous. Gagnez une part des revenus lorsque vous fournissez des données aux autres membres du réseau." + "value" : "Vous partagez avec les autres, nous partageons avec vous. Gagnez une part des revenus lorsque vous fournissez des données à d'autres personnes du réseau." } }, "he" : { @@ -39475,7 +64989,7 @@ "hi" : { "stringUnit" : { "state" : "translated", - "value" : "आप दूसरों के साथ शेयर करते हैं, हम आपके साथ शेयर करते हैं। नेटवर्क में दूसरों को डेटा प्रदान करने पर राजस्व का हिस्सा कमाएं।" + "value" : "आप दूसरों के साथ साझा करते हैं, हम आपके साथ साझा करते हैं। जब आप नेटवर्क में दूसरों को डेटा प्रदान करते हैं तो राजस्व का एक हिस्सा अर्जित करें।" } }, "id" : { @@ -39493,31 +65007,37 @@ "ja" : { "stringUnit" : { "state" : "translated", - "value" : "あなたが他の人とシェアすれば、私たちもあなたとシェアします。ネットワーク内で他のユーザーにデータを提供することで収益の一部を獲得できます。" + "value" : "あなたが他の人と共有すれば、私たちもあなたと共有します。ネットワーク内の他の人にデータを提供すると、収益の一部を獲得できます。" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "여러분이 다른 사람과 공유하면, 저희도 여러분과 공유합니다. 네트워크의 다른 사용자에게 데이터를 제공하여 수익을 얻으세요." + "value" : "당신이 다른 사람과 공유하면, 우리도 당신과 공유합니다. 네트워크의 다른 사람들에게 데이터를 제공하면 수익의 일부를 얻을 수 있습니다." } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Jij deelt met anderen, wij delen met jou. Verdien een deel van de opbrengstt wanneer je data levert aan het netwerk." + "value" : "Jij deelt met anderen, wij delen met jou. Verdien een deel van de opbrengsten wanneer je gegevens deelt met anderen in het netwerk." } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Ty dzielisz się z innymi, my dzielimy się z Tobą. Zarabiaj część przychodów, udostępniając dane innym w sieci." + "value" : "Ty dzielisz się z innymi, my dzielimy się z tobą. Zarabiaj część przychodów, gdy udostępniasz dane innym w sieci." + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Você compartilha com outras pessoas, nós compartilhamos com você. Ganhe uma parte da receita quando fornecer dados para outras pessoas na rede." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Você compartilha com outros, nós compartilhamos com você. Ganhe uma parte da receita ao fornecer dados para outros na rede." + "value" : "Você compartilha com outras pessoas, nós compartilhamos com você. Ganhe uma parte da receita quando fornecer dados para outras pessoas na rede." } }, "pt-PT" : { @@ -39541,7 +65061,7 @@ "th" : { "stringUnit" : { "state" : "translated", - "value" : "คุณแชร์กับผู้อื่น เราแชร์กับคุณ รับส่วนแบ่งรายได้เมื่อคุณให้ข้อมูลแก่ผู้อื่นในเครือข่าย" + "value" : "คุณแบ่งปันให้กับผู้อื่น เราก็แบ่งปันกลับให้คุณ แชร์ข้อมูลกับเครือข่ายแล้วรับส่วนแบ่งรายได้ตอบแทน" } }, "uk" : { @@ -39559,7 +65079,7 @@ "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "你与他人分享,我们与你分享。当你为网络中的其他人提供数据时,即可赚取收益分成。" + "value" : "您与他人分享,我们与您分享。当您向网络中的其他人提供数据时,即可获得收入分成。" } }, "zh-HK" : { @@ -39571,6 +65091,7 @@ } }, "You’ll unlock even faster speeds, and first dibs on new features like robust anti-censorship measures and data control." : { + "comment" : "Used in Shared/Views/UpgradeSubscriptionSheet.swift.", "localizations" : { "ar" : { "stringUnit" : { @@ -39725,6 +65246,7 @@ } }, "You're earning 2x points" : { + "comment" : "Used in Shared/Views/AccountPointsBreakdown.swift.", "localizations" : { "ar" : { "stringUnit" : { @@ -39879,6 +65401,7 @@ } }, "You're premium." : { + "comment" : "Used in Main/Account/AccountRootView.swift.", "localizations" : { "ar" : { "stringUnit" : { @@ -40032,30 +65555,198 @@ } } }, + "You're referral royalty!" : { + "comment" : "dns settings", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "أنت نجم الإحالات!" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Doporučujete po královsku!" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Du bist ein Empfehlungs-Star!" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Είστε αστέρι στις παραπομπές!" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "You're referral royalty!" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "¡Eres una estrella de los referidos!" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "¡Eres una estrella de los referidos!" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "¡Eres una estrella de los referidos!" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vous êtes une star du parrainage !" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "אתה אלוף ההפניות!" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "आप तो रेफ़रल के सुपरस्टार हैं!" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Anda bintang referensi!" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sei una star degli inviti!" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "紹介の達人ですね!" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "추천왕이시네요!" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Jij bent een verwijzingskampioen!" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Polecasz po królewsku!" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "É uma estrela das referências!" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Você é uma estrela das indicações!" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "É uma estrela das referências!" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Вы — звезда рефералов!" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Du är en referensstjärna!" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "คุณคือสุดยอดนักแนะนำ!" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ви — зірка рефералів!" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bạn là bậc thầy giới thiệu!" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "你是推荐王者!" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "你是推薦王者!" + } + } + } + }, "You've got mail" : { + "comment" : "Header for verify screen", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "لديك بريد جديد" + "value" : "لديك بريد إلكتروني" } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Dostali jste poštu" + "value" : "Máš poštu" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Sie haben Post" + "value" : "Du hast eine E-Mail" } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Έχετε μήνυμα" + "value" : "Έχετε ένα email" } }, "en" : { @@ -40067,7 +65758,7 @@ "es" : { "stringUnit" : { "state" : "translated", - "value" : "Tienes correo" + "value" : "Tienes un correo electrónico" } }, "es-419" : { @@ -40076,10 +65767,16 @@ "value" : "Tienes correo" } }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tienes un correo" + } + }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Vous avez reçu un e-mail" + "value" : "Vous avez un courriel" } }, "he" : { @@ -40091,7 +65788,7 @@ "hi" : { "stringUnit" : { "state" : "translated", - "value" : "आपको मेल मिला है" + "value" : "आपके पास एक ईमेल है" } }, "id" : { @@ -40109,19 +65806,19 @@ "ja" : { "stringUnit" : { "state" : "translated", - "value" : "メールが届きました" + "value" : "メールが届いています" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "메일이 도착했습니다" + "value" : "메일을 확인해주세요" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Je hebt post" + "value" : "Je hebt mail" } }, "pl" : { @@ -40130,10 +65827,16 @@ "value" : "Masz wiadomość" } }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Você tem um e-mail" + } + }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Você recebeu um email" + "value" : "Você tem um e-mail" } }, "pt-PT" : { @@ -40145,7 +65848,7 @@ "ru" : { "stringUnit" : { "state" : "translated", - "value" : "У вас новое письмо" + "value" : "Вам письмо" } }, "sv" : { @@ -40157,7 +65860,7 @@ "th" : { "stringUnit" : { "state" : "translated", - "value" : "คุณมีอีเมลใหม่" + "value" : "คุณได้รับอีเมลใหม่" } }, "uk" : { @@ -40175,7 +65878,7 @@ "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "你收到邮件了" + "value" : "您有邮件" } }, "zh-HK" : { @@ -40188,9 +65891,162 @@ }, "Your email" : { "comment" : "Label for the empty text field.", - "isCommentAutoGenerated" : true + "isCommentAutoGenerated" : true, + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "بريدك الإلكتروني" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Váš e-mail" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Deine E-Mail" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Το email σας" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Your email" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tu correo electrónico" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tu correo electrónico" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Votre e-mail" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "האימייל שלך" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "आपका ईमेल" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Email Anda" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "La tua email" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "メールアドレス" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "이메일" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Je e-mail" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Twój e-mail" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Seu e-mail" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "O seu e-mail" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ваш email" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Din e-post" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "อีเมลของคุณ" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ваш email" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Email của bạn" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "你的电子邮件" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "你的電郵" + } + } + } }, "Your friend gets 30 GiB/month for life" : { + "comment" : "Used in Shared/Views/Introduction/ParticipateReferView.swift.", "localizations" : { "ar" : { "stringUnit" : { @@ -40345,29 +66201,30 @@ } }, "Your friends save too -- everyone wins." : { + "comment" : "Call to action", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "أصدقاؤك يوفرون أيضًا -- الجميع يستفيد." + "value" : "أصدقاؤك يوفرون أيضًا -- الجميع يفوز." } }, "cs" : { "stringUnit" : { "state" : "translated", - "value" : "Vaši přátelé také ušetří -- všichni vyhrávají." + "value" : "Vaši přátelé také ušetří – každý vyhrává." } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Auch deine Freunde sparen – alle gewinnen." + "value" : "Deine Freunde sparen ebenfalls – alle gewinnen." } }, "el" : { "stringUnit" : { "state" : "translated", - "value" : "Οι φίλοι σου κερδίζουν επίσης -- όλοι βγαίνουν κερδισμένοι." + "value" : "Οι φίλοι σου εξοικονομούν επίσης -- όλοι κερδίζουν." } }, "en" : { @@ -40388,6 +66245,12 @@ "value" : "Tus amigos también ahorran -- todos ganan." } }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tus amigos también ahorran -- todos ganan." + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -40403,7 +66266,7 @@ "hi" : { "stringUnit" : { "state" : "translated", - "value" : "आपके मित्र भी बचत करते हैं -- सभी को लाभ होता है।" + "value" : "आपके दोस्त भी बचत करते हैं -- सभी जीतते हैं।" } }, "id" : { @@ -40421,13 +66284,13 @@ "ja" : { "stringUnit" : { "state" : "translated", - "value" : "友達も節約できる -- みんなが得する。" + "value" : "あなたの友達も節約できる -- みんなが得をします。" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "친구들도 절약해요 -- 모두가 이깁니다." + "value" : "친구들도 절약합니다 -- 모두가 이깁니다." } }, "nl" : { @@ -40442,6 +66305,12 @@ "value" : "Twoi znajomi też oszczędzają -- wszyscy wygrywają." } }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Os seus amigos também poupam -- todos ganham." + } + }, "pt-BR" : { "stringUnit" : { "state" : "translated", @@ -40498,7 +66367,163 @@ } } }, + "Your purchase needs to be approved before it can complete. UR Pro will turn on by itself once it goes through — there's no need to buy again." : { + "comment" : "Used in Shared/Views/UpgradeSubscriptionSheet.swift.", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "يحتاج شراؤك إلى موافقة قبل أن يكتمل. سيتم تفعيل UR Pro تلقائيًا بمجرد إتمام الموافقة — لا حاجة إلى الشراء مرة أخرى." + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Váš nákup musí být před dokončením schválen. UR Pro se zapne automaticky, jakmile schválení projde — není potřeba kupovat znovu." + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dein Kauf muss noch genehmigt werden, bevor er abgeschlossen werden kann. UR Pro wird automatisch aktiviert, sobald es so weit ist – du musst nichts erneut kaufen." + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Η αγορά σας πρέπει να εγκριθεί για να ολοκληρωθεί. Το UR Pro θα ενεργοποιηθεί από μόνο του μόλις περάσει — δεν χρειάζεται να αγοράσετε ξανά." + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Your purchase needs to be approved before it can complete. UR Pro will turn on by itself once it goes through — there's no need to buy again." + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tu compra necesita aprobación para completarse. UR Pro se activará solo en cuanto se apruebe — no hace falta que vuelvas a comprar." + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tu compra necesita aprobación para completarse. UR Pro se activará solo en cuanto se apruebe — no necesitas volver a comprar." + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Votre achat doit être approuvé avant de pouvoir aboutir. UR Pro s'activera tout seul une fois l'achat validé — pas besoin d'acheter à nouveau." + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "הרכישה שלך צריכה לקבל אישור לפני שתושלם. UR Pro יופעל מעצמו ברגע שהיא תאושר — אין צורך לרכוש שוב." + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "आपकी खरीद पूरी होने से पहले उसे मंज़ूरी मिलनी ज़रूरी है। मंज़ूरी मिलते ही UR Pro अपने आप चालू हो जाएगा — दोबारा खरीदने की ज़रूरत नहीं।" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pembelian Anda perlu disetujui sebelum dapat diselesaikan. UR Pro akan aktif dengan sendirinya setelah disetujui — tidak perlu membeli lagi." + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Il tuo acquisto deve essere approvato prima di poter essere completato. UR Pro si attiverà da solo una volta andato a buon fine — non serve acquistare di nuovo." + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "購入を完了するには承認が必要です。承認されるとUR Proは自動的に有効になります。もう一度購入する必要はありません。" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "구매를 완료하려면 승인이 필요합니다. 승인이 완료되면 UR Pro가 자동으로 켜지므로 다시 구매하지 않아도 됩니다." + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Je aankoop moet worden goedgekeurd voordat die kan worden afgerond. UR Pro wordt vanzelf ingeschakeld zodra dat gebeurt – je hoeft niets opnieuw te kopen." + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Twój zakup wymaga zatwierdzenia, zanim będzie mógł zostać sfinalizowany. UR Pro włączy się automatycznie, gdy tylko to nastąpi — nie musisz kupować ponownie." + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sua compra precisa ser aprovada para poder ser concluída. O UR Pro será ativado automaticamente assim que a aprovação sair — não precisa comprar de novo." + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "A sua compra precisa de ser aprovada para poder ser concluída. O UR Pro é ativado automaticamente assim que a aprovação chegar — não precisa de comprar novamente." + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Покупку нужно подтвердить, чтобы она завершилась. UR Pro включится сам, как только всё пройдёт — покупать заново не нужно." + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ditt köp måste godkännas innan det kan slutföras. UR Pro aktiveras automatiskt när det går igenom — du behöver inte köpa igen." + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "การซื้อของคุณต้องได้รับการอนุมัติก่อนจึงจะสำเร็จ เมื่ออนุมัติแล้ว UR Pro จะเปิดใช้งานเองโดยอัตโนมัติ — ไม่ต้องซื้อซ้ำ" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Покупку потрібно підтвердити, щоб вона завершилася. UR Pro увімкнеться сам, щойно все пройде — купувати повторно не потрібно." + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Giao dịch của bạn cần được phê duyệt trước khi hoàn tất. UR Pro sẽ tự bật ngay khi được duyệt — bạn không cần mua lại." + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "你的购买需要获得批准才能完成。一旦通过,UR Pro 会自动开启 — 无需重新购买。" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "你的購買需要獲得批准才能完成。一旦通過,UR Pro 會自動開啟 — 無需重新購買。" + } + } + } + }, "Your referral link" : { + "comment" : "Used in Shared/Views/Introduction/ParticipateReferView.swift.", "localizations" : { "ar" : { "stringUnit" : { @@ -40651,6 +66676,9 @@ } } } + }, + "τ" : { + "shouldTranslate" : false } }, "version" : "1.1" diff --git a/app/network/Shared/Services/UrApiService/UrApiService.swift b/app/network/Shared/Services/UrApiService/UrApiService.swift index 2d24dd52..090460c3 100644 --- a/app/network/Shared/Services/UrApiService/UrApiService.swift +++ b/app/network/Shared/Services/UrApiService/UrApiService.swift @@ -334,7 +334,7 @@ extension UrApiService { // treat as a brand new network (create flow) continuation.resume(returning: .create(args)) } else { - let errMessage = "Please login with one of: \(acceptedAuthMethods.joined(separator: ", "))." + let errMessage = String(localized: "Please login with one of: \(acceptedAuthMethods.joined(separator: ", ")).") continuation.resume(returning: .incorrectAuth(errMessage)) } diff --git a/app/network/Shared/ViewModels/AccountPreferencesViewModel.swift b/app/network/Shared/ViewModels/AccountPreferencesViewModel.swift index 56a906dc..3f2f8c95 100644 --- a/app/network/Shared/ViewModels/AccountPreferencesViewModel.swift +++ b/app/network/Shared/ViewModels/AccountPreferencesViewModel.swift @@ -153,7 +153,7 @@ class AccountPreferencesViewModel: ObservableObject { setCanReceiveProductUpdates(previousValue ?? lastSavedCanReceiveProductUpdates) isUpdatingAccountPreferences = false // the toggle silently snapped back to its saved value — tell the user why - saveErrorMessage = "Couldn't update your preferences. Please try again." + saveErrorMessage = String(localized: "Couldn't update your preferences. Please try again.") } } diff --git a/app/network/Shared/ViewModels/AppStoreSubscriptionManager.swift b/app/network/Shared/ViewModels/AppStoreSubscriptionManager.swift index d08be87f..a9aae891 100644 --- a/app/network/Shared/ViewModels/AppStoreSubscriptionManager.swift +++ b/app/network/Shared/ViewModels/AppStoreSubscriptionManager.swift @@ -21,9 +21,37 @@ class AppStoreSubscriptionManager: ObservableObject { @Published var isPurchasing: Bool = false @Published private(set) var purchaseSuccess: Bool = false + + /** + * StoreKit ACCEPTED the purchase but it is not complete: it needs approval + * (Ask to Buy) or another auth step (SCA). No transaction arrives now -- it lands + * later on `Transaction.updates`. + * + * This used to be swallowed: `case .pending` only printed, so the sheet's spinner + * simply stopped and dropped the user back on the product list with no + * explanation. They reasonably conclude the purchase failed -- and try to buy + * again. + */ + @Published private(set) var purchasePending: Bool = false private var networkId: SdkId? var onPurchaseSuccess: (() -> Void)? + + /** + * Bumped for every transaction StoreKit hands us on `Transaction.updates` — an + * Ask to Buy approval that came through later, a purchase made on another device, a + * renewal, anything that completed while the app was closed. + * + * This exists because `onPurchaseSuccess` is only assigned inside `purchase()`, while + * the transaction listener starts at `init`. So on a FRESH LAUNCH the callback is nil: + * StoreKit would deliver the transaction, we would dutifully `finish()` it, and then + * do nothing at all. The user's purchase completed and the app never noticed — no + * poll, no refresh, no Pro. + * + * A published counter cannot be nil, and the view observes it, so the wiring cannot + * be forgotten the way an optional callback can. + */ + @Published private(set) var transactionUpdateSequence: Int = 0 private var updateListenerTask: Task? @@ -71,6 +99,18 @@ class AppStoreSubscriptionManager: ObservableObject { await setIsPurchasing(true) + /** + * A new attempt starts clean. + * + * Without this, `purchaseSuccess` from an EARLIER attempt survives: the + * manager is a @StateObject on MainView, so it lives for the whole session and + * nothing ever set the flag back to false. Reopening the upgrade sheet then + * renders PurchaseSuccessView immediately -- "You're premium." -- to a user + * whose purchase never actually completed. + */ + setPurchaseSuccess(false) + setPurchasePending(false) + self.onPurchaseSuccess = onSuccess let callback = onSuccess @@ -114,8 +154,16 @@ class AppStoreSubscriptionManager: ObservableObject { throw SKError(.paymentCancelled) case .pending: + /** + * Ask to Buy (a child needing a parent's approval) or an SCA step. The + * purchase is NOT done and no transaction arrives now -- it lands later + * on Transaction.updates. Surface it, so the sheet can tell the user + * their purchase is awaiting approval instead of silently returning + * them to the product list as if nothing happened. + */ print("Purchase pending approval") - + setPurchasePending(true) + @unknown default: print("Unknown purchase result") } @@ -133,6 +181,22 @@ class AppStoreSubscriptionManager: ObservableObject { self.purchaseSuccess = success } + @MainActor + func setPurchasePending(_ pending: Bool) { + self.purchasePending = pending + } + + /** + * Clear the terminal states so the upgrade sheet opens fresh. Called when the + * sheet is dismissed: the flags describe ONE attempt, and letting them leak into + * the next presentation is what showed "You're premium." to a user who was not. + */ + @MainActor + func resetPurchaseState() { + self.purchaseSuccess = false + self.purchasePending = false + } + private func logTransactionDetails(_ transaction: Transaction) { print("Transaction ID: \(transaction.id)") print("Product ID: \(transaction.productID)") @@ -155,7 +219,14 @@ class AppStoreSubscriptionManager: ObservableObject { if transaction.revocationDate == nil { await MainActor.run { + // The in-flight purchase callback, when there IS one. self.onPurchaseSuccess?() + + // ...and the signal that always fires. On a fresh launch the + // callback above is nil (it is only set inside purchase()), so + // without this an Ask-to-Buy approval or a cross-device + // purchase would be silently swallowed. + self.transactionUpdateSequence += 1 } } diff --git a/app/network/Shared/ViewModels/ConnectWalletProviderViewModel.swift b/app/network/Shared/ViewModels/ConnectWalletProviderViewModel.swift index 39e98e45..5c41d618 100644 --- a/app/network/Shared/ViewModels/ConnectWalletProviderViewModel.swift +++ b/app/network/Shared/ViewModels/ConnectWalletProviderViewModel.swift @@ -45,6 +45,14 @@ class ConnectWalletProviderViewModel: ObservableObject { private let phantomConnectRedirectLink = "urnetwork://phantom-connect" private let phantomDisconnectRedirectLink = "urnetwork://phantom-disconnect" private let phantomSignMessageRedirectLink = "urnetwork://phantom-sign-message" + + /** + * Bittensor: signing runs through the ur.io/wallet-connect bridge + * (injected substrate wallets); the return envelope is plain query params + * (address + sr25519 signature hex) — no encryption envelope + */ + private let bittensorSignMessageRedirectLink = "urnetwork://bittensor-sign-message" + private let bittensorConnectRedirectLink = "urnetwork://bittensor-connect" init() { self.createKeyPair() @@ -270,12 +278,40 @@ class ConnectWalletProviderViewModel: ObservableObject { onSignature: ((_ signature: String) -> Void)? = nil, onError: ((_ error: Error) -> Void)? = nil ) { - + // todo - handle disconnect guard let components = URLComponents(url: url, resolvingAgainstBaseURL: false), - let queryItems = components.queryItems, - components.host == "solflare-connect" || components.host == "phantom-connect" || components.host == "phantom-sign-message" || components.host == "solflare-sign-message" else { + let queryItems = components.queryItems else { + print("no match for components.host") + return + } + + // bittensor returns plain params from the ur.io/wallet-connect bridge + if components.host == "bittensor-sign-message" || components.host == "bittensor-connect" { + if let errorMessage = queryItems.first(where: { $0.name == "errorMessage" })?.value { + onError?(WalletDeepLinkError.walletError(errorMessage)) + return + } + guard let address = queryItems.first(where: { $0.name == "address" })?.value, !address.isEmpty else { + onError?(WalletDeepLinkError.missingParams) + return + } + self.connectedPublicKey = address + self.connectedWalletProvider = .bittensor + if components.host == "bittensor-connect" { + onPublicKeyRetrieved?(address, .bittensor) + return + } + guard let signature = queryItems.first(where: { $0.name == "signature" })?.value, !signature.isEmpty else { + onError?(WalletDeepLinkError.missingParams) + return + } + onSignature?(signature) + return + } + + guard components.host == "solflare-connect" || components.host == "phantom-connect" || components.host == "phantom-sign-message" || components.host == "solflare-sign-message" else { print("no match for components.host") return } @@ -584,6 +620,39 @@ class ConnectWalletProviderViewModel: ObservableObject { } #endif + /** + * Opens the ur.io/wallet-connect bridge to sign in with a Bittensor + * wallet. The bridge drives an injected substrate wallet (extension or a + * mobile wallet's in-app browser) and returns via + * urnetwork://bittensor-sign-message?address=&signature= + */ + func openBittensorSignIn(message: String) { + var webComponents = URLComponents() + webComponents.scheme = "https" + webComponents.host = "ur.io" + webComponents.path = "/wallet-connect" + var items = [ + URLQueryItem(name: "provider", value: "bittensor"), + URLQueryItem(name: "method", value: "signMessage"), + URLQueryItem(name: "message", value: message), + URLQueryItem(name: "redirect_link", value: bittensorSignMessageRedirectLink), + ] + // the WalletConnect Cloud project id (URnetwork-Info.plist) lets the + // bridge pair with a wallet app; without it the bridge uses injected + // wallets only + if let projectId = Bundle.main.object(forInfoDictionaryKey: "URWalletConnectProjectId") as? String, + !projectId.isEmpty { + items.append(URLQueryItem(name: "wc_project_id", value: projectId)) + } + webComponents.queryItems = items + guard let url = webComponents.url else { return } + #if canImport(UIKit) + UIApplication.shared.open(url) + #elseif canImport(AppKit) + NSWorkspace.shared.open(url) + #endif + } + @discardableResult func openURL(_ url: URL, completion: ((Bool) -> Void)? = nil) -> Bool { #if canImport(UIKit) @@ -668,11 +737,13 @@ enum WalletDeepLinkError: Error { case failedGeneratingNonce case missingParams case invalidParameters + case walletError(String) } enum ConnectedWalletProvider { case solflare case phantom + case bittensor } private struct SignMessagePayload: Encodable { diff --git a/app/network/Shared/ViewModels/DeviceManager.swift b/app/network/Shared/ViewModels/DeviceManager.swift index a1fa1cc4..574632dc 100644 --- a/app/network/Shared/ViewModels/DeviceManager.swift +++ b/app/network/Shared/ViewModels/DeviceManager.swift @@ -74,6 +74,26 @@ class DeviceManager: ObservableObject { device?.setRouteLocal(value) } + + @Published var blockerEnabled: Bool = false { + didSet { + guard !isLoadingFromDevice else { return } + setBlockerEnabledInternal(blockerEnabled) + } + } + + // the extension device persists the toggle to local settings and restores + // it at creation; the app-side local state mirror (shared storage) keeps + // the toggle seeded when the extension device is not reachable + private func setBlockerEnabledInternal(_ value: Bool) { + do { + try asyncLocalState?.getLocalState()?.setBlockerEnabled(value) + } catch { + print("error setting blocker enabled: \(error)") + } + + device?.setBlockerEnabled(value) + } @Published var allowProvidingCell: Bool = false { didSet { @@ -189,6 +209,7 @@ class DeviceManager: ObservableObject { private var deviceProvideSub: SdkSubProtocol? private var deviceProvidePausedSub: SdkSubProtocol? private var deviceJwtRefreshSub: SdkSubProtocol? + private var deviceAuthLogoutSub: SdkSubProtocol? private var deviceCanShowRatingDialogSub: SdkSubProtocol? private var deviceCanPromptIntroFunnelSub: SdkSubProtocol? private var deviceAllowForegroundSub: SdkSubProtocol? @@ -197,6 +218,7 @@ class DeviceManager: ObservableObject { private var deviceProvideNetworkModeSub: SdkSubProtocol? private var deviceVpnInterfaceWhileOfflineSub: SdkSubProtocol? private var deviceDefaultLocationSub: SdkSubProtocol? + private var deviceBlockerEnabledSub: SdkSubProtocol? private func updateAllowProvidingCell(_ allow: Bool) { #if os(iOS) @@ -662,6 +684,7 @@ extension DeviceManager { } let routeLocal = localState.getRouteLocal() + let blockerEnabled = localState.getBlockerEnabled() let connectLocation = localState.getConnectLocation() let defaultLocation = localState.getDefaultLocation() let canShowRatingDialog = localState.getCanShowRatingDialog() @@ -738,6 +761,10 @@ extension DeviceManager { device.setProvideNetworkMode(provideNetworkMode?.rawValue ?? ProvideNetworkMode.WiFi.rawValue) device.setCanRefer(canRefer) device.setVpnInterfaceWhileOffline(vpnInterfaceWhileOffline) + device.setBlockerEnabled(blockerEnabled) + isLoadingFromDevice = true + self.blockerEnabled = blockerEnabled + isLoadingFromDevice = false if (performanceProfile != nil) { device.setPerformanceProfile(performanceProfile) @@ -803,17 +830,35 @@ extension DeviceManager { }) self.deviceJwtRefreshSub = device.add(JwtRefreshListener { [weak self] _ in - + print("JwtRefreshListener hit") - + guard let self = self else { return } - + DispatchQueue.main.async { self.updateParsedJwt() } - + + }) + + // the sdk fires this when the jwt refresh finds the client no longer + // exists on the server (e.g. the client was removed): the sdk has + // already cleared its local auth state; log the user out so the ui + // returns to the login flow + self.deviceAuthLogoutSub = device.add(AuthLogoutListener { [weak self] in + + print("AuthLogoutListener hit") + + guard let self = self else { + return + } + + DispatchQueue.main.async { + self.logout() + } + }) self.deviceCanShowRatingDialogSub = device.add(CanShowRatingDialogChangeListener { [weak self] canShowRatingDialog in @@ -828,6 +873,20 @@ extension DeviceManager { try? self?.asyncLocalState?.getLocalState()?.setAllowForeground(allowForeground) }) + self.deviceBlockerEnabledSub = device.add(BlockerEnabledChangeListener { [weak self] blockerEnabled in + guard let self = self else { + return + } + + DispatchQueue.main.async { + if self.blockerEnabled != blockerEnabled { + self.isLoadingFromDevice = true + self.blockerEnabled = blockerEnabled + self.isLoadingFromDevice = false + } + } + }) + self.deviceCanReferSub = device.add(CanReferChangeListener { [weak self] canRefer in try? self?.asyncLocalState?.getLocalState()?.setCanRefer(canRefer) }) @@ -865,6 +924,9 @@ extension DeviceManager { deviceJwtRefreshSub?.close() deviceJwtRefreshSub = nil + deviceAuthLogoutSub?.close() + deviceAuthLogoutSub = nil + deviceCanShowRatingDialogSub?.close() deviceCanShowRatingDialogSub = nil @@ -888,6 +950,9 @@ extension DeviceManager { deviceDefaultLocationSub?.close() deviceDefaultLocationSub = nil + + deviceBlockerEnabledSub?.close() + deviceBlockerEnabledSub = nil } } @@ -1179,18 +1244,31 @@ private class ProvideChangeListener: NSObject, SdkProvideChangeListenerProtocol } private class ProvidePausedChangeListener: NSObject, SdkProvidePausedChangeListenerProtocol { - + private let c: (_ providePaused: Bool) -> Void init(c: @escaping (_ providePaused: Bool) -> Void) { self.c = c } - + func providePausedChanged(_ providePaused: Bool) { c(providePaused) } } +private class BlockerEnabledChangeListener: NSObject, SdkBlockerEnabledChangeListenerProtocol { + + private let c: (_ blockerEnabled: Bool) -> Void + + init(c: @escaping (_ blockerEnabled: Bool) -> Void) { + self.c = c + } + + func blockerEnabledChanged(_ blockerEnabled: Bool) { + c(blockerEnabled) + } +} + private class CanShowRatingDialogChangeListener: NSObject, SdkCanShowRatingDialogChangeListenerProtocol { private let c: (_ canShowRatingDialog: Bool) -> Void @@ -1296,14 +1374,27 @@ private class DefaultLocationChangeListener: NSObject, SdkDefaultLocationChangeL } private class JwtRefreshListener: NSObject, SdkJwtRefreshListenerProtocol { - + private let c: (_ jwt: String?) -> Void init(c: @escaping (_ jwt: String?) -> Void) { self.c = c } - + func jwtRefreshed(_ jwt: String?) { c(jwt) } } + +private class AuthLogoutListener: NSObject, SdkAuthLogoutListenerProtocol { + + private let c: () -> Void + + init(c: @escaping () -> Void) { + self.c = c + } + + func authLogout() { + c() + } +} diff --git a/app/network/Shared/ViewModels/SubscriptionBalanceViewModel.swift b/app/network/Shared/ViewModels/SubscriptionBalanceViewModel.swift index da0be6a0..51ec5f72 100644 --- a/app/network/Shared/ViewModels/SubscriptionBalanceViewModel.swift +++ b/app/network/Shared/ViewModels/SubscriptionBalanceViewModel.swift @@ -28,6 +28,29 @@ class SubscriptionBalanceViewModel: ObservableObject { @Published private(set) var isPolling: Bool = false private var pollingTimer: Timer? private var pollingInterval: TimeInterval = 5.0 // Default 5 seconds + + /** + * The upgrade-confirmation poll has a DEADLINE. + * + * A purchase reaches our server asynchronously (the App Store notifies it by + * webhook), so right after StoreKit reports success the server does not know yet. + * We poll to bridge that gap -- that part works. + * + * But a webhook can be lost or badly delayed, and the poll only ever stopped on + * SUCCESS. So in that case it ran every 5 seconds for the rest of the session, + * behind a spinner, with no way for the user to learn anything had gone wrong. + * They paid, and the app just span. Give up after `maxPollingDuration` and say so. + */ + private var pollingDeadline: Date? + private let maxPollingDuration: TimeInterval = 120.0 // 2 minutes + + /** + * True when the confirmation poll gave up without the server ever confirming Pro. + * StoreKit took the money but we could not verify the entitlement, so the UI must + * SAY that -- it is the difference between "we're still working on it" and an + * endless spinner. + */ + @Published private(set) var purchaseConfirmationTimedOut: Bool = false // this is used primarily for the data usage bar private var backgroundPollingTimer: Timer? @@ -107,25 +130,24 @@ class SubscriptionBalanceViewModel: ObservableObject { self.usedBalanceByteCount = Int(result.startBalanceByteCount) - self.availableByteCount - self.pendingByteCount self.startBalanceByteCount = Int(result.startBalanceByteCount) - if let currentSubscription = result.currentSubscription { - - if let validPlan = Plan(rawValue: currentSubscription.plan.lowercased()) { - - print("current plan is: \(validPlan)") - - - if validPlan == .supporter && !self.isPro { - // free -> paid: signal the upgrade so provide mode resets to never once - self.didDetectUpgradeToPro = true - } + // The server is the source of truth for Pro, and `currentSubscription` is + // non-nil exactly when the network is Pro. The jwt's `pro` claim is baked + // in when the token is issued, so it goes stale on BOTH an upgrade and a + // lapse. Refresh the jwt whenever the two disagree, in either direction. + // + // The downgrade case used to live inside `if let currentSubscription`, + // which is nil precisely when the user is no longer pro -- so it could + // never run. A lapsed subscriber kept showing "Supporter", kept Pro + // behavior, and kept the upgrade CTA hidden until the app was relaunched. + let serverIsPro = result.currentSubscription != nil - if validPlan == .supporter && !self.isPro - || validPlan == .none && self.isPro - { - refreshJwt() - } - - } + if serverIsPro && !self.isPro { + // free -> paid: signal the upgrade so provide mode resets to never once + self.didDetectUpgradeToPro = true + } + + if serverIsPro != self.isPro { + refreshJwt() } self.isLoading = false @@ -181,37 +203,69 @@ class SubscriptionBalanceViewModel: ObservableObject { backgroundPollingTimer?.invalidate() backgroundPollingTimer = nil + // a fresh confirmation attempt: clear any previous give-up, and arm the deadline + self.purchaseConfirmationTimedOut = false + self.pollingDeadline = Date().addingTimeInterval(maxPollingDuration) + self.setPollingInterval(interval) self.setIsPolling(true) Task { - + await fetchSubscriptionBalance() - + if (self.isSupporterWithBalance()) { stopPolling() return } - + // Set up timer for subsequent fetches DispatchQueue.main.async { [weak self] in guard let self = self else { return } - + self.pollingTimer = Timer.scheduledTimer(withTimeInterval: interval, repeats: true) { [weak self] _ in guard let self = self else { return } - + Task { await self.fetchSubscriptionBalance() - + if (await self.isSupporterWithBalance()) { await self.stopPolling() + return + } + + // the server never confirmed within the window -- stop + // hammering the api and tell the user, rather than spinning + // for the rest of the session + if (await self.isPollingDeadlineExpired()) { + await self.timeOutPurchaseConfirmation() } - } } } } } + + private func isPollingDeadlineExpired() -> Bool { + guard let pollingDeadline = self.pollingDeadline else { return false } + return Date() >= pollingDeadline + } + + /** + * Give up waiting for the server to confirm the purchase. The money was taken by + * StoreKit; we simply could not verify the entitlement in time (a lost or slow + * App Store webhook). Stop polling and raise the flag so the UI can show a real + * message -- the purchase is still likely to land, and the background poll and the + * next app launch will pick it up. + */ + private func timeOutPurchaseConfirmation() { + stopPolling() + purchaseConfirmationTimedOut = true + } + + func clearPurchaseConfirmationTimeout() { + purchaseConfirmationTimedOut = false + } func isSupporterWithBalance() -> Bool { print("is supporter with balance? pro=\(isPro) availableByteCount=\(self.availableByteCount)") @@ -221,6 +275,7 @@ class SubscriptionBalanceViewModel: ObservableObject { func stopPolling() { pollingTimer?.invalidate() pollingTimer = nil + pollingDeadline = nil isPolling = false backgroundPollingTimer?.invalidate() backgroundPollingTimer = nil diff --git a/app/network/Shared/Views/AccountMenu.swift b/app/network/Shared/Views/AccountMenu.swift index 2e687e4e..d930f1e6 100644 --- a/app/network/Shared/Views/AccountMenu.swift +++ b/app/network/Shared/Views/AccountMenu.swift @@ -8,9 +8,16 @@ import SwiftUI import URnetworkSdk +/// Pro gold. Reserved for the Pro entitlement across the product — this ring, the +/// avatar ring on Android, the network-name button on ur.io. Because it is used for +/// nothing else, gold reads as "this account is Pro" rather than as decoration. +private let proGold = Color(red: 1.0, green: 0.77, blue: 0.0) +private let proGoldLight = Color(red: 1.0, green: 0.88, blue: 0.51) + struct AccountMenu: View { var isGuest: Bool + var isPro: Bool = false var logout: () -> Void var networkName: String? @Binding var isPresentedCreateAccount: Bool @@ -56,6 +63,32 @@ struct AccountMenu: View { .resizable() .frame(width: 32, height: 32) .clipShape(Circle()) + /** + * Pro members get a glowing gold ring around their avatar. + * + * The ring is an angular gradient so the light appears to travel around + * it rather than sitting flat, and the glow is two stacked shadows — + * a tight bright one and a wide soft one — which reads as light coming + * off the ring rather than a drop shadow under it. + */ + .overlay( + Group { + if isPro { + Circle() + .strokeBorder( + AngularGradient( + gradient: Gradient(colors: [ + proGoldLight, proGold, proGoldLight, proGold, proGoldLight, + ]), + center: .center + ), + lineWidth: 2 + ) + .shadow(color: proGold.opacity(0.6), radius: 5) + .shadow(color: proGold.opacity(0.3), radius: 11) + } + } + ) } .menuStyle(.borderlessButton) diff --git a/app/network/Shared/Views/Introduction/IntroductionUsageBar.swift b/app/network/Shared/Views/Introduction/IntroductionUsageBar.swift index a06d2464..dcc27b21 100644 --- a/app/network/Shared/Views/Introduction/IntroductionUsageBar.swift +++ b/app/network/Shared/Views/Introduction/IntroductionUsageBar.swift @@ -43,7 +43,8 @@ struct IntroductionUsageBar: View { usedByteCount: subscriptionBalanceViewModel.usedBalanceByteCount, meanReliabilityWeight: meanReliabilityWeight, totalReferrals: totalReferrals, - dailyBalanceByteCount: subscriptionBalanceViewModel.startBalanceByteCount + dailyBalanceByteCount: subscriptionBalanceViewModel.startBalanceByteCount, + referralCode: referralCode ) Spacer().frame(height: 32) diff --git a/app/network/Shared/Views/Introduction/IntroductionView.swift b/app/network/Shared/Views/Introduction/IntroductionView.swift index cbff81f1..6dea9a6d 100644 --- a/app/network/Shared/Views/Introduction/IntroductionView.swift +++ b/app/network/Shared/Views/Introduction/IntroductionView.swift @@ -232,8 +232,8 @@ struct IntroductionView: View { HStack(alignment: .center) { Text(""" - URnetwork is powered by a patented protocol - that keeps everyone safe and secure. + URnetwork is powered by the world's largest, safest, + and most decentralized ingress and egress protocol. """) .font(Font.custom("PP NeueBit", size: 22).weight(.bold)) .multilineTextAlignment(.center) diff --git a/app/network/Shared/Views/Introduction/ParticipateReferView.swift b/app/network/Shared/Views/Introduction/ParticipateReferView.swift index 0893c1ad..048feace 100644 --- a/app/network/Shared/Views/Introduction/ParticipateReferView.swift +++ b/app/network/Shared/Views/Introduction/ParticipateReferView.swift @@ -85,7 +85,7 @@ struct ParticipateReferView: View { copyToPasteboard(referralCode) // snackbar not showing above fullScreenCover - snackbarManager.showSnackbar(message: "Bonus referral code copied to clipboard") + snackbarManager.showSnackbar(message: String(localized: "Bonus referral code copied to clipboard")) }) { HStack { @@ -117,9 +117,8 @@ struct ParticipateReferView: View { Spacer().frame(height: 16) ShareLink( - item: URL(string: "https://ur.io/app?bonus=\(referralCode)")!, - subject: Text("URnetwork Referral Code"), - message: Text("All the content in the world from URnetwork")) + item: String(localized: "Join me on URnetwork! Get the app and enter referral code \(referralCode) when you sign up."), + subject: Text("URnetwork Referral Code")) { Text("Refer a friend") .font(themeManager.currentTheme.toolbarTitleFont.bold()) diff --git a/app/network/Shared/Views/RedeemBalanceCodeSheet/RedeemBalanceCodeSheet.swift b/app/network/Shared/Views/RedeemBalanceCodeSheet/RedeemBalanceCodeSheet.swift index 78eafb91..a29b90ef 100644 --- a/app/network/Shared/Views/RedeemBalanceCodeSheet/RedeemBalanceCodeSheet.swift +++ b/app/network/Shared/Views/RedeemBalanceCodeSheet/RedeemBalanceCodeSheet.swift @@ -50,7 +50,25 @@ struct RedeemBalanceCodeSheet: View { } } ) - + + /** + * Someone opening this sheet with no code needs to know where to get + * one. Without this the sheet asks for a code and offers no way to + * obtain one — a dead end. + * + * Deliberately PLAIN TEXT: no link, no button. App Store rules forbid + * steering a user to an external purchase, but nothing stops us saying + * where codes come from. + */ + Text("Don't have a code? Data codes can be purchased at ur.io and emailed to you.") + .font(themeManager.currentTheme.secondaryBodyFont) + .foregroundColor(themeManager.currentTheme.textMutedColor) + .fixedSize(horizontal: false, vertical: true) + .frame(maxWidth: .infinity, alignment: .leading) + .padding(.top, 12) + + Spacer() + } .padding() .toolbar { diff --git a/app/network/Shared/Views/ReferralShareLink/ReferralShareLink.swift b/app/network/Shared/Views/ReferralShareLink/ReferralShareLink.swift index 53273642..7e04e881 100644 --- a/app/network/Shared/Views/ReferralShareLink/ReferralShareLink.swift +++ b/app/network/Shared/Views/ReferralShareLink/ReferralShareLink.swift @@ -21,16 +21,16 @@ struct ReferralShareLink: View { } var body: some View { - // share a clean app link until the referral code loads (the poller keeps - // retrying), instead of permanently disabling sharing on a failed/slow - // fetch or appending an empty `bonus=` - let url: URL = { + // referrals no longer use deep links; share the code and friends enter + // it when they sign up. share a generic invite until the code loads + // (the poller keeps retrying), instead of permanently disabling sharing + let message: String = { if let code = referralLinkViewModel.referralCode, !code.isEmpty { - return URL(string: "https://ur.io/app?bonus=\(code)") ?? URL(string: "https://ur.io/app")! + return String(localized: "Join me on URnetwork! Get the app and enter referral code \(code) when you sign up.") } - return URL(string: "https://ur.io/app")! + return String(localized: "Join me on URnetwork! Get the app and enter my referral code when you sign up.") }() - ShareLink(item: url, subject: Text("URnetwork Referral Code"), message: Text("All the content in the world from URnetwork")) { + ShareLink(item: message, subject: Text("URnetwork Referral Code")) { content() } .disabled(referralLinkViewModel.isLoading) diff --git a/app/network/Shared/Views/SolanaSignMessageSheet.swift b/app/network/Shared/Views/SolanaSignMessageSheet.swift index 6108badf..93a32ab5 100644 --- a/app/network/Shared/Views/SolanaSignMessageSheet.swift +++ b/app/network/Shared/Views/SolanaSignMessageSheet.swift @@ -166,6 +166,11 @@ struct SolanaSignMessageSheet: View { setIsSigningMessage(false) } ) + case .bittensor: + // bittensor signs through the ur.io/wallet-connect + // bridge, not this solana sheet + setIsSigningMessage(false) + return } if !didStartSigning { @@ -187,7 +192,7 @@ struct SolanaSignMessageSheet: View { } private func showWalletOpenFailed() { - snackbarManager.showSnackbar(message: "Couldn't open wallet. Please install it and try again.") + snackbarManager.showSnackbar(message: String(localized: "Couldn't open wallet. Please install it and try again.")) } } diff --git a/app/network/Shared/Views/Stats/ContractDetailsView.swift b/app/network/Shared/Views/Stats/ContractDetailsView.swift index 206d131c..5851e784 100644 --- a/app/network/Shared/Views/Stats/ContractDetailsView.swift +++ b/app/network/Shared/Views/Stats/ContractDetailsView.swift @@ -131,6 +131,7 @@ struct ContractDetailsView: View { HStack(spacing: 4) { Image(systemName: "arrow.up") .font(.system(size: 10, weight: .semibold)) + // real plural rules live in Localizable.xcstrings ("%lld new") Text("\(pending) new") .font(.system(size: 12, weight: .medium).monospacedDigit()) } @@ -223,6 +224,7 @@ struct ContractClientRowView: View { } if 1 < row.pairCount { + // real plural rules live in Localizable.xcstrings ("%lld contracts") Text("\(row.pairCount) contracts") .font(themeManager.currentTheme.secondaryBodyFont) .foregroundColor(themeManager.currentTheme.textMutedColor) @@ -251,7 +253,7 @@ struct ContractClientRowView: View { pasteboard.clearContents() pasteboard.setString(row.clientId, forType: .string) #endif - snackbarManager.showSnackbar(message: "Client ID copied") + snackbarManager.showSnackbar(message: String(localized: "Client ID copied")) } } diff --git a/app/network/Shared/Views/Stats/SplitRulesView.swift b/app/network/Shared/Views/Stats/SplitRulesView.swift index 2ebebbba..55923499 100644 --- a/app/network/Shared/Views/Stats/SplitRulesView.swift +++ b/app/network/Shared/Views/Stats/SplitRulesView.swift @@ -186,6 +186,7 @@ struct SplitRulesView: View { HStack(spacing: 4) { Image(systemName: "arrow.up") .font(.system(size: 10, weight: .semibold)) + // real plural rules live in Localizable.xcstrings ("%lld new") Text("\(pending) new") .font(.system(size: 12, weight: .medium).monospacedDigit()) } @@ -315,7 +316,8 @@ struct SplitRuleRowView: View { .foregroundColor(themeManager.currentTheme.textColor) .fixedSize(horizontal: false, vertical: true) - Text("\(rule.hosts.count) host\(rule.hosts.count == 1 ? "" : "s")") + // real plural rules live in Localizable.xcstrings ("%lld hosts") + Text("\(rule.hosts.count) hosts") .font(themeManager.currentTheme.secondaryBodyFont) .foregroundColor(themeManager.currentTheme.textFaintColor) } diff --git a/app/network/Shared/Views/UpgradeSubscriptionSheet.swift b/app/network/Shared/Views/UpgradeSubscriptionSheet.swift index c06e1dde..b94b0f3b 100644 --- a/app/network/Shared/Views/UpgradeSubscriptionSheet.swift +++ b/app/network/Shared/Views/UpgradeSubscriptionSheet.swift @@ -17,22 +17,64 @@ struct UpgradeSubscriptionSheet: View { var purchase: (Product) -> Void var isPurchasing: Bool var purchaseSuccess: Bool + /** + * StoreKit accepted the purchase but it is awaiting approval (Ask to Buy) or + * another auth step. It is NOT complete and no transaction arrives now. + */ + var purchasePending: Bool = false var dismiss: () -> Void - + @State var selectedPaymentOption: PaymentOption = .yearly - + var body: some View { - + ZStack { - + if (purchaseSuccess) { - + PurchaseSuccessView( dismiss: dismiss ) .transition(.opacity) .frame(maxWidth: .infinity) - + + } else if (purchasePending) { + + /** + * Ask to Buy / SCA. The purchase is not done, and the transaction lands + * later on Transaction.updates -- so there is nothing to wait for here. + * + * This state used to be swallowed entirely: the spinner just stopped and + * the user was returned to the product list as if nothing had happened. + * The natural conclusion is that it failed, so they buy again. + */ + VStack(spacing: 12) { + + Spacer() + + Image(systemName: "clock") + .font(.system(size: 40)) + .foregroundColor(themeManager.currentTheme.textMutedColor) + + Text("Waiting for approval") + .font(themeManager.currentTheme.titleCondensedFont) + .foregroundColor(themeManager.currentTheme.textColor) + + Text("Your purchase needs to be approved before it can complete. UR Pro will turn on by itself once it goes through — there's no need to buy again.") + .font(themeManager.currentTheme.bodyFont) + .foregroundColor(themeManager.currentTheme.textMutedColor) + .multilineTextAlignment(.center) + .padding(.horizontal, 24) + + Spacer() + + UrButton(text: "Got it", action: dismiss) + .padding(.horizontal, 16) + .padding(.bottom, 16) + } + .transition(.opacity) + .frame(maxWidth: .infinity) + } else { VStack { diff --git a/app/network/Shared/Views/UsageBar.swift b/app/network/Shared/Views/UsageBar.swift index 4abbc9c5..14f14ab0 100644 --- a/app/network/Shared/Views/UsageBar.swift +++ b/app/network/Shared/Views/UsageBar.swift @@ -26,14 +26,17 @@ struct UsageBar: View { let totalReferrals: Int let cappedReliabilityData: Double let dailyBalanceByteCount: Int - + // when set, the referral row is a share link that opens the referral flow + let referralCode: String? + init( availableByteCount: Int, pendingByteCount: Int, usedByteCount: Int, meanReliabilityWeight: Double, totalReferrals: Int, - dailyBalanceByteCount: Int + dailyBalanceByteCount: Int, + referralCode: String? = nil ) { self.data = [ .init(name: "Used", bytes: usedByteCount), @@ -47,6 +50,7 @@ struct UsageBar: View { cappedReliabilityData = min(meanReliabilityWeight * 100, 100) self.dailyBalanceByteCount = dailyBalanceByteCount + self.referralCode = referralCode } func minNonZeroValue(_ bytes: Int) -> Int { @@ -147,25 +151,60 @@ struct UsageBar: View { Spacer().frame(height: 8) - HStack { - - Text("Total Referrals: \(totalReferrals)") - .font(themeManager.currentTheme.secondaryBodyFont) - .foregroundStyle(themeManager.currentTheme.textMutedColor) - - Spacer() - - Text("+\(totalReferrals * 30) GiB/Month") - .font(themeManager.currentTheme.secondaryBodyFont) + /** + * referrals. tapping shares the referral link, to make it easy to + * refer people from anywhere the bar is shown + */ + if let referralCode = referralCode { + ShareLink( + item: referralShareMessage(referralCode), + subject: Text("URnetwork Referral Code") + ) { + referralRow(showsShareIcon: true) + } + .buttonStyle(.plain) + .contentShape(Rectangle()) + } else { + referralRow(showsShareIcon: false) + } + + } + + } + + private func referralShareMessage(_ code: String) -> String { + // referrals no longer use deep links; friends enter the code on sign up. + // share a generic invite until the code loads (see ReferralShareLink) + if !code.isEmpty { + return String(localized: "Join me on URnetwork! Get the app and enter referral code \(code) when you sign up.") + } + return String(localized: "Join me on URnetwork! Get the app and enter my referral code when you sign up.") + } + + private func referralRow(showsShareIcon: Bool) -> some View { + HStack { + + // real plural rules live in Localizable.xcstrings + // ("Total referrals: %lld") + Text("Total referrals: \(totalReferrals)") + .font(themeManager.currentTheme.secondaryBodyFont) + .foregroundStyle(themeManager.currentTheme.textMutedColor) + + Spacer() + + Text("+\(totalReferrals * 30) GiB/Month") + .font(themeManager.currentTheme.secondaryBodyFont) + .foregroundStyle(themeManager.currentTheme.textMutedColor) + + if showsShareIcon { + Image(systemName: "square.and.arrow.up") + .font(.system(size: 12, weight: .medium)) .foregroundStyle(themeManager.currentTheme.textMutedColor) - - } - + } - } - + } #Preview { diff --git a/app/network/Shared/Views/WalletIcon.swift b/app/network/Shared/Views/WalletIcon.swift index 2b8c4ece..324e6780 100644 --- a/app/network/Shared/Views/WalletIcon.swift +++ b/app/network/Shared/Views/WalletIcon.swift @@ -11,49 +11,60 @@ struct WalletIcon: View { var blockchain: String var backgroundGradient: Gradient { - + if blockchain == "SOL" { return Gradient(colors: [Color(hex: "#9945FF"), Color(hex: "#14F195")]) } - + + if blockchain == "TAO" { + return Gradient(colors: [Color(hex: "#1C1C1C"), Color(hex: "#3A3A3A")]) + } + // otherwise, POLY return Gradient(colors: [Color(hex: "#8A46FF"), Color(hex: "#6E38CC")]) } - + var logoPath: String { - + if blockchain == "SOL" { return "solana.logo" } - + // otherwise, POLY return "polygon.logo" - + } - + var logoWidth: CGFloat { - + if blockchain == "SOL" { return 24 } - + // otherwise, POLY return 48 - + } - + var body: some View { - + ZStack { LinearGradient(gradient: backgroundGradient, startPoint: .top, endPoint: .bottom) .clipShape( Circle() ) - - Image(logoPath) - .resizable() - .scaledToFit() - .frame(width: logoWidth, height: logoWidth) + + if blockchain == "TAO" { + // the bittensor tau mark + Text("τ") + .font(.system(size: 24, weight: .bold)) + .foregroundColor(.white) + } else { + Image(logoPath) + .resizable() + .scaledToFit() + .frame(width: logoWidth, height: logoWidth) + } } .frame(width: 48, height: 48) diff --git a/app/network/mac0S/Navigation/MainNavigationSplitView.swift b/app/network/mac0S/Navigation/MainNavigationSplitView.swift index 1ef62f78..f02f20c1 100644 --- a/app/network/mac0S/Navigation/MainNavigationSplitView.swift +++ b/app/network/mac0S/Navigation/MainNavigationSplitView.swift @@ -164,6 +164,7 @@ struct MainNavigationSplitView: View { promptMoreDataFlow: { displayIntroduction = true }, meanReliabilityWeight: networkReliabilityStore.reliabilityWindow?.meanReliabilityWeight ?? 0, totalReferrals: referralLinkViewModel.totalReferrals, + referralCode: referralLinkViewModel.referralCode, isPro: isPro ) case .account: From a2c5474878b6b7d34f36836ec4a54081fef5bbe4 Mon Sep 17 00:00:00 2001 From: Ryanmello07 <67509637+Ryanmello07@users.noreply.github.com> Date: Sun, 12 Jul 2026 14:50:57 -0700 Subject: [PATCH 023/109] feat(ios): use server-issued challenges for Bittensor sign-in, matching Solana Bittensor sign-in was still signing a static hardcoded message ('Welcome to URnetwork') via the ur.io/wallet-connect bridge - the same replay-risk this project fixed for Solana. Give it the identical server-issued-challenge flow: - prepareBittensorChallenge() mirrors prepareSolanaChallenge(), fetching a fresh challenge via the same AuthWalletChallenge SDK API with blockchain="bittensor". - handleBittensorSignIn() now fetches the challenge before opening the wallet-connect bridge, instead of using the static welcomeMessage. - handleBittensorWalletResult() gained the same isSigningForCreateNetwork gate handleSolanaWalletResult() already had, so a fresh challenge gets signed before creating a network from a wallet login too. - Fixed a real pre-existing bug in the .create branch of handleAuthLoginResult(): it unconditionally re-fetched a Solana challenge and re-presented the Solana sheet regardless of which wallet the user actually authenticated with, so a Bittensor login landing in .create would have incorrectly tried to re-sign via Solana. It now branches on connectWalletProviderViewModel.connectedWalletProvider. CAVEAT: the exact blockchain identifier string the backend's /auth/wallet-challenge endpoint expects for Bittensor is unverified from this client-side change alone - "bittensor" was chosen by symmetry with the existing "solana" value sent for that wallet. This needs to be confirmed against the live server (or with the backend team) before this is considered production-ready; if the identifier is wrong, the challenge fetch will fail closed (clear network error, sign-in blocked) rather than silently falling back to an insecure client-side message. --- .../LoginInitial/LoginInitialView.swift | 56 +++++++++++++++---- .../LoginInitial/LoginInitialViewModel.swift | 24 ++++++++ 2 files changed, 70 insertions(+), 10 deletions(-) diff --git a/app/network/Authenticate/LoginInitial/LoginInitialView.swift b/app/network/Authenticate/LoginInitial/LoginInitialView.swift index 4bdd422b..a0665e1f 100644 --- a/app/network/Authenticate/LoginInitial/LoginInitialView.swift +++ b/app/network/Authenticate/LoginInitial/LoginInitialView.swift @@ -434,13 +434,27 @@ struct LoginInitialView: View { // the wallet challenge behind authLoginArgs was already // consumed by the /auth/login call that just returned this // .create case — fetch and sign a brand-new one before - // creating the network + // creating the network. Which wallet flow to re-arm depends + // on which one the user actually signed in with, not just + // "any wallet" - bittensor and solana each need their own + // challenge fetch and reopen the right sign-in surface. viewModel.isSigningForCreateNetwork = true - let ok = await viewModel.prepareSolanaChallenge() - if ok { - viewModel.setPresentSigninWithSolanaSheet(true) + if connectWalletProviderViewModel.connectedWalletProvider == .bittensor { + let ok = await viewModel.prepareBittensorChallenge() + if ok { + connectWalletProviderViewModel.openBittensorSignIn( + message: viewModel.bittensorChallengeMessage ?? "" + ) + } else { + viewModel.isSigningForCreateNetwork = false + } } else { - viewModel.isSigningForCreateNetwork = false + let ok = await viewModel.prepareSolanaChallenge() + if ok { + viewModel.setPresentSigninWithSolanaSheet(true) + } else { + viewModel.isSigningForCreateNetwork = false + } } } else { navigate(.createNetwork(authLoginArgs)) @@ -482,17 +496,39 @@ struct LoginInitialView: View { } /** - * Bittensor sign in: opens the ur.io/wallet-connect bridge; the wallet - * returns through onOpenURL with the ss58 address and sr25519 signature + * Bittensor sign in: fetches a fresh server-issued challenge (same + * flow as Solana), then opens the ur.io/wallet-connect bridge; the + * wallet returns through onOpenURL with the ss58 address and sr25519 + * signature over that challenge. */ private func handleBittensorSignIn() { - connectWalletProviderViewModel.openBittensorSignIn( - message: connectWalletProviderViewModel.welcomeMessage - ) + Task { + let ok = await viewModel.prepareBittensorChallenge() + if ok { + connectWalletProviderViewModel.openBittensorSignIn( + message: viewModel.bittensorChallengeMessage ?? "" + ) + } + } } private func handleBittensorWalletResult(message: String, signature: String, publicKey: String) async { + if viewModel.isSigningForCreateNetwork { + viewModel.isSigningForCreateNetwork = false + viewModel.setIsSigningMessage(false) + + let createArgsResult = viewModel.createBittensorAuthLoginArgs(message: message, signature: signature, publicKey: publicKey) + switch createArgsResult { + case .success(let args): + navigate(.createNetwork(args)) + case .failure(let error): + print("error create args result: \(error.localizedDescription)") + viewModel.setLoginErrorMessage("There was an error logging in") + } + return + } + guard viewModel.beginLoginAction(.bittensor) else { return } diff --git a/app/network/Authenticate/LoginInitial/LoginInitialViewModel.swift b/app/network/Authenticate/LoginInitial/LoginInitialViewModel.swift index 29dd2469..89be0990 100644 --- a/app/network/Authenticate/LoginInitial/LoginInitialViewModel.swift +++ b/app/network/Authenticate/LoginInitial/LoginInitialViewModel.swift @@ -136,6 +136,30 @@ extension LoginInitialView { return false } } + + /** + * Bittensor + */ + @Published private(set) var bittensorChallengeMessage: String? + + /// Same server-issued challenge flow as prepareSolanaChallenge(), for + /// the Bittensor wallet-connect bridge. Must be called again for + /// every sign attempt - the server invalidates a challenge the + /// moment it is checked, whether the check succeeds or fails. + func prepareBittensorChallenge() async -> Bool { + let args = SdkAuthWalletChallengeArgs() + args.blockchain = "bittensor" + + do { + let result = try await urApiService.authWalletChallenge(args) + bittensorChallengeMessage = result.messageTemplate + return true + } catch { + bittensorChallengeMessage = nil + setLoginErrorMessage("There was an error connecting to the network") + return false + } + } let termsLink = "https://ur.io/terms" From 203548b235ee3be386765a22f06a98e9d1dd2bbe Mon Sep 17 00:00:00 2001 From: ryanmello07 Date: Sun, 12 Jul 2026 23:34:36 +0100 Subject: [PATCH 024/109] docs(ios): confirm Bittensor challenge blockchain identifier against server#402 The 'bittensor' blockchain string sent to /auth/wallet-challenge was previously an educated guess (by symmetry with Solana's 'solana'). urnetwork/server#402 has since been extended to accept it (and 'tao'/ 'TAO', case-insensitively) - this is now a confirmed contract, not an assumption. --- .../Authenticate/LoginInitial/LoginInitialViewModel.swift | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/network/Authenticate/LoginInitial/LoginInitialViewModel.swift b/app/network/Authenticate/LoginInitial/LoginInitialViewModel.swift index 89be0990..4abb005f 100644 --- a/app/network/Authenticate/LoginInitial/LoginInitialViewModel.swift +++ b/app/network/Authenticate/LoginInitial/LoginInitialViewModel.swift @@ -146,6 +146,11 @@ extension LoginInitialView { /// the Bittensor wallet-connect bridge. Must be called again for /// every sign attempt - the server invalidates a challenge the /// moment it is checked, whether the check succeeds or fails. + /// + /// The server accepts blockchain="bittensor" (case-insensitively, + /// also "tao"/"TAO") — confirmed against urnetwork/server#402 after + /// that PR was extended to support Bittensor wallets alongside + /// Solana in the challenge-based wallet auth flow. func prepareBittensorChallenge() async -> Bool { let args = SdkAuthWalletChallengeArgs() args.blockchain = "bittensor" From 7ef3be7194b168ddff94951342b6b11b59f96da2 Mon Sep 17 00:00:00 2001 From: ryanmello07 Date: Mon, 13 Jul 2026 00:01:39 +0100 Subject: [PATCH 025/109] ci: trigger rebuild with updated SDK fork (ip_blocker methods) From e57855976b33ea6c238fb6ddaae9ee4f94a1596d Mon Sep 17 00:00:00 2001 From: ryanmello07 Date: Mon, 13 Jul 2026 00:05:54 +0100 Subject: [PATCH 026/109] ci: trigger rebuild with updated connect fork (ip_blocker types) From 1003bd7db11d417bd7d8fec2f7702551f0ba3fa8 Mon Sep 17 00:00:00 2001 From: ryanmello07 Date: Mon, 13 Jul 2026 00:13:29 +0100 Subject: [PATCH 027/109] fix(ios): handle .bittensor case in isWalletAppInstalled switch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ad and tracker blocker upstream merge added .bittensor to the ConnectedWalletProvider enum but the isWalletAppInstalled switch wasn't updated. Bittensor wallets are browser extensions (not native apps) so return true like the macOS codepath — the ur.io/wallet-connect bridge handles detection. --- .../Shared/ViewModels/ConnectWalletProviderViewModel.swift | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/network/Shared/ViewModels/ConnectWalletProviderViewModel.swift b/app/network/Shared/ViewModels/ConnectWalletProviderViewModel.swift index 5c41d618..15eb03e3 100644 --- a/app/network/Shared/ViewModels/ConnectWalletProviderViewModel.swift +++ b/app/network/Shared/ViewModels/ConnectWalletProviderViewModel.swift @@ -708,6 +708,10 @@ class ConnectWalletProviderViewModel: ObservableObject { scheme = "phantom://" case .solflare: scheme = "solflare://" + case .bittensor: + // Bittensor wallets are browser extensions — always reachable + // through the ur.io/wallet-connect bridge, same as macOS. + return true } guard let url = URL(string: scheme) else { return false } return UIApplication.shared.canOpenURL(url) From 9612d49ed72a25f1bbbf5ed3c8eded7ed6883e94 Mon Sep 17 00:00:00 2001 From: ryanmello07 Date: Mon, 13 Jul 2026 01:17:00 +0100 Subject: [PATCH 028/109] =?UTF-8?q?fix(ios):=20copilot=20review=20fixes=20?= =?UTF-8?q?=E2=80=94=20empty=20template=20guard,=20IPv6=20normalization,?= =?UTF-8?q?=20bittensor=20switch?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Guard against empty messageTemplate in prepareSolanaChallenge and prepareBittensorChallenge (return false instead of silently accepting). - Preserve IPv6 literals when stripping ports in normalizeNetworkHost: bracketed [2001:db8::1]:8080 keeps the address, bare IPv6 is not mangled, IPv4 host:port continues to strip correctly. - Add .bittensor case to isWalletAppInstalled switch (browser extension wallets are always reachable via the ur.io bridge). Copilot review on apple#327. --- .../LoginInitial/LoginInitialViewModel.swift | 10 ++++++++++ .../Shared/Utilities/NetworkServerUtils.swift | 19 +++++++++++++------ 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/app/network/Authenticate/LoginInitial/LoginInitialViewModel.swift b/app/network/Authenticate/LoginInitial/LoginInitialViewModel.swift index 4abb005f..c707061d 100644 --- a/app/network/Authenticate/LoginInitial/LoginInitialViewModel.swift +++ b/app/network/Authenticate/LoginInitial/LoginInitialViewModel.swift @@ -128,6 +128,11 @@ extension LoginInitialView { do { let result = try await urApiService.authWalletChallenge(args) + guard !result.messageTemplate.isEmpty else { + solanaChallengeMessage = nil + setLoginErrorMessage("There was an error connecting to the network") + return false + } solanaChallengeMessage = result.messageTemplate return true } catch { @@ -157,6 +162,11 @@ extension LoginInitialView { do { let result = try await urApiService.authWalletChallenge(args) + guard !result.messageTemplate.isEmpty else { + bittensorChallengeMessage = nil + setLoginErrorMessage("There was an error connecting to the network") + return false + } bittensorChallengeMessage = result.messageTemplate return true } catch { diff --git a/app/network/Shared/Utilities/NetworkServerUtils.swift b/app/network/Shared/Utilities/NetworkServerUtils.swift index 0face536..844b23b4 100644 --- a/app/network/Shared/Utilities/NetworkServerUtils.swift +++ b/app/network/Shared/Utilities/NetworkServerUtils.swift @@ -25,12 +25,19 @@ enum NetworkServerUtils { if let atRange = value.range(of: "@") { value = String(value[atRange.upperBound...]) } - // Strip a trailing :port - the network domain field is a bare host - // used to derive api./connect.; a custom port belongs in - // the explicit API/connect URL overrides, not baked into the derived - // subdomain (which would otherwise produce invalid hosts like - // "api.192.168.1.5:8080"). - if let colonRange = value.range(of: ":", options: .backwards) { + // Strip a trailing :port when it looks like an IPv4 host:port or a + // bracketed IPv6 literal like [2001:db8::1]:8080. A bare IPv6 + // address (no brackets) must not be mangled — there is no + // reliable way to distinguish its colons from a port separator. + if value.hasSuffix("]"), let bracketStart = value.range(of: "["), + let portColon = value.range(of: "]:", options: .backwards) { + // IPv6 literal with port: "[...]:port" — keep the brackets + value = String(value[.. Date: Sun, 12 Jul 2026 18:08:49 -0700 Subject: [PATCH 029/109] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- app/network/Shared/Utilities/NetworkServerUtils.swift | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/app/network/Shared/Utilities/NetworkServerUtils.swift b/app/network/Shared/Utilities/NetworkServerUtils.swift index 844b23b4..c4eef37c 100644 --- a/app/network/Shared/Utilities/NetworkServerUtils.swift +++ b/app/network/Shared/Utilities/NetworkServerUtils.swift @@ -29,10 +29,9 @@ enum NetworkServerUtils { // bracketed IPv6 literal like [2001:db8::1]:8080. A bare IPv6 // address (no brackets) must not be mangled — there is no // reliable way to distinguish its colons from a port separator. - if value.hasSuffix("]"), let bracketStart = value.range(of: "["), - let portColon = value.range(of: "]:", options: .backwards) { - // IPv6 literal with port: "[...]:port" — keep the brackets - value = String(value[.. Date: Mon, 13 Jul 2026 02:24:21 +0100 Subject: [PATCH 030/109] fix(build): remove duplicate signInWithBittensor parameter from merge --- .../Authenticate/LoginInitial/LoginInitialView.swift | 6 ------ 1 file changed, 6 deletions(-) diff --git a/app/network/Authenticate/LoginInitial/LoginInitialView.swift b/app/network/Authenticate/LoginInitial/LoginInitialView.swift index 73f15b8e..a0665e1f 100644 --- a/app/network/Authenticate/LoginInitial/LoginInitialView.swift +++ b/app/network/Authenticate/LoginInitial/LoginInitialView.swift @@ -84,9 +84,6 @@ struct LoginInitialView: View { signInWithBittensor: { handleBittensorSignIn() }, - signInWithBittensor: { - handleBittensorSignIn() - }, presentAuthCodeLoginSheet: { viewModel.setPresentAuthCodeLoginSheet(true) }, @@ -126,9 +123,6 @@ struct LoginInitialView: View { signInWithBittensor: { handleBittensorSignIn() }, - signInWithBittensor: { - handleBittensorSignIn() - }, presentAuthCodeLoginSheet: { viewModel.setPresentAuthCodeLoginSheet(true) }, From 8e82c05dfeb67e12a3a41c544f7d404c58f77d68 Mon Sep 17 00:00:00 2001 From: ryanmello07 Date: Mon, 13 Jul 2026 04:58:30 +0100 Subject: [PATCH 031/109] fix(ios): solana login + network change refresh - Remove duplicate Task { in onOpenURL signature handler that created an unnecessary nested async context, causing the signature callback to potentially complete before the auth call finished. - Set connectedWalletProvider in handleSignMessage so the onSignature callback can correctly dispatch to the Solana handler even when sign happens without a prior connect. - Add .id(deviceManager.activeHostName) to LoginInitialView so the view (and its urApiService) are recreated when the network space changes, matching Android's behavior. --- .../Authenticate/LoginInitial/LoginInitialView.swift | 10 ++++------ app/network/Authenticate/LoginNavigationView.swift | 2 ++ .../ViewModels/ConnectWalletProviderViewModel.swift | 1 + 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/app/network/Authenticate/LoginInitial/LoginInitialView.swift b/app/network/Authenticate/LoginInitial/LoginInitialView.swift index cc9fdc36..874cc8d9 100644 --- a/app/network/Authenticate/LoginInitial/LoginInitialView.swift +++ b/app/network/Authenticate/LoginInitial/LoginInitialView.swift @@ -243,12 +243,11 @@ struct LoginInitialView: View { onSignature: { signature in guard let pk = connectWalletProviderViewModel.connectedPublicKey else { - viewModel.setIsSigningMessage(false) - viewModel.setLoginErrorMessage(String(localized: "There was an error logging in")) - return - } + viewModel.setIsSigningMessage(false) + viewModel.setLoginErrorMessage(String(localized: "There was an error logging in")) + return + } - Task { Task { if connectWalletProviderViewModel.connectedWalletProvider == .bittensor { await handleBittensorWalletResult( @@ -265,7 +264,6 @@ struct LoginInitialView: View { } } - }, onError: { _ in viewModel.setIsSigningMessage(false) diff --git a/app/network/Authenticate/LoginNavigationView.swift b/app/network/Authenticate/LoginNavigationView.swift index 43a08bd8..88ba4638 100644 --- a/app/network/Authenticate/LoginNavigationView.swift +++ b/app/network/Authenticate/LoginNavigationView.swift @@ -15,6 +15,7 @@ struct LoginNavigationView: View { @StateObject private var guestUpgradeViewModel: GuestUpgradeViewModel @EnvironmentObject var themeManager: ThemeManager + @EnvironmentObject var deviceManager: DeviceManager var api: SdkApi let urApiService: UrApiServiceProtocol @@ -40,6 +41,7 @@ struct LoginNavigationView: View { handleSuccess: handleSuccess, guestUpgradeViewModel: guestUpgradeViewModel ) + .id(deviceManager.activeHostName) .background(themeManager.currentTheme.backgroundColor.ignoresSafeArea()) .navigationDestination(for: LoginInitialNavigationPath.self) { path in switch path { diff --git a/app/network/Shared/ViewModels/ConnectWalletProviderViewModel.swift b/app/network/Shared/ViewModels/ConnectWalletProviderViewModel.swift index 15eb03e3..7d3ee367 100644 --- a/app/network/Shared/ViewModels/ConnectWalletProviderViewModel.swift +++ b/app/network/Shared/ViewModels/ConnectWalletProviderViewModel.swift @@ -320,6 +320,7 @@ class ConnectWalletProviderViewModel: ObservableObject { let connectedWalletProvider = (host == "solflare-connect" || host == "solflare-sign-message") ? ConnectedWalletProvider.solflare : ConnectedWalletProvider.phantom + self.connectedWalletProvider = connectedWalletProvider let isConnecting = host == "solflare-connect" || host == "phantom-connect" if isConnecting { From 2f0ecd2f72c5645a165d29f92fddbce35925f2f1 Mon Sep 17 00:00:00 2001 From: ryanmello07 Date: Tue, 14 Jul 2026 01:07:35 +0100 Subject: [PATCH 032/109] fix(ios): force-recreate LoginNavigationView on network change MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous .id() on LoginInitialView only recreated the child view — the parent LoginNavigationView still held a stale urApiService (let, set once in init). Move the .id(deviceManager.activeHostName) to all call sites that instantiate LoginNavigationView: - ContentView (initial login flow) - ConnectView-iOS (guest upgrade) - AccountRootView (iOS + macOS account creation) When the network space changes, activeHostName changes, causing SwiftUI to destroy and recreate the entire LoginNavigationView tree, which picks up the fresh api from ContentView's reactive deviceManager.api binding. --- app/network/ContentView.swift | 1 + app/network/Main/Account/AccountRootView.swift | 2 ++ app/network/Main/Connect/ConnectView-iOS.swift | 1 + 3 files changed, 4 insertions(+) diff --git a/app/network/ContentView.swift b/app/network/ContentView.swift index 01ebe3fd..6ebff06e 100644 --- a/app/network/ContentView.swift +++ b/app/network/ContentView.swift @@ -41,6 +41,7 @@ struct ContentView: View { api: api, handleSuccess: handleSuccessWithJwt ) + .id(deviceManager.activeHostName) .opacity(opacity) case .main: diff --git a/app/network/Main/Account/AccountRootView.swift b/app/network/Main/Account/AccountRootView.swift index f230f3e6..3ed3dc12 100644 --- a/app/network/Main/Account/AccountRootView.swift +++ b/app/network/Main/Account/AccountRootView.swift @@ -480,6 +480,7 @@ struct AccountRootView: View { } } ) + .id(deviceManager.activeHostName) } .toolbar { ToolbarItem { @@ -508,6 +509,7 @@ struct AccountRootView: View { } } ) + .id(deviceManager.activeHostName) .environmentObject(themeManager) .environmentObject(deviceManager) .environmentObject(snackbarManager) diff --git a/app/network/Main/Connect/ConnectView-iOS.swift b/app/network/Main/Connect/ConnectView-iOS.swift index 084f82e5..ff07e8c9 100644 --- a/app/network/Main/Connect/ConnectView-iOS.swift +++ b/app/network/Main/Connect/ConnectView-iOS.swift @@ -394,6 +394,7 @@ struct ConnectView_iOS: View { } } ) + .id(deviceManager.activeHostName) } .onChange(of: connectViewModel.connectionStatus) { _ in checkTunnelStatus() From 3db7f38fca8b50fd3c54980901a7847655719725 Mon Sep 17 00:00:00 2001 From: ryanmello07 Date: Tue, 14 Jul 2026 01:47:57 +0100 Subject: [PATCH 033/109] fix(ios): make UrApiService resolve the active api live, not once at init The previous fix (.id(deviceManager.activeHostName) on the views that instantiate LoginNavigationView) was unreliable in practice - after applying a custom network in the login flow's NetworkServerSheet, the app kept using the old network for wallet logins until restarted, even though auth-code login picked up the change immediately. Root cause: UrApiService captured a single SdkApi reference in its initializer (private let api: SdkApi) and never re-read it. Forcing SwiftUI to destroy/recreate the whole LoginNavigationView subtree via .id() should have rebuilt this, but proved unreliable - likely because the .sheet(...) that calls deviceManager.applyNetworkSpace(...) is itself hosted by the view being torn down, so the id-triggered recreation can race with or get coalesced away by the sheet's own dismissal transaction. This mirrors Android's actual fix: application.api is a *computed* property () that's re-evaluated on every access, never cached. Ported the same pattern to iOS: - UrApiService.api is now a computed property backed by an closure, resolved on every call instead of once at init. Added used at the top of every async function (mechanically, via ) which throws a clear error if no active api exists, instead of the old compile-time-guaranteed-non-optional but effectively stale reference. - Kept the existing for call sites that don't need to track network changes (e.g. MainView, once fully logged in). - Added for call sites that do. - LoginNavigationView now builds urApiService as a computed property reading live via a weak closure, instead of storing a UrApiService built once in init() (before deviceManager, an @EnvironmentObject, was even available). The .id(deviceManager.activeHostName) modifiers added previously are left in place as defense-in-depth (they still correctly reset transient @State like navigation path and in-flight sheet state on network switch) but are no longer the mechanism relied on for the api itself to update. --- .../Authenticate/LoginNavigationView.swift | 15 +++- .../Services/UrApiService/UrApiService.swift | 76 ++++++++++++++++++- 2 files changed, 86 insertions(+), 5 deletions(-) diff --git a/app/network/Authenticate/LoginNavigationView.swift b/app/network/Authenticate/LoginNavigationView.swift index 88ba4638..d603197e 100644 --- a/app/network/Authenticate/LoginNavigationView.swift +++ b/app/network/Authenticate/LoginNavigationView.swift @@ -18,13 +18,24 @@ struct LoginNavigationView: View { @EnvironmentObject var deviceManager: DeviceManager var api: SdkApi - let urApiService: UrApiServiceProtocol var cancel: (() -> Void)? = nil var handleSuccess: (_ jwt: String) async -> Void + + // Built from a live closure over `deviceManager.api` (an @EnvironmentObject, + // not available yet at init time) rather than the `api` snapshot passed + // in, so that switching the active network space (Settings > Change + // Network API) while this view is on screen is picked up immediately by + // every subsequent API call - including wallet-auth challenge/login, + // which previously kept hitting the network active when the login flow + // was first presented until the app was restarted. + private var urApiService: UrApiServiceProtocol { + UrApiService(apiProvider: { [weak deviceManager] in + deviceManager?.api ?? api + }) + } init(api: SdkApi, cancel: (() -> Void)? = nil, handleSuccess: @escaping (_ jwt: String) async -> Void) { self.api = api - self.urApiService = UrApiService(api: api) self.cancel = cancel self.handleSuccess = handleSuccess _guestUpgradeViewModel = StateObject(wrappedValue: GuestUpgradeViewModel(api: api)) diff --git a/app/network/Shared/Services/UrApiService/UrApiService.swift b/app/network/Shared/Services/UrApiService/UrApiService.swift index 090460c3..7eceb04b 100644 --- a/app/network/Shared/Services/UrApiService/UrApiService.swift +++ b/app/network/Shared/Services/UrApiService/UrApiService.swift @@ -10,12 +10,41 @@ import URnetworkSdk class UrApiService: UrApiServiceProtocol { - private let api: SdkApi + // Resolved live on every call instead of captured once at init. This + // mirrors Android's `application.api` (a `get() = networkSpaceManagerProvider + // .getNetworkSpace()?.api` computed property) so that switching the + // active network space (Settings > Change Network API) is picked up + // immediately by any in-flight or new API call, without needing to + // tear down and recreate the whole view hierarchy that holds this + // service (which proved unreliable via SwiftUI .id() invalidation). + private let apiProvider: () -> SdkApi? + private var api: SdkApi? { + apiProvider() + } let domain = "UrApiService" + /// Fixed-api initializer, kept for call sites/tests that already have a + /// concrete `SdkApi` and don't need to react to network switches (e.g. + /// once a device is fully initialized and logged in, the api rarely + /// changes for the lifetime of that screen). init(api: SdkApi) { - self.api = api + self.apiProvider = { api } + } + + /// Live-provider initializer. Pass a closure that always resolves the + /// current api (e.g. `{ deviceManager.api }`) so this service tracks + /// network-space changes made while the view using it is still on + /// screen (the login flow, most notably). + init(apiProvider: @escaping () -> SdkApi?) { + self.apiProvider = apiProvider + } + + private func requireApi() throws -> SdkApi { + guard let api else { + throw NSError(domain: domain, code: -1, userInfo: [NSLocalizedDescriptionKey: "No active network API available"]) + } + return api } private func nonEmptyJwt(_ jwt: String, context: String) -> Result { @@ -37,6 +66,8 @@ extension UrApiService { func getLeaderboard() async throws -> [LeaderboardEntry] { let args = SdkGetLeaderboardArgs() + let api = try requireApi() + let result: SdkLeaderboardResult = try await withCheckedThrowingContinuation { continuation in let callback = GetLeaderboardCallback { result, err in @@ -59,7 +90,7 @@ extension UrApiService { continuation.resume(returning: result) } - self.api.getLeaderboard(args, callback: callback) + api.getLeaderboard(args, callback: callback) } var earners: [LeaderboardEntry] = [] @@ -95,6 +126,8 @@ extension UrApiService { */ func setNetworkRankingPublic(_ isPublic: Bool) async throws { + let api = try requireApi() + let _: SdkSetNetworkRankingPublicResult = try await withCheckedThrowingContinuation { continuation in let callback = SetLeaderboardVisibilityCallback { result, err in @@ -132,6 +165,8 @@ extension UrApiService { */ func getLeaderboardRanking() async throws -> SdkGetNetworkRankingResult { + let api = try requireApi() + return try await withCheckedThrowingContinuation { continuation in let callback = GetNetworkRankingCallback { result, err in @@ -170,6 +205,7 @@ extension UrApiService { feedback: String, starCount: Int ) async throws -> SdkFeedbackSendResult { + let api = try requireApi() return try await withCheckedThrowingContinuation { continuation in let callback = SendFeedbackCallback { result, err in @@ -208,6 +244,7 @@ extension UrApiService { * Search providers */ func searchProviders(_ query: String) async throws -> SdkFilteredLocations { + let api = try requireApi() return try await withCheckedThrowingContinuation { continuation in let callback = FindLocationsCallback { result, err in @@ -239,6 +276,7 @@ extension UrApiService { * Get all providers */ func getAllProviders() async throws -> SdkFilteredLocations { + let api = try requireApi() return try await withCheckedThrowingContinuation { continuation in let callback = FindLocationsCallback { result, err in @@ -270,6 +308,7 @@ extension UrApiService { extension UrApiService { func authLogin(_ args: SdkAuthLoginArgs) async throws -> AuthLoginResult { + let api = try requireApi() return try await withCheckedThrowingContinuation { continuation in let callback = AuthLoginCallback { result, error in @@ -357,6 +396,7 @@ extension UrApiService { } func createNetwork(_ args: SdkNetworkCreateArgs) async throws -> LoginNetworkResult { + let api = try requireApi() return try await withCheckedThrowingContinuation { continuation in let callback = NetworkCreateCallback { result, err in @@ -402,6 +442,7 @@ extension UrApiService { } func upgradeGuest(_ args: SdkUpgradeGuestArgs) async throws -> LoginNetworkResult { + let api = try requireApi() return try await withCheckedThrowingContinuation { continuation in let callback = UpgradeGuestCallback { result, err in @@ -448,6 +489,8 @@ extension UrApiService { func createAuthCode() async throws -> SdkAuthCodeCreateResult { + let api = try requireApi() + return try await withCheckedThrowingContinuation { continuation in let callback = AuthCodeCreateCallback { result, err in @@ -481,6 +524,7 @@ extension UrApiService { } func authCodeLogin(_ args: SdkAuthCodeLoginArgs) async throws -> SdkAuthCodeLoginResult { + let api = try requireApi() return try await withCheckedThrowingContinuation { continuation in let callback = AuthCodeLoginCallback { result, err in @@ -516,6 +560,7 @@ extension UrApiService { } func authWalletChallenge(_ args: SdkAuthWalletChallengeArgs) async throws -> SdkAuthWalletChallengeResult { + let api = try requireApi() return try await withCheckedThrowingContinuation { continuation in let callback = AuthWalletChallengeCallback { result, err in @@ -550,6 +595,7 @@ extension UrApiService { extension UrApiService { func validateReferralCode(_ code: String) async throws -> SdkValidateReferralCodeResult { + let api = try requireApi() return try await withCheckedThrowingContinuation { continuation in let callback = ValidateReferralCallback { result, err in @@ -584,6 +630,8 @@ extension UrApiService { func fetchSubscriptionBalance() async throws -> SdkSubscriptionBalanceResult { + let api = try requireApi() + return try await withCheckedThrowingContinuation { continuation in let callback = GetSubscriptionBalanceCallback { result, err in @@ -609,6 +657,8 @@ extension UrApiService { func redeemBalanceCode(_ code: String) async throws -> SdkRedeemBalanceCodeResult { + let api = try requireApi() + return try await withCheckedThrowingContinuation { continuation in let callback = RedeemBalanceCodeCallback { result, err in @@ -636,6 +686,7 @@ extension UrApiService { } func getRedeemedBalanceCodes() async throws -> SdkGetNetworkRedeemedBalanceCodesResult { + let api = try requireApi() return try await withCheckedThrowingContinuation { continuation in let callback = GetNetworkRedeemedBalanceCodesCallback { result, err in @@ -670,6 +721,8 @@ extension UrApiService { func blockLocation(_ locationId: SdkId) async throws -> SdkNetworkBlockLocationResult { + let api = try requireApi() + return try await withCheckedThrowingContinuation { continuation in let callback = BlockLocationCallback { result, err in @@ -698,6 +751,8 @@ extension UrApiService { func unblockLocation(_ locationId: SdkId) async throws -> SdkNetworkUnblockLocationResult { + let api = try requireApi() + return try await withCheckedThrowingContinuation { continuation in let callback = UnblockLocationCallback { result, err in @@ -726,6 +781,8 @@ extension UrApiService { func getBlockedLocations() async throws -> SdkGetNetworkBlockedLocationsResult { + let api = try requireApi() + return try await withCheckedThrowingContinuation { continuation in let callback = GetNetworkBlockedLocationsCallback { result, err in @@ -756,6 +813,8 @@ extension UrApiService { func deleteAccount() async throws -> SdkNetworkDeleteResult { + let api = try requireApi() + return try await withCheckedThrowingContinuation { continuation in let callback = NetworkDeleteCallback { result, err in @@ -782,6 +841,8 @@ extension UrApiService { func getReferralNetwork() async throws -> SdkGetReferralNetworkResult { + let api = try requireApi() + return try await withCheckedThrowingContinuation { continuation in let callback = GetNetworkReferralCallback { result, err in @@ -806,6 +867,8 @@ extension UrApiService { func setNetworkReferral(_ referralCode: String) async throws -> SdkSetNetworkReferralResult { + let api = try requireApi() + return try await withCheckedThrowingContinuation { continuation in let callback = UpdateReferralNetworkCallback { result, err in @@ -833,6 +896,7 @@ extension UrApiService { } func unlinkReferralNetwork() async throws -> SdkUnlinkReferralNetworkResult { + let api = try requireApi() return try await withCheckedThrowingContinuation { continuation in let callback = UnlinkReferralNetworkCallback { result, err in @@ -862,6 +926,8 @@ extension UrApiService { func getNetworkReliability() async throws -> SdkGetNetworkReliabilityResult { + let api = try requireApi() + return try await withCheckedThrowingContinuation { continuation in let callback = GetNetworkReliabilityCallback { result, err in @@ -892,6 +958,8 @@ extension UrApiService { func validateWalletAddress(address: String, chain: String) async throws -> Bool { + let api = try requireApi() + return try await withCheckedThrowingContinuation { continuation in let callback = ValidateAddressCallback { result, err in @@ -1160,6 +1228,7 @@ enum UpdateReferralNetworkError: Error { extension UrApiService { func getNetworkClients() async throws -> SdkNetworkClientsResult { + let api = try requireApi() return try await withCheckedThrowingContinuation { continuation in let callback = GetNetworkClientsCallback { result, err in @@ -1183,6 +1252,7 @@ extension UrApiService { } func deviceSetName(deviceId: SdkId, deviceName: String) async throws -> Void { + let api = try requireApi() let _: SdkDeviceSetNameResult = try await withCheckedThrowingContinuation { continuation in let args = SdkDeviceSetNameArgs() From 1c4a133c680a22b4040e4febc0ec97d73ef21e1c Mon Sep 17 00:00:00 2001 From: ryanmello07 Date: Wed, 15 Jul 2026 08:30:19 +0100 Subject: [PATCH 034/109] ci: trigger beta build on PRs to beta/custom-server Add pull_request trigger alongside push so that all PRs targeting beta/custom-server automatically run the CI pipeline. This allows review and verification before merging. --- .github/workflows/beta-build.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/beta-build.yml b/.github/workflows/beta-build.yml index 19db1bee..d0954b5e 100644 --- a/.github/workflows/beta-build.yml +++ b/.github/workflows/beta-build.yml @@ -4,6 +4,9 @@ on: push: branches: - beta/custom-server + pull_request: + branches: + - beta/custom-server workflow_dispatch: permissions: From 6618d9ea8fbfc00be16f7c96006aee98e4aa1826 Mon Sep 17 00:00:00 2001 From: ryanmello07 Date: Wed, 15 Jul 2026 07:23:44 +0100 Subject: [PATCH 035/109] feat: add seedphrase sign-in and instant sign-up, remove guest mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add seedphrase-based authentication flow and instant (seedphrase) account creation to replace guest mode in the iOS app. Sign In — Seedphrase: - LoginSeedphraseView: multi-line text editor for 24-word seedphrase - LoginSeedphraseViewModel: validates, normalizes, and submits - UrApiService.loginWithSeedphrase: normalizes (lowercase, trim, collapse spaces), sets args.seedphrase, uses existing AuthLoginCallback - 'Sign in with Seedphrase' button on LoginInitialView Sign Up — Instant Account: - CreateNetworkInstantView: one-tap account creation, terms toggle - CreateNetworkInstantViewModel: calls createInstantAccount() - SeedphraseDisplayView: numbered grid with copy + confirmation flow - UrApiService.createInstantAccount: no auth args, reads result.seedphrase Guest Mode Cleanup: - Removed GuestModeSheet.swift - Removed GuestUpgradeViewModel.swift - Removed guest mode properties from LoginInitialViewModel - Removed guest upgrade flow from LoginPasswordView - Removed guest mode button from LoginInitialView SDK integration: - Uses new SdkAuthLoginArgs.seedphrase field - Uses new SdkNetworkCreateResult.seedphrase field - Removed SdkNetworkCreateArgs.guestMode field --- .../CreateNetworkInstantView.swift | 108 ++++++ .../CreateNetworkInstantViewModel.swift | 73 ++++ .../SeedphraseDisplayView.swift | 124 +++++++ .../LoginInitial/GuestModeSheet.swift | 67 ---- .../LoginInitial/LoginInitialView.swift | 324 +++++++----------- .../LoginInitial/LoginInitialViewModel.swift | 40 +-- .../Authenticate/LoginNavigationView.swift | 21 +- .../LoginNavigationViewModel.swift | 2 + .../LoginPassword/LoginPasswordView.swift | 49 +-- .../LoginSeedphrase/LoginSeedphraseView.swift | 104 ++++++ .../LoginSeedphraseViewModel.swift | 64 ++++ .../UrApiService/MockUrApiService.swift | 8 + .../Services/UrApiService/UrApiService.swift | 98 ++++++ .../UrApiService/UrApiServiceProtocol.swift | 2 + .../ViewModels/GuestUpgradeViewModel.swift | 111 ------ 15 files changed, 727 insertions(+), 468 deletions(-) create mode 100644 app/network/Authenticate/CreateNetworkInstant/CreateNetworkInstantView.swift create mode 100644 app/network/Authenticate/CreateNetworkInstant/CreateNetworkInstantViewModel.swift create mode 100644 app/network/Authenticate/CreateNetworkInstant/SeedphraseDisplayView.swift delete mode 100644 app/network/Authenticate/LoginInitial/GuestModeSheet.swift create mode 100644 app/network/Authenticate/LoginSeedphrase/LoginSeedphraseView.swift create mode 100644 app/network/Authenticate/LoginSeedphrase/LoginSeedphraseViewModel.swift delete mode 100644 app/network/Shared/ViewModels/GuestUpgradeViewModel.swift diff --git a/app/network/Authenticate/CreateNetworkInstant/CreateNetworkInstantView.swift b/app/network/Authenticate/CreateNetworkInstant/CreateNetworkInstantView.swift new file mode 100644 index 00000000..71a30b13 --- /dev/null +++ b/app/network/Authenticate/CreateNetworkInstant/CreateNetworkInstantView.swift @@ -0,0 +1,108 @@ +// +// CreateNetworkInstantView.swift +// URnetwork +// + +import SwiftUI +import URnetworkSdk + +private struct InstantAccountResult: Identifiable { + let id = UUID() + let jwt: String + let seedphrase: String +} + +struct CreateNetworkInstantView: View { + + @EnvironmentObject var themeManager: ThemeManager + + @StateObject private var viewModel: ViewModel + + let handleSuccess: (_ jwt: String) async -> Void + let back: () -> Void + + @State private var accountResult: InstantAccountResult? = nil + + init( + urApiService: UrApiServiceProtocol, + handleSuccess: @escaping (_ jwt: String) async -> Void, + back: @escaping () -> Void + ) { + _viewModel = StateObject(wrappedValue: ViewModel(urApiService: urApiService)) + self.handleSuccess = handleSuccess + self.back = back + } + + var body: some View { + ScrollView { + VStack(alignment: .center) { + + Text("Create Instant Account") + .foregroundColor(.urWhite) + .font(themeManager.currentTheme.titleFont) + + Spacer().frame(height: 16) + + Text("No email, phone, or password needed. Your account is secured by a seedphrase.") + .foregroundColor(themeManager.currentTheme.textMutedColor) + .font(themeManager.currentTheme.bodyFont) + .multilineTextAlignment(.center) + + Spacer().frame(height: 48) + + UrSwitchToggle(isOn: $viewModel.termsAgreed, isEnabled: !viewModel.isCreatingAccount) { + Text("I agree to URnetwork's [Terms and Services](https://ur.io/terms) and [Privacy Policy](https://ur.io/privacy)") + .foregroundColor(themeManager.currentTheme.textMutedColor) + .font(themeManager.currentTheme.secondaryBodyFont) + } + + Spacer().frame(height: 32) + + UrButton( + text: "Create Account", + action: { + Task { + let result = await viewModel.createInstantAccount() + if let (jwt, seedphrase) = result { + accountResult = InstantAccountResult(jwt: jwt, seedphrase: seedphrase) + } + } + }, + enabled: viewModel.formIsValid, + isProcessing: viewModel.isCreatingAccount + ) + + Spacer().frame(height: 8) + + UrInlineErrorText(message: viewModel.errorMessage) + + } + .padding() + .frame(maxWidth: 400) + .frame(maxWidth: .infinity) + } + .background(themeManager.currentTheme.backgroundColor.ignoresSafeArea()) + .toolbar { + ToolbarItem(placement: .navigationBarLeading) { + Button(action: { back() }) { + Image(systemName: "chevron.left") + .foregroundColor(themeManager.currentTheme.textColor) + } + } + } + .navigationBarBackButtonHidden(true) + .fullScreenCover(item: $accountResult) { result in + SeedphraseDisplayView( + seedphrase: result.seedphrase, + onConfirmed: { + accountResult = nil + Task { + await handleSuccess(result.jwt) + } + } + ) + .environmentObject(themeManager) + } + } + +} diff --git a/app/network/Authenticate/CreateNetworkInstant/CreateNetworkInstantViewModel.swift b/app/network/Authenticate/CreateNetworkInstant/CreateNetworkInstantViewModel.swift new file mode 100644 index 00000000..7243fdf6 --- /dev/null +++ b/app/network/Authenticate/CreateNetworkInstant/CreateNetworkInstantViewModel.swift @@ -0,0 +1,73 @@ +// +// CreateNetworkInstantViewModel.swift +// URnetwork +// + +import Foundation +import URnetworkSdk + +extension CreateNetworkInstantView { + + @MainActor + class ViewModel: ObservableObject { + + private let urApiService: UrApiServiceProtocol + + @Published var termsAgreed: Bool = false { + didSet { + errorMessage = nil + validateForm() + } + } + + @Published private(set) var isCreatingAccount: Bool = false + + @Published private(set) var formIsValid: Bool = false + + @Published private(set) var errorMessage: String? + + let domain = "CreateNetworkInstantViewModel" + + init(urApiService: UrApiServiceProtocol) { + self.urApiService = urApiService + } + + func setErrorMessage(_ message: String?) { + errorMessage = message + } + + private func validateForm() { + formIsValid = termsAgreed && !isCreatingAccount + } + + func createInstantAccount() async -> (jwt: String, seedphrase: String)? { + + if isCreatingAccount { + return nil + } + + guard termsAgreed else { + errorMessage = "You must agree to the Terms and Privacy Policy" + return nil + } + + isCreatingAccount = true + errorMessage = nil + + defer { + isCreatingAccount = false + } + + do { + let result = try await urApiService.createInstantAccount() + return result + } catch { + errorMessage = "There was an error creating your account. Please try again." + return nil + } + + } + + } + +} diff --git a/app/network/Authenticate/CreateNetworkInstant/SeedphraseDisplayView.swift b/app/network/Authenticate/CreateNetworkInstant/SeedphraseDisplayView.swift new file mode 100644 index 00000000..cc67e21c --- /dev/null +++ b/app/network/Authenticate/CreateNetworkInstant/SeedphraseDisplayView.swift @@ -0,0 +1,124 @@ +// +// SeedphraseDisplayView.swift +// URnetwork +// + +import SwiftUI + +struct SeedphraseDisplayView: View { + + @EnvironmentObject var themeManager: ThemeManager + + let seedphrase: String + let onConfirmed: (_ jwt: String) -> Void + + @State private var hasCopied = false + + private let words: [String] + + init(seedphrase: String, onConfirmed: @escaping (String) -> Void) { + self.seedphrase = seedphrase + self.onConfirmed = onConfirmed + self.words = seedphrase.components(separatedBy: " ") + } + + var body: some View { + NavigationStack { + ScrollView { + VStack(alignment: .center) { + + Text("Your Seedphrase") + .foregroundColor(.urWhite) + .font(themeManager.currentTheme.titleFont) + + Spacer().frame(height: 16) + + Text("⚠️ This is the ONLY time you'll see this.") + .foregroundColor(.urYellow) + .font(themeManager.currentTheme.bodyFont.bold()) + .multilineTextAlignment(.center) + + Text("Write it down and store it somewhere safe. If you lose it, you'll lose access to your account.") + .foregroundColor(themeManager.currentTheme.textMutedColor) + .font(themeManager.currentTheme.secondaryBodyFont) + .multilineTextAlignment(.center) + + Spacer().frame(height: 32) + + // Seedphrase grid display + LazyVGrid(columns: Array(repeating: GridItem(.flexible(), spacing: 8), count: 2), spacing: 8) { + ForEach(Array(words.enumerated()), id: \.offset) { index, word in + HStack(spacing: 4) { + Text("\(index + 1).") + .foregroundColor(themeManager.currentTheme.textMutedColor) + .font(.system(.caption, design: .monospaced)) + .frame(width: 24, alignment: .trailing) + Text(word) + .foregroundColor(.urWhite) + .font(.system(.body, design: .monospaced)) + .lineLimit(1) + Spacer() + } + .padding(.vertical, 6) + .padding(.horizontal, 8) + .background(themeManager.currentTheme.surfaceColor) + .cornerRadius(6) + } + } + .padding(12) + .overlay( + RoundedRectangle(cornerRadius: 8) + .stroke(themeManager.currentTheme.textMutedColor.opacity(0.3), lineWidth: 1) + ) + + Spacer().frame(height: 24) + + UrButton( + text: hasCopied ? "Copied!" : "Copy to Clipboard", + action: { + UIPasteboard.general.string = seedphrase + hasCopied = true + DispatchQueue.main.asyncAfter(deadline: .now() + 2) { + hasCopied = false + } + }, + enabled: !hasCopied + ) + + Spacer().frame(height: 16) + + UrButton( + text: "I've Saved My Seedphrase", + action: { + onConfirmed(seedphrase) + }, + enabled: true, + isProcessing: false + ) + .tint(.urGreen) + + } + .padding() + .frame(maxWidth: 400) + .frame(maxWidth: .infinity) + } + .background(themeManager.currentTheme.backgroundColor.ignoresSafeArea()) + .interactiveDismissDisabled(true) + .toolbar { + ToolbarItem(placement: .principal) { + Text("Secure Your Account") + .font(themeManager.currentTheme.toolbarTitleFont).fontWeight(.bold) + } + } + } + } + +} + +#Preview { + SeedphraseDisplayView( + seedphrase: "abandon ability able about above absent absorb abstract absurd abuse access accident account accuse achieve acid acoustic acquire across act action actor active activity", + onConfirmed: { _ in } + ) + .environmentObject(ThemeManager.shared) +} diff --git a/app/network/Authenticate/LoginInitial/GuestModeSheet.swift b/app/network/Authenticate/LoginInitial/GuestModeSheet.swift deleted file mode 100644 index e6179bfe..00000000 --- a/app/network/Authenticate/LoginInitial/GuestModeSheet.swift +++ /dev/null @@ -1,67 +0,0 @@ -// -// GuestModeSheet.swift -// URnetwork -// -// Created by Stuart Kuentzel on 2025/01/10. -// - -import SwiftUI - -struct GuestModeSheet: View { - - @Binding var termsAgreed: Bool - let isCreatingGuestNetwork: Bool - let errorMessage: String? - let onCreateGuestNetwork: () -> Void - - - @EnvironmentObject var themeManager: ThemeManager - - var body: some View { - VStack { - - HStack { - - Text("Try guest mode") - .font(themeManager.currentTheme.secondaryTitleFont) - - Spacer() - - } - - Spacer().frame(height: 24) - - UrSwitchToggle(isOn: $termsAgreed, isEnabled: !isCreatingGuestNetwork) { - Text("I agree to URnetwork's [Terms and Services](https://ur.io/terms) and [Privacy Policy](https://ur.io/privacy)") - .foregroundColor(themeManager.currentTheme.textMutedColor) - .font(themeManager.currentTheme.secondaryBodyFont) - } - - Spacer().frame(height: 24) - - UrButton( - text: "Enter URnetwork", - action: { - onCreateGuestNetwork() - }, - enabled: termsAgreed && !isCreatingGuestNetwork, - isProcessing: isCreatingGuestNetwork - ) - - Spacer().frame(height: 8) - - UrInlineErrorText(message: errorMessage) - - } - .padding() - } -} - -#Preview { - GuestModeSheet( - termsAgreed: .constant(false), - isCreatingGuestNetwork: false, - errorMessage: nil, - onCreateGuestNetwork: {} - ) -} diff --git a/app/network/Authenticate/LoginInitial/LoginInitialView.swift b/app/network/Authenticate/LoginInitial/LoginInitialView.swift index 874cc8d9..0d9dcc24 100644 --- a/app/network/Authenticate/LoginInitial/LoginInitialView.swift +++ b/app/network/Authenticate/LoginInitial/LoginInitialView.swift @@ -20,8 +20,6 @@ struct LoginInitialView: View { @StateObject private var viewModel: ViewModel @State private var initialIsLandscape: Bool = false - @ObservedObject var guestUpgradeViewModel: GuestUpgradeViewModel - let navigate: (LoginInitialNavigationPath) -> Void let cancel: (() -> Void)? let handleSuccess: (_ jwt: String) async -> Void @@ -31,14 +29,12 @@ struct LoginInitialView: View { urApiService: UrApiServiceProtocol, navigate: @escaping (LoginInitialNavigationPath) -> Void, cancel: (() -> Void)? = nil, - handleSuccess: @escaping (_ jwt: String) async -> Void, - guestUpgradeViewModel: GuestUpgradeViewModel + handleSuccess: @escaping (_ jwt: String) async -> Void ) { _viewModel = StateObject(wrappedValue: ViewModel(urApiService: urApiService)) self.navigate = navigate self.cancel = cancel self.handleSuccess = handleSuccess - self.guestUpgradeViewModel = guestUpgradeViewModel self.urApiService = urApiService } @@ -70,7 +66,7 @@ struct LoginInitialView: View { handleGoogleSignInButton: handleGoogleSignInButton, isValidUserAuth: viewModel.isValidUserAuth, activeLoginAction: viewModel.activeLoginAction, - isLoginActionInFlight: viewModel.isLoginActionInFlight || guestUpgradeViewModel.isUpgrading, + isLoginActionInFlight: viewModel.isLoginActionInFlight, loginErrorMessage: viewModel.loginErrorMessage, deviceExists: deviceExists, presentSignInWithSolanaSheet: { @@ -87,7 +83,12 @@ struct LoginInitialView: View { presentAuthCodeLoginSheet: { viewModel.setPresentAuthCodeLoginSheet(true) }, - presentGuestNetworkSheet: $viewModel.presentGuestNetworkSheet, + presentSeedphraseLogin: { + navigate(.seedphrase) + }, + presentCreateInstant: { + navigate(.createInstant) + } ) .frame(width: geometry.size.width / 2, alignment: .leading) @@ -109,7 +110,7 @@ struct LoginInitialView: View { handleGoogleSignInButton: handleGoogleSignInButton, isValidUserAuth: viewModel.isValidUserAuth, activeLoginAction: viewModel.activeLoginAction, - isLoginActionInFlight: viewModel.isLoginActionInFlight || guestUpgradeViewModel.isUpgrading, + isLoginActionInFlight: viewModel.isLoginActionInFlight, loginErrorMessage: viewModel.loginErrorMessage, deviceExists: deviceExists, presentSignInWithSolanaSheet: { @@ -126,7 +127,12 @@ struct LoginInitialView: View { presentAuthCodeLoginSheet: { viewModel.setPresentAuthCodeLoginSheet(true) }, - presentGuestNetworkSheet: $viewModel.presentGuestNetworkSheet + presentSeedphraseLogin: { + navigate(.seedphrase) + }, + presentCreateInstant: { + navigate(.createInstant) + } ) } @@ -156,23 +162,6 @@ struct LoginInitialView: View { .presentationDetents([.height(216)]) #endif - } - .sheet(isPresented: $viewModel.presentGuestNetworkSheet) { - - GuestModeSheet( - termsAgreed: $viewModel.termsAgreed, - isCreatingGuestNetwork: viewModel.isCreatingGuestNetwork, - errorMessage: viewModel.guestNetworkErrorMessage, - onCreateGuestNetwork: { - Task { - let result = await viewModel.createGuestNetwork() - await self.handleCreateGuestNetworkResult(result) - } - } - ) - .environmentObject(themeManager) - .presentationDetents([.height(264)]) - } .sheet(isPresented: $viewModel.presentAuthCodeLoginSheet) { @@ -305,23 +294,10 @@ struct LoginInitialView: View { switch createArgsResult { case .success(let args): - if deviceManager.device != nil { - - let upgradeArgs = self.createUpgradeSolanaWalletArgs(args) - - let result = await guestUpgradeViewModel.linkGuestToExistingLogin(args: upgradeArgs) - - await self.handleAuthLoginResult(result) - viewModel.presentSigninWithSolanaSheet = false - viewModel.setIsSigningMessage(false) - - - } else { - let result = await viewModel.authLogin(args: args) - await self.handleAuthLoginResult(result) - viewModel.presentSigninWithSolanaSheet = false - viewModel.setIsSigningMessage(false) - } + let result = await viewModel.authLogin(args: args) + await self.handleAuthLoginResult(result) + viewModel.presentSigninWithSolanaSheet = false + viewModel.setIsSigningMessage(false) case .failure(let error): print("error create args result: \(error.localizedDescription)") @@ -330,25 +306,6 @@ struct LoginInitialView: View { } } - private func handleCreateGuestNetworkResult(_ result: LoginNetworkResult) async { - switch result { - - case .successWithJwt(let jwt): - viewModel.presentGuestNetworkSheet = false - await handleSuccess(jwt) - break - case .failure(let error): - print("CreateNetworkView: handleResult: \(error.localizedDescription)") - viewModel.setGuestNetworkErrorMessage("There was an error creating your guest network. Please try again.") - break - default: - print("neither success with jwt or failure") - viewModel.setGuestNetworkErrorMessage("There was an error creating your guest network. Please try again.") - break - - } - } - private func handleAppleLoginResult(_ result: Result) async { guard viewModel.beginLoginAction(.apple) else { @@ -362,25 +319,8 @@ struct LoginInitialView: View { let createArgsResult = viewModel.createAppleAuthLoginArgs(result) switch createArgsResult { case .success(let args): - - if deviceManager.device != nil { - - // device exists, meaning we're in the guest flow - // link guest account to google account - - let upgradeArgs = self.createUpgradeExistingSocialArgs(args) - - let result = await guestUpgradeViewModel.linkGuestToExistingLogin(args: upgradeArgs) - await self.handleAuthLoginResult(result) - - } else { - - // login with apple - // let result = await viewModel.authLogin(args: args) - let result = await viewModel.authLogin(args: args) - await self.handleAuthLoginResult(result) - - } + let result = await viewModel.authLogin(args: args) + await self.handleAuthLoginResult(result) case .failure(let error): print("error create args result: \(error.localizedDescription)") @@ -489,18 +429,6 @@ struct LoginInitialView: View { } } - private func createUpgradeSolanaWalletArgs(_ args: SdkAuthLoginArgs) -> SdkUpgradeGuestExistingArgs { - let updateArgs = SdkUpgradeGuestExistingArgs() - updateArgs.walletAuth = args.walletAuth - return updateArgs - } - - /** - * Bittensor sign in: fetches a fresh server-issued challenge (same - * flow as Solana), then opens the ur.io/wallet-connect bridge; the - * wallet returns through onOpenURL with the ss58 address and sr25519 - * signature over that challenge. - */ private func handleBittensorSignIn() { Task { let ok = await viewModel.prepareBittensorChallenge() @@ -541,20 +469,9 @@ struct LoginInitialView: View { switch createArgsResult { case .success(let args): - if deviceManager.device != nil { - - let upgradeArgs = self.createUpgradeSolanaWalletArgs(args) - - let result = await guestUpgradeViewModel.linkGuestToExistingLogin(args: upgradeArgs) - - await self.handleAuthLoginResult(result) - viewModel.setIsSigningMessage(false) - - } else { - let result = await viewModel.authLogin(args: args) - await self.handleAuthLoginResult(result) - viewModel.setIsSigningMessage(false) - } + let result = await viewModel.authLogin(args: args) + await self.handleAuthLoginResult(result) + viewModel.setIsSigningMessage(false) case .failure(let error): print("error create args result: \(error.localizedDescription)") @@ -563,82 +480,6 @@ struct LoginInitialView: View { } } - private func createUpgradeExistingSocialArgs(_ args: SdkAuthLoginArgs) -> SdkUpgradeGuestExistingArgs { - let updateArgs = SdkUpgradeGuestExistingArgs() - updateArgs.authJwt = args.authJwt - updateArgs.authJwtType = args.authJwtType - return updateArgs - } - - private func handleGoogleSignInButton() async { - - guard viewModel.beginLoginAction(.google) else { - return - } - - defer { - viewModel.endLoginAction(.google) - } - - do { - #if os(iOS) - - guard let rootViewController = getRootViewController() else { - print("no root view controller found") - viewModel.setLoginErrorMessage(String(localized: "There was an error logging in")) - return - } - - let signInResult = try await GIDSignIn.sharedInstance.signIn(withPresenting: rootViewController) - #elseif os(macOS) - - guard let presentingWindow = NSApplication.shared.windows.first else { - print("There is no presenting window!") - viewModel.setLoginErrorMessage(String(localized: "There was an error logging in")) - return - } - - let signInResult = try await GIDSignIn.sharedInstance.signIn(withPresenting: presentingWindow) - #endif - - let createArgsResult = viewModel.createGoogleAuthLoginArgs(signInResult) - switch createArgsResult { - case .success(let args): - - if deviceManager.device != nil { - - // device exists, meaning we're in the guest flow - // link guest account to google account - // let result = await viewModel.linkGuestToExistingSocialLogin(args: args) - - let upgradeArgs = self.createUpgradeExistingSocialArgs(args) - - let result = await guestUpgradeViewModel.linkGuestToExistingLogin(args: upgradeArgs) - - await self.handleAuthLoginResult(result) - - } else { - - // login with google - let result = await viewModel.authLogin(args: args) - await self.handleAuthLoginResult(result) - - } - - case .failure(let error): - print("error create args result: \(error.localizedDescription)") - viewModel.setLoginErrorMessage(String(localized: "There was an error logging in")) - } - - } catch { - print("Error signing in: \(error.localizedDescription)") - viewModel.setLoginErrorMessage(String(localized: "There was an error logging in")) - } - - - - } - } private struct LoginInitialFormView: View { @@ -659,7 +500,9 @@ private struct LoginInitialFormView: View { let signInWithBittensor: () -> Void let presentAuthCodeLoginSheet: () -> Void - @Binding var presentGuestNetworkSheet: Bool + let presentSeedphraseLogin: () -> Void + let presentCreateInstant: () -> Void + @State private var presentNetworkServerSheet = false var body: some View { @@ -754,28 +597,54 @@ private struct LoginInitialFormView: View { Spacer() .frame(height: 24) - if !deviceExists { - // if a device exists, it means they are already in guest mode and trying to upgrade their account - // restrict access to create guest network from within authed guest network - + // Seedphrase login button + Button(action: presentSeedphraseLogin) { HStack { - Text("Commitment issues?") - .font(themeManager.currentTheme.bodyFont) - .foregroundColor(themeManager.currentTheme.textMutedColor) - - Button(action: { - presentGuestNetworkSheet = true - }) { - Text("Try Guest Mode") - .font(themeManager.currentTheme.bodyFont) - .foregroundColor(themeManager.currentTheme.textColor) - } - .buttonStyle(.plain) - .disabled(isLoginActionInFlight) - + Image(systemName: "key.fill") + .resizable() + .scaledToFit() + .frame(width: 16) + Spacer().frame(width: 8) + Text("Sign in with Seedphrase") + .foregroundColor(themeManager.currentTheme.inverseTextColor) + .font( + Font.system(size: 19, weight: .medium) + ) } - + .frame(maxWidth: .infinity) + } + .frame(height: 48) + .background(.white) + .clipShape(Capsule()) + .buttonStyle(.plain) + .opacity(isLoginActionInFlight ? 0.3 : 1) + .disabled(isLoginActionInFlight) + + Spacer() + .frame(height: 12) + + // Instant create account button + Button(action: presentCreateInstant) { + HStack { + Image(systemName: "bolt.fill") + .resizable() + .scaledToFit() + .frame(width: 16) + Spacer().frame(width: 8) + Text("Create Instant Account") + .foregroundColor(themeManager.currentTheme.inverseTextColor) + .font( + Font.system(size: 19, weight: .medium) + ) + } + .frame(maxWidth: .infinity) } + .frame(height: 48) + .background(.white) + .clipShape(Capsule()) + .buttonStyle(.plain) + .opacity(isLoginActionInFlight ? 0.3 : 1) + .disabled(isLoginActionInFlight) Spacer() .frame(height: 8) @@ -997,6 +866,8 @@ private struct SSOButtons: View { var presentSignInWithSolanaSheet: () -> Void var signInWithBittensor: () -> Void let presentAuthCodeLoginSheet: () -> Void + let presentSeedphraseLogin: () -> Void + let presentCreateInstant: () -> Void let activeLoginAction: LoginInitialView.LoginAction? let isLoginActionInFlight: Bool @@ -1114,6 +985,53 @@ private struct SSOButtons: View { Spacer().frame(maxWidth: .infinity) } .frame(maxWidth: .infinity) + + Spacer() + .frame(height: 8) + + // Seedphrase login button (macOS) + Button(action: presentSeedphraseLogin) { + HStack { + Image(systemName: "key.fill") + .resizable() + .scaledToFit() + .frame(width: 12) + Spacer().frame(width: 8) + Text("Sign in with Seedphrase") + .foregroundColor(themeManager.currentTheme.inverseTextColor) + .font(Font.system(size: 12, weight: .medium)) + } + .frame(maxWidth: .infinity) + } + .frame(height: 30) + .frame(maxWidth: .infinity) + .background(.white) + .cornerRadius(6) + .buttonStyle(.plain) + .disabled(isLoginActionInFlight) + + Spacer().frame(width: 8) + + // Instant create account button (macOS) + Button(action: presentCreateInstant) { + HStack { + Image(systemName: "bolt.fill") + .resizable() + .scaledToFit() + .frame(width: 12) + Spacer().frame(width: 8) + Text("Create Instant Account") + .foregroundColor(themeManager.currentTheme.inverseTextColor) + .font(Font.system(size: 12, weight: .medium)) + } + .frame(maxWidth: .infinity) + } + .frame(height: 30) + .frame(maxWidth: .infinity) + .background(.white) + .cornerRadius(6) + .buttonStyle(.plain) + .disabled(isLoginActionInFlight) } } diff --git a/app/network/Authenticate/LoginInitial/LoginInitialViewModel.swift b/app/network/Authenticate/LoginInitial/LoginInitialViewModel.swift index c707061d..fa1e69cf 100644 --- a/app/network/Authenticate/LoginInitial/LoginInitialViewModel.swift +++ b/app/network/Authenticate/LoginInitial/LoginInitialViewModel.swift @@ -72,18 +72,6 @@ extension LoginInitialView { loginErrorMessage = message } - /** - * Guest mode - */ - @Published private(set) var isCreatingGuestNetwork: Bool = false - @Published var presentGuestNetworkSheet: Bool = false - @Published var termsAgreed: Bool = false - @Published private(set) var guestNetworkErrorMessage: String? - - func setGuestNetworkErrorMessage(_ message: String?) -> Void { - guestNetworkErrorMessage = message - } - /** * Auth code login */ @@ -287,34 +275,16 @@ extension LoginInitialView.ViewModel { } -// MARK: create guest network +// MARK: create instant seedphrase account extension LoginInitialView.ViewModel { - func createGuestNetwork() async -> LoginNetworkResult { - - if self.isCreatingGuestNetwork { - return .failure(LoginError.inProgress) - } - - self.isCreatingGuestNetwork = true - self.guestNetworkErrorMessage = nil + func createInstantAccount() async -> (jwt: String, seedphrase: String)? { do { - - let args = SdkNetworkCreateArgs() - args.terms = true - args.guestMode = true - - let result = try await urApiService.createNetwork(args) - - self.isCreatingGuestNetwork = false - - + let result = try await urApiService.createInstantAccount() return result - - } catch(let error) { - self.isCreatingGuestNetwork = false - return .failure(error) + } catch { + return nil } } diff --git a/app/network/Authenticate/LoginNavigationView.swift b/app/network/Authenticate/LoginNavigationView.swift index d603197e..2d6fd4a6 100644 --- a/app/network/Authenticate/LoginNavigationView.swift +++ b/app/network/Authenticate/LoginNavigationView.swift @@ -12,8 +12,6 @@ struct LoginNavigationView: View { @StateObject private var viewModel = ViewModel() - @StateObject private var guestUpgradeViewModel: GuestUpgradeViewModel - @EnvironmentObject var themeManager: ThemeManager @EnvironmentObject var deviceManager: DeviceManager @@ -38,7 +36,6 @@ struct LoginNavigationView: View { self.api = api self.cancel = cancel self.handleSuccess = handleSuccess - _guestUpgradeViewModel = StateObject(wrappedValue: GuestUpgradeViewModel(api: api)) } var body: some View { @@ -49,8 +46,7 @@ struct LoginNavigationView: View { urApiService: urApiService, navigate: viewModel.navigate, cancel: cancel, - handleSuccess: handleSuccess, - guestUpgradeViewModel: guestUpgradeViewModel + handleSuccess: handleSuccess ) .id(deviceManager.activeHostName) .background(themeManager.currentTheme.backgroundColor.ignoresSafeArea()) @@ -61,7 +57,6 @@ struct LoginNavigationView: View { userAuth: userAuth, navigate: viewModel.navigate, handleSuccess: handleSuccess, - guestUpgradeViewModel: guestUpgradeViewModel, api: api ) .background(themeManager.currentTheme.backgroundColor.ignoresSafeArea()) @@ -89,6 +84,20 @@ struct LoginNavigationView: View { api: api ) .background(themeManager.currentTheme.backgroundColor.ignoresSafeArea()) + case .seedphrase: + LoginSeedphraseView( + urApiService: urApiService, + handleSuccess: handleSuccess, + back: viewModel.back + ) + .background(themeManager.currentTheme.backgroundColor.ignoresSafeArea()) + case .createInstant: + CreateNetworkInstantView( + urApiService: urApiService, + handleSuccess: handleSuccess, + back: viewModel.back + ) + .background(themeManager.currentTheme.backgroundColor.ignoresSafeArea()) } } } diff --git a/app/network/Authenticate/LoginNavigationViewModel.swift b/app/network/Authenticate/LoginNavigationViewModel.swift index 29260e74..a1f53261 100644 --- a/app/network/Authenticate/LoginNavigationViewModel.swift +++ b/app/network/Authenticate/LoginNavigationViewModel.swift @@ -14,6 +14,8 @@ enum LoginInitialNavigationPath: Hashable { case createNetwork(_ authLoginArgs: SdkAuthLoginArgs) case verify(_ userAuth: String) case resetPassword(_ userAuth: String) + case seedphrase + case createInstant } extension LoginNavigationView { diff --git a/app/network/Authenticate/LoginPassword/LoginPasswordView.swift b/app/network/Authenticate/LoginPassword/LoginPasswordView.swift index ca47c8fd..1c92fe2e 100644 --- a/app/network/Authenticate/LoginPassword/LoginPasswordView.swift +++ b/app/network/Authenticate/LoginPassword/LoginPasswordView.swift @@ -11,9 +11,7 @@ import URnetworkSdk struct LoginPasswordView: View { @EnvironmentObject var themeManager: ThemeManager - @EnvironmentObject var deviceManager: DeviceManager @StateObject private var viewModel: ViewModel - @ObservedObject var guestUpgradeViewModel: GuestUpgradeViewModel var userAuth: String var navigate: (LoginInitialNavigationPath) -> Void @@ -25,14 +23,12 @@ struct LoginPasswordView: View { userAuth: String, navigate: @escaping (LoginInitialNavigationPath) -> Void, handleSuccess: @escaping (_ jwt: String) async -> Void, - guestUpgradeViewModel: GuestUpgradeViewModel, api: SdkApi? ) { _viewModel = StateObject(wrappedValue: ViewModel(api: api)) self.userAuth = userAuth self.navigate = navigate self.handleSuccess = handleSuccess - self.guestUpgradeViewModel = guestUpgradeViewModel } var body: some View { @@ -88,7 +84,6 @@ struct LoginPasswordView: View { }, enabled: !isLoginInProgress && viewModel.isValid, isProcessing: isLoginInProgress - // todo add icon ) Spacer().frame(height: 8) @@ -123,7 +118,7 @@ struct LoginPasswordView: View { } private var isLoginInProgress: Bool { - viewModel.isLoggingIn || guestUpgradeViewModel.isUpgrading + viewModel.isLoggingIn } private func submitPasswordLogin() async { @@ -133,45 +128,8 @@ struct LoginPasswordView: View { viewModel.setErrorMessage(nil) - if deviceManager.device != nil { - // in guest mode; merge user auth account and login - let args = SdkUpgradeGuestExistingArgs() - args.userAuth = self.userAuth - args.password = self.viewModel.password - - let result = await guestUpgradeViewModel.linkGuestToExistingLogin(args: args) - await handleUpgradeLoginResult(result) - } else { - let result = await viewModel.loginWithPassword(userAuth: self.userAuth) - await handleLoginResult(result) - } - } - - private func handleUpgradeLoginResult(_ result: AuthLoginResult) async { - switch result { - - case .login(let authJwt): - await handleSuccess(authJwt) - break - - case .failure(let error): - print("auth login error: \(error.localizedDescription)") - viewModel.setIsLoggingIn(false) - viewModel.setErrorMessage(snackbarErrorMessage) - break - - case .verificationRequired(let userAuth): - navigate(.verify(userAuth)) - viewModel.setIsLoggingIn(false) - break - - default: - viewModel.setIsLoggingIn(false) - viewModel.setErrorMessage(snackbarErrorMessage) - print("upgrade login result does not match any case") - return - - } + let result = await viewModel.loginWithPassword(userAuth: self.userAuth) + await handleLoginResult(result) } private func handleLoginResult(_ result: LoginNetworkResult) async { @@ -179,7 +137,6 @@ struct LoginPasswordView: View { case .successWithJwt(let jwt): await handleSuccess(jwt) - // viewModel.setIsLoggingIn(false) break case .successWithVerificationRequired: diff --git a/app/network/Authenticate/LoginSeedphrase/LoginSeedphraseView.swift b/app/network/Authenticate/LoginSeedphrase/LoginSeedphraseView.swift new file mode 100644 index 00000000..1e908398 --- /dev/null +++ b/app/network/Authenticate/LoginSeedphrase/LoginSeedphraseView.swift @@ -0,0 +1,104 @@ +// +// LoginSeedphraseView.swift +// URnetwork +// + +import SwiftUI +import URnetworkSdk + +struct LoginSeedphraseView: View { + + @EnvironmentObject var themeManager: ThemeManager + + @StateObject private var viewModel: ViewModel + + let handleSuccess: (_ jwt: String) async -> Void + let back: () -> Void + + init( + urApiService: UrApiServiceProtocol, + handleSuccess: @escaping (_ jwt: String) async -> Void, + back: @escaping () -> Void + ) { + _viewModel = StateObject(wrappedValue: ViewModel(urApiService: urApiService)) + self.handleSuccess = handleSuccess + self.back = back + } + + var body: some View { + ScrollView { + VStack(alignment: .center) { + + Text("Sign in with Seedphrase") + .foregroundColor(.urWhite) + .font(themeManager.currentTheme.titleFont) + + Spacer().frame(height: 48) + + TextEditor(text: $viewModel.seedphrase) + .font(.system(.body, design: .monospaced)) + .frame(minHeight: 160) + .padding(12) + .overlay( + RoundedRectangle(cornerRadius: 8) + .stroke(themeManager.currentTheme.textMutedColor.opacity(0.3), lineWidth: 1) + ) + .overlay(alignment: .topLeading) { + if viewModel.seedphrase.isEmpty { + Text("Paste your 24-word seedphrase here") + .foregroundColor(themeManager.currentTheme.textMutedColor) + .font(themeManager.currentTheme.bodyFont) + .padding(.horizontal, 16) + .padding(.vertical, 20) + .allowsHitTesting(false) + } + } + .cornerRadius(8) + + Spacer().frame(height: 32) + + UrButton( + text: "Sign In", + action: { + Task { + let result = await viewModel.login() + await handleResult(result) + } + }, + enabled: viewModel.isSeedphraseValid && !viewModel.isLoggingIn, + isProcessing: viewModel.isLoggingIn + ) + + Spacer().frame(height: 8) + + UrInlineErrorText(message: viewModel.errorMessage) + + } + .padding() + .frame(maxWidth: 400) + .frame(maxWidth: .infinity) + } + .background(themeManager.currentTheme.backgroundColor.ignoresSafeArea()) + .toolbar { + ToolbarItem(placement: .navigationBarLeading) { + Button(action: { back() }) { + Image(systemName: "chevron.left") + .foregroundColor(themeManager.currentTheme.textColor) + } + } + } + .navigationBarBackButtonHidden(true) + } + + private func handleResult(_ result: AuthLoginResult) async { + switch result { + case .login(let jwt): + await handleSuccess(jwt) + case .failure(let error): + viewModel.errorMessage = error.localizedDescription + default: + viewModel.errorMessage = "There was an error signing in with your seedphrase. Please try again." + } + } + +} diff --git a/app/network/Authenticate/LoginSeedphrase/LoginSeedphraseViewModel.swift b/app/network/Authenticate/LoginSeedphrase/LoginSeedphraseViewModel.swift new file mode 100644 index 00000000..a97d6271 --- /dev/null +++ b/app/network/Authenticate/LoginSeedphrase/LoginSeedphraseViewModel.swift @@ -0,0 +1,64 @@ +// +// LoginSeedphraseViewModel.swift +// URnetwork +// + +import Foundation +import URnetworkSdk + +extension LoginSeedphraseView { + + @MainActor + class ViewModel: ObservableObject { + + private let urApiService: UrApiServiceProtocol + + @Published var seedphrase: String = "" { + didSet { + errorMessage = nil + } + } + + @Published private(set) var isLoggingIn: Bool = false + + @Published private(set) var errorMessage: String? + + let domain = "LoginSeedphraseViewModel" + + init(urApiService: UrApiServiceProtocol) { + self.urApiService = urApiService + } + + var isSeedphraseValid: Bool { + !seedphrase.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty + } + + func login() async -> AuthLoginResult { + + if isLoggingIn { + return .failure(NSError(domain: domain, code: 0, userInfo: [NSLocalizedDescriptionKey: "Login already in progress"])) + } + + guard isSeedphraseValid else { + return .failure(NSError(domain: domain, code: 0, userInfo: [NSLocalizedDescriptionKey: "Seedphrase is empty"])) + } + + isLoggingIn = true + errorMessage = nil + + defer { + isLoggingIn = false + } + + do { + let result = try await urApiService.loginWithSeedphrase(seedphrase: seedphrase) + return result + } catch { + return .failure(error) + } + + } + + } + +} diff --git a/app/network/Shared/Services/UrApiService/MockUrApiService.swift b/app/network/Shared/Services/UrApiService/MockUrApiService.swift index f27dd46c..997c2f5a 100644 --- a/app/network/Shared/Services/UrApiService/MockUrApiService.swift +++ b/app/network/Shared/Services/UrApiService/MockUrApiService.swift @@ -97,6 +97,14 @@ class MockUrApiService: UrApiServiceProtocol { return SdkUnlinkReferralNetworkResult() } + func loginWithSeedphrase(seedphrase: String) async throws -> AuthLoginResult { + return AuthLoginResult.failure(LoginError.inProgress) + } + + func createInstantAccount() async throws -> (jwt: String, seedphrase: String) { + return ("mock-jwt", "mock seedphrase words here for testing purposes only") + } + func redeemBalanceCode(_ code: String) async throws -> SdkRedeemBalanceCodeResult { return SdkRedeemBalanceCodeResult() } diff --git a/app/network/Shared/Services/UrApiService/UrApiService.swift b/app/network/Shared/Services/UrApiService/UrApiService.swift index 7eceb04b..a65fe84a 100644 --- a/app/network/Shared/Services/UrApiService/UrApiService.swift +++ b/app/network/Shared/Services/UrApiService/UrApiService.swift @@ -395,6 +395,104 @@ extension UrApiService { } } + func loginWithSeedphrase(seedphrase: String) async throws -> AuthLoginResult { + let api = try requireApi() + return try await withCheckedThrowingContinuation { continuation in + + let callback = AuthLoginCallback { result, error in + + if let error { + continuation.resume(throwing: error) + return + } + + guard let result else { + continuation.resume(throwing: NSError(domain: "UrApiService", code: -1, userInfo: [NSLocalizedDescriptionKey: "No result found"])) + return + } + + if let resultError = result.error { + continuation.resume(throwing: NSError(domain: "UrApiService", code: -1, userInfo: [NSLocalizedDescriptionKey: "result.error exists \(resultError.message)"])) + return + } + + if let network = result.network { + switch self.nonEmptyJwt(network.byJwt, context: "loginWithSeedphrase") { + case .success(let jwt): + continuation.resume(returning: .login(jwt)) + case .failure(let error): + continuation.resume(throwing: error) + } + return + } + + continuation.resume(returning: .failure(NSError(domain: "UrApiService", code: -1, userInfo: [NSLocalizedDescriptionKey: "Seedphrase login returned no network"]))) + + } + + let args = SdkAuthLoginArgs() + // Normalize: lowercase, trim, collapse multiple spaces + let normalized = seedphrase + .lowercased() + .trimmingCharacters(in: .whitespacesAndNewlines) + .components(separatedBy: .whitespacesAndNewlines) + .filter { !$0.isEmpty } + .joined(separator: " ") + args.seedphrase = normalized + api.authLogin(args, callback: callback) + + } + } + + func createInstantAccount() async throws -> (jwt: String, seedphrase: String) { + let api = try requireApi() + return try await withCheckedThrowingContinuation { continuation in + + let callback = NetworkCreateCallback { result, err in + + if let err = err { + continuation.resume(throwing: err) + return + } + + guard let result = result else { + continuation.resume(throwing: NSError(domain: "UrApiService", code: -1, userInfo: [NSLocalizedDescriptionKey: "createInstantAccount returned nil result"])) + return + } + + if let resultError = result.error { + continuation.resume(throwing: NSError(domain: "UrApiService", code: -1, userInfo: [NSLocalizedDescriptionKey: resultError.message])) + return + } + + guard let seedphrase = result.seedphrase, !seedphrase.isEmpty else { + continuation.resume(throwing: NSError(domain: "UrApiService", code: -1, userInfo: [NSLocalizedDescriptionKey: "No seedphrase in result"])) + return + } + + if let network = result.network { + switch self.nonEmptyJwt(network.byJwt, context: "createInstantAccount") { + case .success(let jwt): + continuation.resume(returning: (jwt, seedphrase)) + case .failure(let error): + continuation.resume(throwing: error) + } + return + } else { + continuation.resume(throwing: NSError(domain: "UrApiService", code: 0, userInfo: [NSLocalizedDescriptionKey: "No network object found in result"])) + return + } + + } + + let args = SdkNetworkCreateArgs() + args.terms = true + // No userAuth, password, authJwt, walletAuth — triggers seedphrase path + api.networkCreate(args, callback: callback) + + } + } + func createNetwork(_ args: SdkNetworkCreateArgs) async throws -> LoginNetworkResult { let api = try requireApi() return try await withCheckedThrowingContinuation { continuation in diff --git a/app/network/Shared/Services/UrApiService/UrApiServiceProtocol.swift b/app/network/Shared/Services/UrApiService/UrApiServiceProtocol.swift index c1e481e9..3a0c5b7d 100644 --- a/app/network/Shared/Services/UrApiService/UrApiServiceProtocol.swift +++ b/app/network/Shared/Services/UrApiService/UrApiServiceProtocol.swift @@ -66,6 +66,8 @@ protocol UrApiServiceProtocol { /** * Settings */ + func loginWithSeedphrase(seedphrase: String) async throws -> AuthLoginResult + func createInstantAccount() async throws -> (jwt: String, seedphrase: String) func deleteAccount() async throws -> SdkNetworkDeleteResult func getReferralNetwork() async throws -> SdkGetReferralNetworkResult func setNetworkReferral(_ referralCode: String) async throws -> SdkSetNetworkReferralResult diff --git a/app/network/Shared/ViewModels/GuestUpgradeViewModel.swift b/app/network/Shared/ViewModels/GuestUpgradeViewModel.swift deleted file mode 100644 index 99a04080..00000000 --- a/app/network/Shared/ViewModels/GuestUpgradeViewModel.swift +++ /dev/null @@ -1,111 +0,0 @@ -// -// GuestUpgradeViewModel.swift -// URnetwork -// -// Created by Stuart Kuentzel on 2025/01/19. -// - -import Foundation -import URnetworkSdk - -@MainActor -class GuestUpgradeViewModel: ObservableObject { - - @Published private(set) var isUpgrading: Bool = false - - private var api: SdkApi - - let domain = "[GuestUpgradeViewModel]" - - init(api: SdkApi) { - self.api = api - } - - func linkGuestToExistingLogin(args: SdkUpgradeGuestExistingArgs) async -> AuthLoginResult { - - if isUpgrading { - return .failure(LoginError.inProgress) - } - - isUpgrading = true - defer { - isUpgrading = false - } - - do { - - let result: AuthLoginResult = try await withCheckedThrowingContinuation { continuation in - - let callback = UpgradeGuestExistingCallback { result, error in - - guard let result else { - continuation.resume(throwing: error ?? NSError(domain: "[GuestUpgradeViewModel]", code: -1, userInfo: [NSLocalizedDescriptionKey: "No result found"])) - - return - } - - if let resultError = result.error { - - continuation.resume(throwing: NSError(domain: "[GuestUpgradeViewModel]", code: -1, userInfo: [NSLocalizedDescriptionKey: "result.error exists \(resultError.message)"])) - - return - } - - /** - * In the case a guest user is upgrading to an existing account - * but the existing account has not yet been verified - */ - if let verificationRequired = result.verificationRequired { - continuation.resume(returning: .verificationRequired(verificationRequired.userAuth)) - return - } - - /** - * JWT exists, proceed to authenticate network - */ - if let network = result.network { - guard !network.byJwt.isEmpty else { - continuation.resume(throwing: NSError(domain: "[GuestUpgradeViewModel]", code: -1, userInfo: [NSLocalizedDescriptionKey: "byJWT is empty"])) - return - } - - continuation.resume(returning: .login(network.byJwt)) - return - } - - /** - * No network exists - * Navigate to create view - */ - let authLoginArgs = SdkAuthLoginArgs() - - authLoginArgs.authJwt = args.authJwt - authLoginArgs.authJwtType = args.authJwtType - authLoginArgs.userAuth = args.userAuth - authLoginArgs.walletAuth = args.walletAuth - - - continuation.resume(returning: .create(authLoginArgs)) - - - } - - api.upgradeGuestExisting(args, callback: callback) - - } - - return result - - } catch(let error) { - return .failure(error) - } - - } - -} - -private class UpgradeGuestExistingCallback: SdkCallback, SdkUpgradeGuestExistingCallbackProtocol { - func result(_ result: SdkUpgradeGuestExistingResult?, err: Error?) { - handleResult(result, err: err) - } -} From 201f0fbab87ae75da685c19c3090d9c73734e537 Mon Sep 17 00:00:00 2001 From: ryanmello07 Date: Wed, 15 Jul 2026 07:35:49 +0100 Subject: [PATCH 036/109] =?UTF-8?q?fix:=20address=20code=20review=20findin?= =?UTF-8?q?gs=20=E2=80=94=20dead=20code,=20seedphrase=20normalization,=20w?= =?UTF-8?q?ord=20count=20validation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Remove dead createInstantAccount() from LoginInitialViewModel (unused since CreateNetworkInstantView has its own VM) - SeedphraseDisplayView normalizes whitespace before splitting into word grid - LoginSeedphraseViewModel now normalizes seedphrase (lowercase, trim, collapse spaces) before submit - Add word count validation (12 or 24 words) with non-blocking warning display - View shows word count warning for incorrect word counts --- .../SeedphraseDisplayView.swift | 10 ++++-- .../LoginInitial/LoginInitialViewModel.swift | 16 --------- .../LoginSeedphrase/LoginSeedphraseView.swift | 10 ++++++ .../LoginSeedphraseViewModel.swift | 34 ++++++++++++++++++- 4 files changed, 51 insertions(+), 19 deletions(-) diff --git a/app/network/Authenticate/CreateNetworkInstant/SeedphraseDisplayView.swift b/app/network/Authenticate/CreateNetworkInstant/SeedphraseDisplayView.swift index cc67e21c..b544456f 100644 --- a/app/network/Authenticate/CreateNetworkInstant/SeedphraseDisplayView.swift +++ b/app/network/Authenticate/CreateNetworkInstant/SeedphraseDisplayView.swift @@ -17,9 +17,15 @@ struct SeedphraseDisplayView: View { private let words: [String] init(seedphrase: String, onConfirmed: @escaping (String) -> Void) { - self.seedphrase = seedphrase + // Normalize: trim whitespace and collapse multiple spaces + let normalized = seedphrase + .trimmingCharacters(in: .whitespacesAndNewlines) + .components(separatedBy: .whitespacesAndNewlines) + .filter { !$0.isEmpty } + .joined(separator: " ") + self.seedphrase = normalized self.onConfirmed = onConfirmed - self.words = seedphrase.components(separatedBy: " ") + self.words = normalized.components(separatedBy: " ") } var body: some View { diff --git a/app/network/Authenticate/LoginInitial/LoginInitialViewModel.swift b/app/network/Authenticate/LoginInitial/LoginInitialViewModel.swift index fa1e69cf..3e434fd2 100644 --- a/app/network/Authenticate/LoginInitial/LoginInitialViewModel.swift +++ b/app/network/Authenticate/LoginInitial/LoginInitialViewModel.swift @@ -275,22 +275,6 @@ extension LoginInitialView.ViewModel { } -// MARK: create instant seedphrase account -extension LoginInitialView.ViewModel { - - func createInstantAccount() async -> (jwt: String, seedphrase: String)? { - - do { - let result = try await urApiService.createInstantAccount() - return result - } catch { - return nil - } - - } - -} - // MARK: Solana Sign in extension LoginInitialView.ViewModel { func createSolanaAuthLoginArgs(message: String, signature: String, publicKey: String) -> Result { diff --git a/app/network/Authenticate/LoginSeedphrase/LoginSeedphraseView.swift b/app/network/Authenticate/LoginSeedphrase/LoginSeedphraseView.swift index 1e908398..adef85a2 100644 --- a/app/network/Authenticate/LoginSeedphrase/LoginSeedphraseView.swift +++ b/app/network/Authenticate/LoginSeedphrase/LoginSeedphraseView.swift @@ -71,6 +71,16 @@ struct LoginSeedphraseView: View { Spacer().frame(height: 8) + if let wordCountWarning = viewModel.wordCountWarning { + Text(wordCountWarning) + .foregroundColor(.urYellow) + .font(themeManager.currentTheme.secondaryBodyFont) + .multilineTextAlignment(.center) + .padding(.horizontal) + + Spacer().frame(height: 8) + } + UrInlineErrorText(message: viewModel.errorMessage) } diff --git a/app/network/Authenticate/LoginSeedphrase/LoginSeedphraseViewModel.swift b/app/network/Authenticate/LoginSeedphrase/LoginSeedphraseViewModel.swift index a97d6271..2f1539bf 100644 --- a/app/network/Authenticate/LoginSeedphrase/LoginSeedphraseViewModel.swift +++ b/app/network/Authenticate/LoginSeedphrase/LoginSeedphraseViewModel.swift @@ -19,6 +19,8 @@ extension LoginSeedphraseView { } } + @Published private(set) var wordCountWarning: String? + @Published private(set) var isLoggingIn: Bool = false @Published private(set) var errorMessage: String? @@ -33,6 +35,33 @@ extension LoginSeedphraseView { !seedphrase.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty } + var normalizedSeedphrase: String { + seedphrase + .lowercased() + .trimmingCharacters(in: .whitespacesAndNewlines) + .components(separatedBy: .whitespacesAndNewlines) + .filter { !$0.isEmpty } + .joined(separator: " ") + } + + var wordCount: Int { + normalizedSeedphrase.isEmpty ? 0 : normalizedSeedphrase.components(separatedBy: " ").count + } + + func validateWordCount() -> Bool { + let count = wordCount + if count == 12 || count == 24 { + wordCountWarning = nil + return true + } + if count > 0 { + wordCountWarning = "Seedphrase should be 12 or 24 words (you entered \(count))" + } else { + wordCountWarning = nil + } + return false + } + func login() async -> AuthLoginResult { if isLoggingIn { @@ -43,6 +72,9 @@ extension LoginSeedphraseView { return .failure(NSError(domain: domain, code: 0, userInfo: [NSLocalizedDescriptionKey: "Seedphrase is empty"])) } + // Warn but don't block on word count — server handles validation + validateWordCount() + isLoggingIn = true errorMessage = nil @@ -51,7 +83,7 @@ extension LoginSeedphraseView { } do { - let result = try await urApiService.loginWithSeedphrase(seedphrase: seedphrase) + let result = try await urApiService.loginWithSeedphrase(seedphrase: normalizedSeedphrase) return result } catch { return .failure(error) From 7b90467f8a6aca780d23a92a5eefca4f8831a16e Mon Sep 17 00:00:00 2001 From: ryanmello07 Date: Wed, 15 Jul 2026 08:04:53 +0100 Subject: [PATCH 037/109] fix: resolve 6 Xcode build errors from CI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - CreateNetworkInstantView: fix onConfirmed closure missing parameter (SeedphraseDisplayView expects (String) -> Void) - SeedphraseDisplayView: break up complex LazyVGrid expression to avoid Swift compiler timeout - LoginInitialView: restore handleGoogleSignInButton() that was lost during guest mode cleanup rewrite - LoginSeedphraseViewModel: make errorMessage settable (not private(set)) so view can display errors - UrApiService: fix guard let binding — seedphrase is a String, not String? (use guard! + explicit let) --- .../CreateNetworkInstantView.swift | 2 +- .../SeedphraseDisplayView.swift | 3 +- .../LoginInitial/LoginInitialView.swift | 50 +++++++++++++++++++ .../LoginSeedphraseViewModel.swift | 6 ++- .../Services/UrApiService/UrApiService.swift | 4 +- 5 files changed, 61 insertions(+), 4 deletions(-) diff --git a/app/network/Authenticate/CreateNetworkInstant/CreateNetworkInstantView.swift b/app/network/Authenticate/CreateNetworkInstant/CreateNetworkInstantView.swift index 71a30b13..2b61f120 100644 --- a/app/network/Authenticate/CreateNetworkInstant/CreateNetworkInstantView.swift +++ b/app/network/Authenticate/CreateNetworkInstant/CreateNetworkInstantView.swift @@ -94,7 +94,7 @@ struct CreateNetworkInstantView: View { .fullScreenCover(item: $accountResult) { result in SeedphraseDisplayView( seedphrase: result.seedphrase, - onConfirmed: { + onConfirmed: { _ in accountResult = nil Task { await handleSuccess(result.jwt) diff --git a/app/network/Authenticate/CreateNetworkInstant/SeedphraseDisplayView.swift b/app/network/Authenticate/CreateNetworkInstant/SeedphraseDisplayView.swift index b544456f..548595d5 100644 --- a/app/network/Authenticate/CreateNetworkInstant/SeedphraseDisplayView.swift +++ b/app/network/Authenticate/CreateNetworkInstant/SeedphraseDisplayView.swift @@ -52,7 +52,8 @@ struct SeedphraseDisplayView: View { Spacer().frame(height: 32) // Seedphrase grid display - LazyVGrid(columns: Array(repeating: GridItem(.flexible(), spacing: 8), count: 2), spacing: 8) { + let gridColumns = Array(repeating: GridItem(.flexible(), spacing: 8), count: 2) + LazyVGrid(columns: gridColumns, spacing: 8) { ForEach(Array(words.enumerated()), id: \.offset) { index, word in HStack(spacing: 4) { Text("\(index + 1).") diff --git a/app/network/Authenticate/LoginInitial/LoginInitialView.swift b/app/network/Authenticate/LoginInitial/LoginInitialView.swift index 0d9dcc24..0a5fffaf 100644 --- a/app/network/Authenticate/LoginInitial/LoginInitialView.swift +++ b/app/network/Authenticate/LoginInitial/LoginInitialView.swift @@ -354,6 +354,56 @@ struct LoginInitialView: View { } + private func handleGoogleSignInButton() async { + + guard viewModel.beginLoginAction(.google) else { + return + } + + defer { + viewModel.endLoginAction(.google) + } + + do { + #if os(iOS) + + guard let rootViewController = getRootViewController() else { + print("no root view controller found") + viewModel.setLoginErrorMessage(String(localized: "There was an error logging in")) + return + } + + let signInResult = try await GIDSignIn.sharedInstance.signIn(withPresenting: rootViewController) + #elseif os(macOS) + + guard let presentingWindow = NSApplication.shared.windows.first else { + print("There is no presenting window!") + viewModel.setLoginErrorMessage(String(localized: "There was an error logging in")) + return + } + + let signInResult = try await GIDSignIn.sharedInstance.signIn(withPresenting: presentingWindow) + #endif + + let createArgsResult = viewModel.createGoogleAuthLoginArgs(signInResult) + switch createArgsResult { + case .success(let args): + let result = await viewModel.authLogin(args: args) + await self.handleAuthLoginResult(result) + + case .failure(let error): + print("error create args result: \(error.localizedDescription)") + viewModel.setLoginErrorMessage(String(localized: "There was an error logging in")) + } + + } catch { + print("Error signing in: \(error.localizedDescription)") + viewModel.setLoginErrorMessage(String(localized: "There was an error logging in")) + } + + + } + private func handleAuthLoginResult(_ authLoginResult: AuthLoginResult) async { switch authLoginResult { diff --git a/app/network/Authenticate/LoginSeedphrase/LoginSeedphraseViewModel.swift b/app/network/Authenticate/LoginSeedphrase/LoginSeedphraseViewModel.swift index 2f1539bf..3039b33d 100644 --- a/app/network/Authenticate/LoginSeedphrase/LoginSeedphraseViewModel.swift +++ b/app/network/Authenticate/LoginSeedphrase/LoginSeedphraseViewModel.swift @@ -23,7 +23,7 @@ extension LoginSeedphraseView { @Published private(set) var isLoggingIn: Bool = false - @Published private(set) var errorMessage: String? + @Published var errorMessage: String? let domain = "LoginSeedphraseViewModel" @@ -31,6 +31,10 @@ extension LoginSeedphraseView { self.urApiService = urApiService } + func setErrorMessage(_ message: String?) { + errorMessage = message + } + var isSeedphraseValid: Bool { !seedphrase.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty } diff --git a/app/network/Shared/Services/UrApiService/UrApiService.swift b/app/network/Shared/Services/UrApiService/UrApiService.swift index a65fe84a..fd28d5c4 100644 --- a/app/network/Shared/Services/UrApiService/UrApiService.swift +++ b/app/network/Shared/Services/UrApiService/UrApiService.swift @@ -465,11 +465,13 @@ extension UrApiService { return } - guard let seedphrase = result.seedphrase, !seedphrase.isEmpty else { + guard !result.seedphrase.isEmpty else { continuation.resume(throwing: NSError(domain: "UrApiService", code: -1, userInfo: [NSLocalizedDescriptionKey: "No seedphrase in result"])) return } + let seedphrase = result.seedphrase + if let network = result.network { switch self.nonEmptyJwt(network.byJwt, context: "createInstantAccount") { case .success(let jwt): From 7097b3e45fd7a86fd620255e760b1db7759cafaf Mon Sep 17 00:00:00 2001 From: ryanmello07 Date: Wed, 15 Jul 2026 08:51:23 +0100 Subject: [PATCH 038/109] fix: refactor SeedphraseDisplayView to resolve Swift compiler timeout Split the complex expression inside LazyVGrid into separate computed properties and helper functions so each sub-expression is small enough for the Swift type checker to handle independently. Changes: - SeedWord Identifiable struct replaces KeyPathIterator for ForEach - Extracted titleView, warningView, instructionsView, wordGridView, wordRowView, copyButton, confirmationButton as separate helpers - Use String(index + 1) instead of interpolation for numeric label --- .../SeedphraseDisplayView.swift | 158 ++++++++++-------- 1 file changed, 92 insertions(+), 66 deletions(-) diff --git a/app/network/Authenticate/CreateNetworkInstant/SeedphraseDisplayView.swift b/app/network/Authenticate/CreateNetworkInstant/SeedphraseDisplayView.swift index 548595d5..914d00ba 100644 --- a/app/network/Authenticate/CreateNetworkInstant/SeedphraseDisplayView.swift +++ b/app/network/Authenticate/CreateNetworkInstant/SeedphraseDisplayView.swift @@ -5,6 +5,11 @@ import SwiftUI +private struct SeedWord: Identifiable { + let id: Int + let word: String +} + struct SeedphraseDisplayView: View { @EnvironmentObject var themeManager: ThemeManager @@ -14,7 +19,7 @@ struct SeedphraseDisplayView: View { @State private var hasCopied = false - private let words: [String] + private let words: [SeedWord] init(seedphrase: String, onConfirmed: @escaping (String) -> Void) { // Normalize: trim whitespace and collapse multiple spaces @@ -25,7 +30,8 @@ struct SeedphraseDisplayView: View { .joined(separator: " ") self.seedphrase = normalized self.onConfirmed = onConfirmed - self.words = normalized.components(separatedBy: " ") + let tokens = normalized.components(separatedBy: " ") + self.words = tokens.enumerated().map { SeedWord(id: $0.offset, word: $0.element) } } var body: some View { @@ -33,76 +39,18 @@ struct SeedphraseDisplayView: View { ScrollView { VStack(alignment: .center) { - Text("Your Seedphrase") - .foregroundColor(.urWhite) - .font(themeManager.currentTheme.titleFont) - - Spacer().frame(height: 16) - - Text("⚠️ This is the ONLY time you'll see this.") - .foregroundColor(.urYellow) - .font(themeManager.currentTheme.bodyFont.bold()) - .multilineTextAlignment(.center) - - Text("Write it down and store it somewhere safe. If you lose it, you'll lose access to your account.") - .foregroundColor(themeManager.currentTheme.textMutedColor) - .font(themeManager.currentTheme.secondaryBodyFont) - .multilineTextAlignment(.center) + titleView + warningView + instructionsView Spacer().frame(height: 32) - // Seedphrase grid display - let gridColumns = Array(repeating: GridItem(.flexible(), spacing: 8), count: 2) - LazyVGrid(columns: gridColumns, spacing: 8) { - ForEach(Array(words.enumerated()), id: \.offset) { index, word in - HStack(spacing: 4) { - Text("\(index + 1).") - .foregroundColor(themeManager.currentTheme.textMutedColor) - .font(.system(.caption, design: .monospaced)) - .frame(width: 24, alignment: .trailing) - Text(word) - .foregroundColor(.urWhite) - .font(.system(.body, design: .monospaced)) - .lineLimit(1) - Spacer() - } - .padding(.vertical, 6) - .padding(.horizontal, 8) - .background(themeManager.currentTheme.surfaceColor) - .cornerRadius(6) - } - } - .padding(12) - .overlay( - RoundedRectangle(cornerRadius: 8) - .stroke(themeManager.currentTheme.textMutedColor.opacity(0.3), lineWidth: 1) - ) + wordGridView Spacer().frame(height: 24) - UrButton( - text: hasCopied ? "Copied!" : "Copy to Clipboard", - action: { - UIPasteboard.general.string = seedphrase - hasCopied = true - DispatchQueue.main.asyncAfter(deadline: .now() + 2) { - hasCopied = false - } - }, - enabled: !hasCopied - ) - - Spacer().frame(height: 16) - - UrButton( - text: "I've Saved My Seedphrase", - action: { - onConfirmed(seedphrase) - }, - enabled: true, - isProcessing: false - ) - .tint(.urGreen) + copyButton + confirmationButton } .padding() @@ -120,6 +68,84 @@ struct SeedphraseDisplayView: View { } } + private var titleView: some View { + Text("Your Seedphrase") + .foregroundColor(.urWhite) + .font(themeManager.currentTheme.titleFont) + } + + private var warningView: some View { + Text("⚠️ This is the ONLY time you'll see this.") + .foregroundColor(.urYellow) + .font(themeManager.currentTheme.bodyFont.bold()) + .multilineTextAlignment(.center) + } + + private var instructionsView: some View { + Text("Write it down and store it somewhere safe. If you lose it, you'll lose access to your account.") + .foregroundColor(themeManager.currentTheme.textMutedColor) + .font(themeManager.currentTheme.secondaryBodyFont) + .multilineTextAlignment(.center) + } + + private var wordGridView: some View { + let columns = [GridItem(.flexible(), spacing: 8), GridItem(.flexible(), spacing: 8)] + return LazyVGrid(columns: columns, spacing: 8) { + ForEach(words) { seedWord in + wordRowView(index: seedWord.id, word: seedWord.word) + } + } + .padding(12) + .overlay( + RoundedRectangle(cornerRadius: 8) + .stroke(themeManager.currentTheme.textMutedColor.opacity(0.3), lineWidth: 1) + ) + } + + private func wordRowView(index: Int, word: String) -> some View { + HStack(spacing: 4) { + Text(String(index + 1) + ".") + .foregroundColor(themeManager.currentTheme.textMutedColor) + .font(.system(.caption, design: .monospaced)) + .frame(width: 24, alignment: .trailing) + Text(word) + .foregroundColor(.urWhite) + .font(.system(.body, design: .monospaced)) + .lineLimit(1) + Spacer() + } + .padding(.vertical, 6) + .padding(.horizontal, 8) + .background(themeManager.currentTheme.surfaceColor) + .cornerRadius(6) + } + + private var copyButton: some View { + UrButton( + text: hasCopied ? "Copied!" : "Copy to Clipboard", + action: { + UIPasteboard.general.string = seedphrase + hasCopied = true + DispatchQueue.main.asyncAfter(deadline: .now() + 2) { + hasCopied = false + } + }, + enabled: !hasCopied + ) + } + + private var confirmationButton: some View { + UrButton( + text: "I've Saved My Seedphrase", + action: { + onConfirmed(seedphrase) + }, + enabled: true, + isProcessing: false + ) + .tint(.urGreen) + } + } #Preview { From 4326bee36808eeee2dea43635e53178d09401bb9 Mon Sep 17 00:00:00 2001 From: ryanmello07 Date: Wed, 15 Jul 2026 08:55:02 +0100 Subject: [PATCH 039/109] =?UTF-8?q?fix:=20apply=20subagent=20review=20find?= =?UTF-8?q?ings=20=E2=80=94=20fix=20maxWidth=20layout=20order?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Reorder .frame() modifiers so .infinity expands then 400 caps it - Keep clipboard copy without auto-clear (user needs time to paste) --- .../CreateNetworkInstant/SeedphraseDisplayView.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/network/Authenticate/CreateNetworkInstant/SeedphraseDisplayView.swift b/app/network/Authenticate/CreateNetworkInstant/SeedphraseDisplayView.swift index 914d00ba..b7ca7099 100644 --- a/app/network/Authenticate/CreateNetworkInstant/SeedphraseDisplayView.swift +++ b/app/network/Authenticate/CreateNetworkInstant/SeedphraseDisplayView.swift @@ -54,8 +54,8 @@ struct SeedphraseDisplayView: View { } .padding() - .frame(maxWidth: 400) .frame(maxWidth: .infinity) + .frame(maxWidth: 400) } .background(themeManager.currentTheme.backgroundColor.ignoresSafeArea()) .interactiveDismissDisabled(true) From f7677547410bb3c91a2623215a360ab54e901e4f Mon Sep 17 00:00:00 2001 From: ryanmello07 Date: Wed, 15 Jul 2026 09:06:18 +0100 Subject: [PATCH 040/109] fix: rename surfaceColor to tintedBackgroundBase to match Theme struct MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Theme struct doesn't have a surfaceColor property — the correct token for card/row backgrounds is tintedBackgroundBase. --- .../CreateNetworkInstant/SeedphraseDisplayView.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/network/Authenticate/CreateNetworkInstant/SeedphraseDisplayView.swift b/app/network/Authenticate/CreateNetworkInstant/SeedphraseDisplayView.swift index b7ca7099..c95c2e8f 100644 --- a/app/network/Authenticate/CreateNetworkInstant/SeedphraseDisplayView.swift +++ b/app/network/Authenticate/CreateNetworkInstant/SeedphraseDisplayView.swift @@ -116,7 +116,7 @@ struct SeedphraseDisplayView: View { } .padding(.vertical, 6) .padding(.horizontal, 8) - .background(themeManager.currentTheme.surfaceColor) + .background(themeManager.currentTheme.tintedBackgroundBase) .cornerRadius(6) } From 0fac0ac84da17bc7b253b0812da5e366b0c40e97 Mon Sep 17 00:00:00 2001 From: ryanmello07 Date: Wed, 15 Jul 2026 09:22:00 +0100 Subject: [PATCH 041/109] fix: resolve 5 macOS build errors with platform guards - CreateNetworkInstantView: wrap fullScreenCover + navBarBackButtonHidden under #if os(iOS) - CreateNetworkInstantView + LoginSeedphraseView: wrap navigationBarLeading under #if os(iOS)/#elseif os(macOS) - LoginInitialView: add presentSeedphraseLogin & presentCreateInstant args to iOS SSOButtons call - SeedphraseDisplayView: platform-guard UIPasteboard (iOS) / NSPasteboard (macOS) --- .../CreateNetworkInstantView.swift | 13 ++++++++++++- .../SeedphraseDisplayView.swift | 5 +++++ .../LoginInitial/LoginInitialView.swift | 2 ++ .../LoginSeedphrase/LoginSeedphraseView.swift | 13 ++++++++++++- 4 files changed, 31 insertions(+), 2 deletions(-) diff --git a/app/network/Authenticate/CreateNetworkInstant/CreateNetworkInstantView.swift b/app/network/Authenticate/CreateNetworkInstant/CreateNetworkInstantView.swift index 2b61f120..cd10d73a 100644 --- a/app/network/Authenticate/CreateNetworkInstant/CreateNetworkInstantView.swift +++ b/app/network/Authenticate/CreateNetworkInstant/CreateNetworkInstantView.swift @@ -78,18 +78,28 @@ struct CreateNetworkInstantView: View { } .padding() - .frame(maxWidth: 400) .frame(maxWidth: .infinity) + .frame(maxWidth: 400) } .background(themeManager.currentTheme.backgroundColor.ignoresSafeArea()) .toolbar { + #if os(iOS) ToolbarItem(placement: .navigationBarLeading) { Button(action: { back() }) { Image(systemName: "chevron.left") .foregroundColor(themeManager.currentTheme.textColor) } } + #elseif os(macOS) + ToolbarItem { + Button(action: { back() }) { + Image(systemName: "chevron.left") + .foregroundColor(themeManager.currentTheme.textColor) + } + } + #endif } + #if os(iOS) .navigationBarBackButtonHidden(true) .fullScreenCover(item: $accountResult) { result in SeedphraseDisplayView( @@ -103,6 +113,7 @@ struct CreateNetworkInstantView: View { ) .environmentObject(themeManager) } + #endif } } diff --git a/app/network/Authenticate/CreateNetworkInstant/SeedphraseDisplayView.swift b/app/network/Authenticate/CreateNetworkInstant/SeedphraseDisplayView.swift index c95c2e8f..90e503d3 100644 --- a/app/network/Authenticate/CreateNetworkInstant/SeedphraseDisplayView.swift +++ b/app/network/Authenticate/CreateNetworkInstant/SeedphraseDisplayView.swift @@ -124,7 +124,12 @@ struct SeedphraseDisplayView: View { UrButton( text: hasCopied ? "Copied!" : "Copy to Clipboard", action: { + #if os(iOS) UIPasteboard.general.string = seedphrase + #elseif os(macOS) + NSPasteboard.general.clearContents() + NSPasteboard.general.setString(seedphrase, forType: .string) + #endif hasCopied = true DispatchQueue.main.asyncAfter(deadline: .now() + 2) { hasCopied = false diff --git a/app/network/Authenticate/LoginInitial/LoginInitialView.swift b/app/network/Authenticate/LoginInitial/LoginInitialView.swift index 0a5fffaf..8a4a8931 100644 --- a/app/network/Authenticate/LoginInitial/LoginInitialView.swift +++ b/app/network/Authenticate/LoginInitial/LoginInitialView.swift @@ -635,6 +635,8 @@ private struct LoginInitialFormView: View { presentSignInWithSolanaSheet: presentSignInWithSolanaSheet, signInWithBittensor: signInWithBittensor, presentAuthCodeLoginSheet: presentAuthCodeLoginSheet, + presentSeedphraseLogin: presentSeedphraseLogin, + presentCreateInstant: presentCreateInstant, activeLoginAction: activeLoginAction, isLoginActionInFlight: isLoginActionInFlight ) diff --git a/app/network/Authenticate/LoginSeedphrase/LoginSeedphraseView.swift b/app/network/Authenticate/LoginSeedphrase/LoginSeedphraseView.swift index adef85a2..44923b83 100644 --- a/app/network/Authenticate/LoginSeedphrase/LoginSeedphraseView.swift +++ b/app/network/Authenticate/LoginSeedphrase/LoginSeedphraseView.swift @@ -85,19 +85,30 @@ struct LoginSeedphraseView: View { } .padding() - .frame(maxWidth: 400) .frame(maxWidth: .infinity) + .frame(maxWidth: 400) } .background(themeManager.currentTheme.backgroundColor.ignoresSafeArea()) .toolbar { + #if os(iOS) ToolbarItem(placement: .navigationBarLeading) { Button(action: { back() }) { Image(systemName: "chevron.left") .foregroundColor(themeManager.currentTheme.textColor) } } + #elseif os(macOS) + ToolbarItem { + Button(action: { back() }) { + Image(systemName: "chevron.left") + .foregroundColor(themeManager.currentTheme.textColor) + } + } + #endif } + #if os(iOS) .navigationBarBackButtonHidden(true) + #endif } private func handleResult(_ result: AuthLoginResult) async { From e4b37f5b1ea5819b01479666ac43d8cac56e73b8 Mon Sep 17 00:00:00 2001 From: ryanmello07 Date: Wed, 15 Jul 2026 09:56:41 +0100 Subject: [PATCH 042/109] fix: add presentSeedphraseLogin and presentCreateInstant to iOS SSOButtons struct MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The shared SSOButtons() call site in LoginInitialFormView now passes the two new closure parameters, but the iOS SSOButtons struct didn't declare them — only the macOS one did. --- app/network/Authenticate/LoginInitial/LoginInitialView.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/network/Authenticate/LoginInitial/LoginInitialView.swift b/app/network/Authenticate/LoginInitial/LoginInitialView.swift index 8a4a8931..1f1ff120 100644 --- a/app/network/Authenticate/LoginInitial/LoginInitialView.swift +++ b/app/network/Authenticate/LoginInitial/LoginInitialView.swift @@ -751,6 +751,8 @@ private struct SSOButtons: View { let presentSignInWithSolanaSheet: () -> Void let signInWithBittensor: () -> Void let presentAuthCodeLoginSheet: () -> Void + let presentSeedphraseLogin: () -> Void + let presentCreateInstant: () -> Void let activeLoginAction: LoginInitialView.LoginAction? let isLoginActionInFlight: Bool From 3fa50e10ec46c259b7f2021049c73a851b55bb20 Mon Sep 17 00:00:00 2001 From: ryanmello07 Date: Thu, 16 Jul 2026 21:07:42 +0100 Subject: [PATCH 043/109] ci: upload IPA + macOS artifacts to PR checks Add upload-artifact steps so PR builds produce downloadable IPA (URnetwork-beta.ipa) and macOS zip that can be tested before merging. Artifacts are retained for 7 days. --- .github/workflows/beta-build.yml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/.github/workflows/beta-build.yml b/.github/workflows/beta-build.yml index d0954b5e..a894d8c7 100644 --- a/.github/workflows/beta-build.yml +++ b/.github/workflows/beta-build.yml @@ -121,6 +121,22 @@ jobs: fi ditto -c -k --sequesterRsrc --keepParent "$APP" URnetwork-beta-macOS.zip + - name: Upload IPA artifact + uses: actions/upload-artifact@v4 + with: + name: URnetwork-beta-ios + path: urnetwork-ios/app/URnetwork-beta.ipa + if-no-files-found: error + retention-days: 7 + + - name: Upload macOS artifact + uses: actions/upload-artifact@v4 + with: + name: URnetwork-beta-macOS + path: urnetwork-ios/app/URnetwork-beta-macOS.zip + if-no-files-found: error + retention-days: 7 + - name: Create prerelease env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} From b3e1fd74074f60514bea2d923928e7b132718c5e Mon Sep 17 00:00:00 2001 From: ryanmello07 Date: Thu, 16 Jul 2026 21:14:53 +0100 Subject: [PATCH 044/109] ci: upload IPA + macOS artifacts to PR checks --- .github/workflows/beta-build.yml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/.github/workflows/beta-build.yml b/.github/workflows/beta-build.yml index d0954b5e..a894d8c7 100644 --- a/.github/workflows/beta-build.yml +++ b/.github/workflows/beta-build.yml @@ -121,6 +121,22 @@ jobs: fi ditto -c -k --sequesterRsrc --keepParent "$APP" URnetwork-beta-macOS.zip + - name: Upload IPA artifact + uses: actions/upload-artifact@v4 + with: + name: URnetwork-beta-ios + path: urnetwork-ios/app/URnetwork-beta.ipa + if-no-files-found: error + retention-days: 7 + + - name: Upload macOS artifact + uses: actions/upload-artifact@v4 + with: + name: URnetwork-beta-macOS + path: urnetwork-ios/app/URnetwork-beta-macOS.zip + if-no-files-found: error + retention-days: 7 + - name: Create prerelease env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} From da13f2afd8d63f3e4716897df0a42a065bbb20a6 Mon Sep 17 00:00:00 2001 From: ryanmello07 Date: Thu, 16 Jul 2026 22:56:07 +0100 Subject: [PATCH 045/109] feat: add account management UI for seedphrase, auth methods, and network name - Add 6 new SDK service methods: generateSeedphrase, regenerateSeedphrase, addAuth, removeAuth, changeNetworkName, claimNetworkName - Remove upgradeGuest and all guest mode references - Add Seedphrase Management section to Settings (generate/regenerate) - Add Sign-In Methods section to Settings (list, add, remove auth methods) - Add AddAuthSheet for adding email/seedphrase sign-in methods - Add editable network name field to ProfileView with claim support - Update MockUrApiService with stub implementations - Pass NetworkUserViewModel into Settings for auth method display - Update AccountNavStack to pass authType to ProfileView --- .../CreateNetwork/CreateNetworkView.swift | 11 +- .../CreateNetworkViewModel.swift | 52 ---- .../Main/Account/AccountNavStackView.swift | 4 +- .../Main/Account/Profile/ProfileView.swift | 105 ++++++-- .../Account/Profile/ProfileViewModel.swift | 132 ++++++++++ .../Main/Account/Settings/AddAuthSheet.swift | 136 ++++++++++ .../Account/Settings/SettingsForm-iOS.swift | 131 ++++++++++ .../Main/Account/Settings/SettingsView.swift | 52 +++- .../Account/Settings/SettingsViewModel.swift | 89 ++++++- .../UrApiService/MockUrApiService.swift | 28 ++- .../Services/UrApiService/UrApiService.swift | 232 +++++++++++++++--- .../UrApiService/UrApiServiceProtocol.swift | 19 +- 12 files changed, 874 insertions(+), 117 deletions(-) create mode 100644 app/network/Main/Account/Settings/AddAuthSheet.swift diff --git a/app/network/Authenticate/CreateNetwork/CreateNetworkView.swift b/app/network/Authenticate/CreateNetwork/CreateNetworkView.swift index f3091c10..3ecc5ad4 100644 --- a/app/network/Authenticate/CreateNetwork/CreateNetworkView.swift +++ b/app/network/Authenticate/CreateNetwork/CreateNetworkView.swift @@ -188,16 +188,7 @@ struct CreateNetworkView: View { #endif Task { - let result = deviceManager.device != nil - // device exists - upgrade guest network - ? await viewModel.upgradeGuestNetwork( - userAuth: userAuth, - authJwt: authLoginArgs.authJwt, - authType: authLoginArgs.authJwtType, - walletAuth: authLoginArgs.walletAuth - ) - // no device exists - create a new network - : await viewModel.createNetwork( + let result = await viewModel.createNetwork( userAuth: userAuth, authJwt: authLoginArgs.authJwt, authType: authLoginArgs.authJwtType, diff --git a/app/network/Authenticate/CreateNetwork/CreateNetworkViewModel.swift b/app/network/Authenticate/CreateNetwork/CreateNetworkViewModel.swift index d133425e..a77bd0ea 100644 --- a/app/network/Authenticate/CreateNetwork/CreateNetworkViewModel.swift +++ b/app/network/Authenticate/CreateNetwork/CreateNetworkViewModel.swift @@ -247,58 +247,6 @@ extension CreateNetworkView { } - func upgradeGuestNetwork( - userAuth: String?, - authJwt: String?, - authType: String?, - walletAuth: SdkWalletAuthArgs? - ) async -> LoginNetworkResult { - - if !formIsValid { - return .failure(NSError(domain: domain, code: 0, userInfo: [NSLocalizedDescriptionKey: "Create network form is invalid"])) - } - - if isCreatingNetwork { - return .failure(NSError(domain: domain, code: 0, userInfo: [NSLocalizedDescriptionKey: "Network creation already in progress"])) - } - - - self.createNetworkErrorMessage = nil - self.isCreatingNetwork = true - - do { - - let args = SdkUpgradeGuestArgs() - args.networkName = networkName.trimmingCharacters(in: .whitespacesAndNewlines) - - if let userAuth = userAuth { - args.userAuth = userAuth - args.password = password - } - - if let authJwt, let authType { - args.authJwt = authJwt - args.authJwtType = authType - } - - if let walletAuth { - args.walletAuth = walletAuth - } - - let result = try await urApiService.upgradeGuest(args) - - self.isCreatingNetwork = false - - return result - - } catch { - self.isCreatingNetwork = false - return .failure(error) - } - - - } - func createNetwork( userAuth: String?, authJwt: String?, diff --git a/app/network/Main/Account/AccountNavStackView.swift b/app/network/Main/Account/AccountNavStackView.swift index 1aa85852..7c24e142 100644 --- a/app/network/Main/Account/AccountNavStackView.swift +++ b/app/network/Main/Account/AccountNavStackView.swift @@ -105,7 +105,8 @@ struct AccountNavStackView: View { api: api, back: viewModel.back, networkName: networkName, - userAuth: networkUserViewModel.networkUser?.userAuth + userAuth: networkUserViewModel.networkUser?.userAuth, + authType: networkUserViewModel.networkUser?.authType ) .background(themeManager.currentTheme.backgroundColor) .navigationTitle("Profile") @@ -119,6 +120,7 @@ struct AccountNavStackView: View { accountWalletsViewModel: accountWalletsViewModel, navigate: viewModel.navigate, providerCountries: providerCountries, + networkUserViewModel: networkUserViewModel ) .background(themeManager.currentTheme.backgroundColor.ignoresSafeArea()) .navigationTitle("Settings") diff --git a/app/network/Main/Account/Profile/ProfileView.swift b/app/network/Main/Account/Profile/ProfileView.swift index 77fdeae4..4b026afd 100644 --- a/app/network/Main/Account/Profile/ProfileView.swift +++ b/app/network/Main/Account/Profile/ProfileView.swift @@ -18,42 +18,103 @@ struct ProfileView: View { var back: () -> Void var networkName: String? var userAuth: String? + var authType: String? - init(api: SdkApi, back: @escaping () -> Void, networkName: String?, userAuth: String?) { + init(api: SdkApi, back: @escaping () -> Void, networkName: String?, userAuth: String?, authType: String? = nil) { _viewModel = StateObject.init(wrappedValue: ViewModel( api: api )) self.back = back self.userAuth = userAuth self.networkName = networkName + self.authType = authType } var body: some View { VStack { - -// HStack { -// Text("Profile") -// .font(themeManager.currentTheme.titleFont) -// .foregroundColor(themeManager.currentTheme.textColor) -// -// Spacer() -// } -// -// Spacer().frame(height: 64) - + HStack { UrLabel(text: "Network name") Spacer() } - HStack { - Text(networkName ?? "") - .font(themeManager.currentTheme.bodyFont) - .foregroundColor(themeManager.currentTheme.textColor) + if viewModel.isEditingNetworkName { + // Editable view + HStack { + TextField("Enter network name", text: $viewModel.editedNetworkName) + .textFieldStyle(.plain) + .font(themeManager.currentTheme.bodyFont) + .foregroundColor(themeManager.currentTheme.textColor) + .autocorrectionDisabled() + #if os(iOS) + .autocapitalization(.none) + #endif + } + .padding() + .background(themeManager.currentTheme.tintedBackgroundBase) + .cornerRadius(8) - Spacer() + if let error = viewModel.networkNameError { + Text(error) + .font(themeManager.currentTheme.secondaryBodyFont) + .foregroundColor(.urRed) + } + + HStack(spacing: 12) { + UrButton( + text: "Save", + action: { + Task { + if authType == "seedphrase" { + let result = await viewModel.claimNetworkName() + handleNetworkNameResult(result) + } else { + let result = await viewModel.saveNetworkName() + handleNetworkNameResult(result) + } + } + }, + enabled: !viewModel.isSavingNetworkName && !viewModel.editedNetworkName.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty, + isProcessing: viewModel.isSavingNetworkName + ) + + Button(action: { + viewModel.cancelEditingNetworkName() + }) { + Text("Cancel") + } + } + } else { + // Read-only view + HStack { + Text(networkName ?? "") + .font(themeManager.currentTheme.bodyFont) + .foregroundColor(themeManager.currentTheme.textColor) + + Spacer() + + Button(action: { + viewModel.startEditingNetworkName(currentName: networkName ?? "") + }) { + Image(systemName: "pencil") + .foregroundColor(themeManager.currentTheme.textMutedColor) + } + } + + HStack { + if authType == "seedphrase" { + Text("Claim a custom network name to replace your auto-generated one") + .font(themeManager.currentTheme.secondaryBodyFont) + .foregroundColor(themeManager.currentTheme.textMutedColor) + } else { + Text("Tap the edit icon to change your network name") + .font(themeManager.currentTheme.secondaryBodyFont) + .foregroundColor(themeManager.currentTheme.textMutedColor) + } + Spacer() + } } Spacer().frame(height: 32) @@ -97,6 +158,16 @@ struct ProfileView: View { snackbarManager.showSnackbar(message: String(localized: "Error sending password reset link")) } } + + private func handleNetworkNameResult(_ result: Result) { + switch result { + case .success(let newName): + snackbarManager.showSnackbar(message: String(localized: "Network name changed to \(newName)")) + case .failure(let error): + print("Error changing network name: \(error)") + snackbarManager.showSnackbar(message: String(localized: "Error changing network name")) + } + } } #Preview { diff --git a/app/network/Main/Account/Profile/ProfileViewModel.swift b/app/network/Main/Account/Profile/ProfileViewModel.swift index 3eb0567c..b1432155 100644 --- a/app/network/Main/Account/Profile/ProfileViewModel.swift +++ b/app/network/Main/Account/Profile/ProfileViewModel.swift @@ -18,6 +18,11 @@ extension ProfileView { @Published private(set) var isSendingPasswordResetLink: Bool = false @Published private(set) var sendPasswordResetLinkError: String? + @Published var isEditingNetworkName: Bool = false + @Published var editedNetworkName: String = "" + @Published private(set) var isSavingNetworkName: Bool = false + @Published private(set) var networkNameError: String? + init(api: SdkApi) { self.api = api } @@ -70,6 +75,116 @@ extension ProfileView { } + func startEditingNetworkName(currentName: String) { + editedNetworkName = currentName + isEditingNetworkName = true + networkNameError = nil + } + + func saveNetworkName() async -> Result { + + let name = editedNetworkName.trimmingCharacters(in: .whitespacesAndNewlines) + guard !name.isEmpty else { + return .failure(NetworkNameError.empty) + } + + isSavingNetworkName = true + networkNameError = nil + + do { + let result: SdkChangeNetworkNameResult = try await withCheckedThrowingContinuation { continuation in + + let callback = ChangeNetworkNameCallback { result, err in + + if let err = err { + continuation.resume(throwing: err) + return + } + + guard let result = result else { + continuation.resume(throwing: NetworkNameError.resultNil) + return + } + + if let errMsg = result.error?.message { + continuation.resume(throwing: NSError(domain: "ProfileViewModel", code: -1, userInfo: [NSLocalizedDescriptionKey: errMsg])) + return + } + + continuation.resume(returning: result) + } + + let args = SdkChangeNetworkNameArgs() + args.newName = name + api.changeNetworkName(args, callback: callback) + } + + self.isSavingNetworkName = false + self.isEditingNetworkName = false + return .success(result.networkName) + + } catch(let error) { + self.isSavingNetworkName = false + self.networkNameError = error.localizedDescription + return .failure(error) + } + } + + func claimNetworkName() async -> Result { + + let name = editedNetworkName.trimmingCharacters(in: .whitespacesAndNewlines) + guard !name.isEmpty else { + return .failure(NetworkNameError.empty) + } + + isSavingNetworkName = true + networkNameError = nil + + do { + let result: SdkClaimNetworkNameResult = try await withCheckedThrowingContinuation { continuation in + + let callback = ClaimNetworkNameCallback { result, err in + + if let err = err { + continuation.resume(throwing: err) + return + } + + guard let result = result else { + continuation.resume(throwing: NetworkNameError.resultNil) + return + } + + if let errMsg = result.error?.message { + continuation.resume(throwing: NSError(domain: "ProfileViewModel", code: -1, userInfo: [NSLocalizedDescriptionKey: errMsg])) + return + } + + continuation.resume(returning: result) + } + + let args = SdkClaimNetworkNameArgs() + args.newName = name + api.claimNetworkName(args, callback: callback) + } + + self.isSavingNetworkName = false + self.isEditingNetworkName = false + return .success(result.networkName) + + } catch(let error) { + self.isSavingNetworkName = false + self.networkNameError = error.localizedDescription + return .failure(error) + } + } + + func cancelEditingNetworkName() { + isEditingNetworkName = false + editedNetworkName = "" + networkNameError = nil + } + } } @@ -79,8 +194,25 @@ enum SendPasswordResetLinkError: Error { case resultInvalid } +enum NetworkNameError: Error { + case empty + case resultNil +} + private class AuthPasswordResetCallback: SdkCallback, SdkAuthPasswordResetCallbackProtocol { func result(_ result: SdkAuthPasswordResetResult?, err: Error?) { handleResult(result, err: err) } } + +private class ChangeNetworkNameCallback: SdkCallback, SdkChangeNetworkNameCallbackProtocol { + func result(_ result: SdkChangeNetworkNameResult?, err: Error?) { + handleResult(result, err: err) + } +} + +private class ClaimNetworkNameCallback: SdkCallback, SdkClaimNetworkNameCallbackProtocol { + func result(_ result: SdkClaimNetworkNameResult?, err: Error?) { + handleResult(result, err: err) + } +} diff --git a/app/network/Main/Account/Settings/AddAuthSheet.swift b/app/network/Main/Account/Settings/AddAuthSheet.swift new file mode 100644 index 00000000..b97a87b5 --- /dev/null +++ b/app/network/Main/Account/Settings/AddAuthSheet.swift @@ -0,0 +1,136 @@ +// +// AddAuthSheet.swift +// URnetwork +// + +import SwiftUI +import URnetworkSdk + +struct AddAuthSheet: View { + + @EnvironmentObject var themeManager: ThemeManager + @EnvironmentObject var snackbarManager: UrSnackbarManager + + let api: UrApiServiceProtocol + + @State private var email: String = "" + @State private var password: String = "" + @State private var isAdding: Bool = false + @State private var selectedMethod: String = "email" + @State private var addError: String? + + var body: some View { + NavigationStack { + ScrollView { + VStack(alignment: .leading, spacing: 16) { + + Text("Add a sign-in method") + .font(themeManager.currentTheme.titleFont) + .foregroundColor(themeManager.currentTheme.textColor) + + Text("Link another way to sign in to your account.") + .font(themeManager.currentTheme.secondaryBodyFont) + .foregroundColor(themeManager.currentTheme.textMutedColor) + + Spacer().frame(height: 16) + + Picker("Method", selection: $selectedMethod) { + Text("Email").tag("email") + Text("Seedphrase").tag("seedphrase") + } + .pickerStyle(.segmented) + + if selectedMethod == "email" { + UrTextField( + text: $email, + label: "Email", + placeholder: "your@email.com", + keyboardType: .emailAddress, + autocapitalization: .never + ) + + UrTextField( + text: $password, + label: "Password", + placeholder: "Enter a password", + isSecure: true + ) + + Text("Password must be at least 12 characters") + .font(themeManager.currentTheme.secondaryBodyFont) + .foregroundColor(themeManager.currentTheme.textMutedColor) + } else { + Text("A new seedphrase will be generated for this account.") + .font(themeManager.currentTheme.secondaryBodyFont) + .foregroundColor(themeManager.currentTheme.textMutedColor) + } + + if let error = addError { + Text(error) + .font(themeManager.currentTheme.secondaryBodyFont) + .foregroundColor(.urRed) + } + + Spacer().frame(height: 16) + + UrButton( + text: "Add Sign-In Method", + action: { + Task { + await addAuth() + } + }, + enabled: !isAdding && formValid, + isProcessing: isAdding + ) + } + .padding() + } + .background(themeManager.currentTheme.backgroundColor.ignoresSafeArea()) + .toolbar { + ToolbarItem(placement: .confirmationAction) { + Button("Done") { + dismiss() + } + } + } + } + } + + @Environment(\.dismiss) private var dismiss + + private var formValid: Bool { + if selectedMethod == "email" { + return !email.isEmpty && password.count >= 12 + } + return true + } + + private func addAuth() async { + isAdding = true + addError = nil + + do { + let args = SdkAddAuthArgs() + + if selectedMethod == "email" { + args.userAuth = email + args.password = password + } + // seedphrase is generated server-side, no args needed + + let _ = try await api.addAuth(args) + isAdding = false + snackbarManager.showSnackbar(message: String(localized: "Sign-in method added successfully")) + dismiss() + } catch(let error) { + isAdding = false + addError = error.localizedDescription + } + } +} + +//#Preview { +// AddAuthSheet(api: MockUrApiService()) +// .environmentObject(ThemeManager.shared) +//} diff --git a/app/network/Main/Account/Settings/SettingsForm-iOS.swift b/app/network/Main/Account/Settings/SettingsForm-iOS.swift index 9568c62d..3cd13c8a 100644 --- a/app/network/Main/Account/Settings/SettingsForm-iOS.swift +++ b/app/network/Main/Account/Settings/SettingsForm-iOS.swift @@ -37,6 +37,9 @@ struct SettingsForm_iOS: View { @Binding var canReceiveNotifications: Bool @Binding var canReceiveProductUpdates: Bool + let networkUserViewModel: NetworkUserViewModel? + let viewModel: SettingsView.ViewModel + var body: some View { Form { @@ -137,6 +140,99 @@ struct SettingsForm_iOS: View { } } + // MARK: - Seedphrase Management + + Section("Seedphrase") { + VStack(alignment: .leading, spacing: 8) { + if let networkUser = networkUserViewModel?.networkUser { + if hasSeedphrase(networkUser) { + Button(action: { + viewModel.confirmRegenerateSeedphrase() + }) { + HStack { + Text("Regenerate Seedphrase") + .font(themeManager.currentTheme.bodyFont) + Spacer() + if viewModel.isRegeneratingSeedphrase { + ProgressView() + } + } + } + .disabled(viewModel.isRegeneratingSeedphrase) + } else { + Button(action: { + viewModel.confirmGenerateSeedphrase() + }) { + HStack { + Text("Generate Seedphrase") + .font(themeManager.currentTheme.bodyFont) + Spacer() + if viewModel.isGeneratingSeedphrase { + ProgressView() + } + } + } + .disabled(viewModel.isGeneratingSeedphrase) + } + } else { + Button(action: { + viewModel.confirmGenerateSeedphrase() + }) { + HStack { + Text("Generate Seedphrase") + .font(themeManager.currentTheme.bodyFont) + Spacer() + if viewModel.isGeneratingSeedphrase { + ProgressView() + } + } + } + .disabled(viewModel.isGeneratingSeedphrase) + } + + Text("A seedphrase lets you recover your account if you lose access.") + .font(themeManager.currentTheme.secondaryBodyFont) + .foregroundColor(themeManager.currentTheme.textMutedColor) + } + } + + // MARK: - Sign-In Methods + + Section("Sign-In Methods") { + if let networkUser = networkUserViewModel?.networkUser { + let authMethods = parseAuthMethods(networkUser) + + ForEach(authMethods, id: \.self) { method in + HStack { + Text(methodDisplayName(method)) + .font(themeManager.currentTheme.bodyFont) + Spacer() + if authMethods.count > 1 { + Button(role: .destructive) { + viewModel.presentRemoveAuth(method) + } label: { + Text("Remove") + } + } + } + } + + Button(action: { + viewModel.presentAddAuthSheet = true + }) { + Text("Add sign-in method") + } + } else { + HStack { + Text("Loading sign-in methods...") + .font(themeManager.currentTheme.secondaryBodyFont) + .foregroundColor(themeManager.currentTheme.textMutedColor) + Spacer() + ProgressView() + } + } + } + Section("Account") { /** * Update referral code @@ -405,6 +501,41 @@ struct SettingsForm_iOS: View { .scrollContentBackground(.hidden) .background(themeManager.currentTheme.backgroundColor) } + + // MARK: - Helpers + + private func hasSeedphrase(_ networkUser: SdkNetworkUser) -> Bool { + return networkUser.authType == "seedphrase" + } + + private func parseAuthMethods(_ networkUser: SdkNetworkUser) -> [String] { + var methods: [String] = [] + let authType = networkUser.authType + if !authType.isEmpty { methods.append(authType) } + // The SDK SdkNetworkUser currently only exposes a single authType. + // Future expansion could read from an array returned by the endpoint. + // For now the user's primary auth type is shown. + + // If there's a userAuth for email, include "email" as a method + if let userAuth = networkUser.userAuth, !userAuth.isEmpty { + if !methods.contains("email") { + methods.append("email") + } + } + + return methods.isEmpty ? ["seedphrase"] : methods + } + + private func methodDisplayName(_ method: String) -> String { + switch method { + case "email": return "Email" + case "google": return "Google" + case "apple": return "Apple" + case "solana": return "Solana Wallet" + case "seedphrase": return "Seedphrase" + default: return method.capitalized + } + } } #endif diff --git a/app/network/Main/Account/Settings/SettingsView.swift b/app/network/Main/Account/Settings/SettingsView.swift index acf9fb3f..297934cf 100644 --- a/app/network/Main/Account/Settings/SettingsView.swift +++ b/app/network/Main/Account/Settings/SettingsView.swift @@ -27,6 +27,7 @@ struct SettingsView: View { let api: UrApiServiceProtocol let navigate: (AccountNavigationPath) -> Void let providerCountries: [SdkConnectLocation] + let networkUserViewModel: NetworkUserViewModel? init( api: UrApiServiceProtocol, @@ -35,7 +36,8 @@ struct SettingsView: View { referralLinkViewModel: ReferralLinkViewModel, accountWalletsViewModel: AccountWalletsViewModel, navigate: @escaping (AccountNavigationPath) -> Void, - providerCountries: [SdkConnectLocation] + providerCountries: [SdkConnectLocation], + networkUserViewModel: NetworkUserViewModel? = nil ) { _viewModel = StateObject(wrappedValue: ViewModel(api: api)) self.clientId = clientId @@ -45,6 +47,7 @@ struct SettingsView: View { self.navigate = navigate self.providerCountries = providerCountries self.api = api + self.networkUserViewModel = networkUserViewModel } var body: some View { @@ -77,6 +80,8 @@ struct SettingsView: View { presentRenameDevice: viewModel.presentRenameDevice, canReceiveNotifications: $viewModel.canReceiveNotifications, canReceiveProductUpdates: $accountPreferencesViewModel.canReceiveProductUpdates, + networkUserViewModel: networkUserViewModel, + viewModel: viewModel, ) .background(themeManager.currentTheme.backgroundColor) .task { @@ -172,7 +177,50 @@ struct SettingsView: View { .presentationDetents([.height(268)]) .presentationDragIndicator(.visible) } - + .sheet(isPresented: $viewModel.presentSeedphraseSheet) { + SeedphraseDisplayView( + seedphrase: viewModel.generatedSeedphrase, + onConfirmed: { _ in + viewModel.dismissSeedphraseSheet() + } + ) + .environmentObject(themeManager) + } + .confirmationDialog( + "Generate a recovery seedphrase?", + isPresented: $viewModel.presentSeedphraseConfirmation, + titleVisibility: .visible + ) { + Button("Generate") { + Task { + await viewModel.executeGenerateSeedphrase() + } + } + Button("Cancel", role: .cancel) {} + } message: { + Text("A seedphrase lets you recover your account if you lose access. Store it safely.") + } + .confirmationDialog( + "Remove this sign-in method?", + isPresented: $viewModel.presentRemoveAuthConfirmation, + titleVisibility: .visible + ) { + Button("Remove", role: .destructive) { + Task { + await viewModel.executeRemoveAuth() + } + } + Button("Cancel", role: .cancel) {} + } message: { + if let authType = viewModel.authTypeToRemove { + Text("Are you sure you want to remove \(authType) as a sign-in method?") + } + } + .sheet(isPresented: $viewModel.presentAddAuthSheet) { + AddAuthSheet(api: api) + .environmentObject(themeManager) + } + #elseif os(macOS) SettingsForm_macOS( urApiService: api, diff --git a/app/network/Main/Account/Settings/SettingsViewModel.swift b/app/network/Main/Account/Settings/SettingsViewModel.swift index cf9f5151..5ad14b8d 100644 --- a/app/network/Main/Account/Settings/SettingsViewModel.swift +++ b/app/network/Main/Account/Settings/SettingsViewModel.swift @@ -243,7 +243,6 @@ extension SettingsView { } catch(let error) { print("\(domain) Error fetching transfer stats: \(error)") - // isLoadingTransferStats = false } } @@ -274,6 +273,94 @@ extension SettingsView { } #endif + // MARK: - Seedphrase Management + + @Published var presentSeedphraseSheet: Bool = false + @Published var generatedSeedphrase: String = "" + @Published var isGeneratingSeedphrase: Bool = false + @Published var isRegeneratingSeedphrase: Bool = false + @Published var presentSeedphraseConfirmation: Bool = false + @Published var isRegenerating: Bool = false + @Published var seedphraseError: String? + + func confirmGenerateSeedphrase() { + presentSeedphraseConfirmation = true + } + + func confirmRegenerateSeedphrase() { + presentSeedphraseConfirmation = true + } + + func executeGenerateSeedphrase() async { + isGeneratingSeedphrase = true + seedphraseError = nil + + do { + let result = try await api.generateSeedphrase() + self.generatedSeedphrase = result.seedphrase + self.isGeneratingSeedphrase = false + self.presentSeedphraseConfirmation = false + self.presentSeedphraseSheet = true + } catch(let error) { + self.isGeneratingSeedphrase = false + self.seedphraseError = error.localizedDescription + self.presentSeedphraseConfirmation = false + } + } + + func executeRegenerateSeedphrase() async { + isRegeneratingSeedphrase = true + seedphraseError = nil + + do { + let result = try await api.regenerateSeedphrase() + self.generatedSeedphrase = result.seedphrase + self.isRegeneratingSeedphrase = false + self.presentSeedphraseConfirmation = false + self.presentSeedphraseSheet = true + } catch(let error) { + self.isRegeneratingSeedphrase = false + self.seedphraseError = error.localizedDescription + self.presentSeedphraseConfirmation = false + } + } + + func dismissSeedphraseSheet() { + self.presentSeedphraseSheet = false + self.generatedSeedphrase = "" + } + + // MARK: - Auth Method Management + + @Published var presentAddAuthSheet: Bool = false + @Published var presentRemoveAuthConfirmation: Bool = false + @Published var authTypeToRemove: String? + @Published var isAddingAuth: Bool = false + @Published var isRemovingAuth: Bool = false + @Published var addAuthError: String? + @Published var removeAuthError: String? + + func presentRemoveAuth(_ authType: String) { + self.authTypeToRemove = authType + self.presentRemoveAuthConfirmation = true + } + + func executeRemoveAuth() async { + guard let authType = authTypeToRemove else { return } + isRemovingAuth = true + removeAuthError = nil + + do { + let _ = try await api.removeAuth(authType: authType) + self.isRemovingAuth = false + self.presentRemoveAuthConfirmation = false + self.authTypeToRemove = nil + } catch(let error) { + self.isRemovingAuth = false + self.removeAuthError = error.localizedDescription + } + } + } } diff --git a/app/network/Shared/Services/UrApiService/MockUrApiService.swift b/app/network/Shared/Services/UrApiService/MockUrApiService.swift index 997c2f5a..dc338996 100644 --- a/app/network/Shared/Services/UrApiService/MockUrApiService.swift +++ b/app/network/Shared/Services/UrApiService/MockUrApiService.swift @@ -49,10 +49,6 @@ class MockUrApiService: UrApiServiceProtocol { SdkValidateReferralCodeResult() } - func upgradeGuest(_ args: SdkUpgradeGuestArgs) async throws -> LoginNetworkResult { - return LoginNetworkResult.failure(LoginError.appleLoginFailed) - } - func fetchSubscriptionBalance() async throws -> SdkSubscriptionBalanceResult { return SdkSubscriptionBalanceResult() } @@ -105,6 +101,30 @@ class MockUrApiService: UrApiServiceProtocol { return ("mock-jwt", "mock seedphrase words here for testing purposes only") } + func generateSeedphrase() async throws -> SdkGenerateSeedphraseResult { + return SdkGenerateSeedphraseResult() + } + + func regenerateSeedphrase() async throws -> SdkRegenerateSeedphraseResult { + return SdkRegenerateSeedphraseResult() + } + + func addAuth(_ args: SdkAddAuthArgs) async throws -> SdkAddAuthResult { + return SdkAddAuthResult() + } + + func removeAuth(authType: String) async throws -> SdkRemoveAuthResult { + return SdkRemoveAuthResult() + } + + func changeNetworkName(_ newName: String) async throws -> SdkChangeNetworkNameResult { + return SdkChangeNetworkNameResult() + } + + func claimNetworkName(_ newName: String) async throws -> SdkClaimNetworkNameResult { + return SdkClaimNetworkNameResult() + } + func redeemBalanceCode(_ code: String) async throws -> SdkRedeemBalanceCodeResult { return SdkRedeemBalanceCodeResult() } diff --git a/app/network/Shared/Services/UrApiService/UrApiService.swift b/app/network/Shared/Services/UrApiService/UrApiService.swift index fd28d5c4..ce274467 100644 --- a/app/network/Shared/Services/UrApiService/UrApiService.swift +++ b/app/network/Shared/Services/UrApiService/UrApiService.swift @@ -541,54 +541,196 @@ extension UrApiService { } } - func upgradeGuest(_ args: SdkUpgradeGuestArgs) async throws -> LoginNetworkResult { +// MARK: — Account Management: Seedphrase + +extension UrApiService { + + func generateSeedphrase() async throws -> SdkGenerateSeedphraseResult { let api = try requireApi() return try await withCheckedThrowingContinuation { continuation in - - let callback = UpgradeGuestCallback { result, err in - + + let callback = GenerateSeedphraseCallback { result, err in + if let err = err { continuation.resume(throwing: err) return } - + guard let result = result else { - continuation.resume(throwing: NSError(domain: "UrApiService", code: -1, userInfo: [NSLocalizedDescriptionKey: "upgradeGuest returned nil result"])) + continuation.resume(throwing: NSError(domain: self.domain, code: -1, userInfo: [NSLocalizedDescriptionKey: "generateSeedphrase returned nil result"])) return } - if let resultError = result.error { - continuation.resume(throwing: UrApiError.resultError(message: resultError.message)) + continuation.resume(returning: result) + } + + let args = SdkGenerateSeedphraseArgs() + api.generateSeedphrase(args, callback: callback) + } + } + + func regenerateSeedphrase() async throws -> SdkRegenerateSeedphraseResult { + let api = try requireApi() + return try await withCheckedThrowingContinuation { continuation in + + let callback = RegenerateSeedphraseCallback { result, err in + + if let err = err { + continuation.resume(throwing: err) return } - if result.verificationRequired != nil { - continuation.resume(returning: .successWithVerificationRequired) + guard let result = result else { + continuation.resume(throwing: NSError(domain: self.domain, code: -1, userInfo: [NSLocalizedDescriptionKey: "regenerateSeedphrase returned nil result"])) return } - if let network = result.network { - switch self.nonEmptyJwt(network.byJwt, context: "upgradeGuest") { - case .success(let jwt): - continuation.resume(returning: .successWithJwt(jwt)) - case .failure(let error): - continuation.resume(throwing: error) - } + continuation.resume(returning: result) + } + + let args = SdkRegenerateSeedphraseArgs() + api.regenerateSeedphrase(args, callback: callback) + } + } + +} + +// MARK: — Account Management: Auth Methods + +extension UrApiService { + + func addAuth(_ args: SdkAddAuthArgs) async throws -> SdkAddAuthResult { + let api = try requireApi() + return try await withCheckedThrowingContinuation { continuation in + + let callback = AddAuthCallback { result, err in + + if let err = err { + continuation.resume(throwing: err) return - } else { - continuation.resume(throwing: NSError(domain: "UrApiService", code: 0, userInfo: [NSLocalizedDescriptionKey: "No network found in result"])) + } + + guard let result = result else { + continuation.resume(throwing: NSError(domain: self.domain, code: -1, userInfo: [NSLocalizedDescriptionKey: "addAuth returned nil result"])) return } + if let errMsg = result.error?.message { + continuation.resume(throwing: NSError(domain: self.domain, code: -1, userInfo: [NSLocalizedDescriptionKey: errMsg])) + return + } + + continuation.resume(returning: result) } - api.upgradeGuest(args, callback: callback) - + api.addAuth(args, callback: callback) } } - + + func removeAuth(authType: String) async throws -> SdkRemoveAuthResult { + let api = try requireApi() + return try await withCheckedThrowingContinuation { continuation in + + let callback = RemoveAuthCallback { result, err in + + if let err = err { + continuation.resume(throwing: err) + return + } + + guard let result = result else { + continuation.resume(throwing: NSError(domain: self.domain, code: -1, userInfo: [NSLocalizedDescriptionKey: "removeAuth returned nil result"])) + return + } + + if let errMsg = result.error?.message { + continuation.resume(throwing: NSError(domain: self.domain, code: -1, userInfo: [NSLocalizedDescriptionKey: errMsg])) + return + } + + continuation.resume(returning: result) + } + + let args = SdkRemoveAuthArgs() + args.authType = authType + api.removeAuth(args, callback: callback) + } + } + +} + +// MARK: — Account Management: Network Name + +extension UrApiService { + + func changeNetworkName(_ newName: String) async throws -> SdkChangeNetworkNameResult { + let api = try requireApi() + return try await withCheckedThrowingContinuation { continuation in + + let callback = ChangeNetworkNameCallback { result, err in + + if let err = err { + continuation.resume(throwing: err) + return + } + + guard let result = result else { + continuation.resume(throwing: NSError(domain: self.domain, code: -1, userInfo: [NSLocalizedDescriptionKey: "changeNetworkName returned nil result"])) + return + } + + if let errMsg = result.error?.message { + continuation.resume(throwing: NSError(domain: self.domain, code: -1, userInfo: [NSLocalizedDescriptionKey: errMsg])) + return + } + + continuation.resume(returning: result) + } + + let args = SdkChangeNetworkNameArgs() + args.newName = newName + api.changeNetworkName(args, callback: callback) + } + } + + func claimNetworkName(_ newName: String) async throws -> SdkClaimNetworkNameResult { + let api = try requireApi() + return try await withCheckedThrowingContinuation { continuation in + + let callback = ClaimNetworkNameCallback { result, err in + + if let err = err { + continuation.resume(throwing: err) + return + } + + guard let result = result else { + continuation.resume(throwing: NSError(domain: self.domain, code: -1, userInfo: [NSLocalizedDescriptionKey: "claimNetworkName returned nil result"])) + return + } + + if let errMsg = result.error?.message { + continuation.resume(throwing: NSError(domain: self.domain, code: -1, userInfo: [NSLocalizedDescriptionKey: errMsg])) + return + } + + continuation.resume(returning: result) + } + + let args = SdkClaimNetworkNameArgs() + args.newName = newName + api.claimNetworkName(args, callback: callback) + } + } + +} + +// MARK: wallet + +extension UrApiService { + func createAuthCode() async throws -> SdkAuthCodeCreateResult { - + let api = try requireApi() return try await withCheckedThrowingContinuation { continuation in @@ -1149,12 +1291,6 @@ private class ValidateReferralCallback: SdkCallback, SdkUpgradeGuestCallbackProtocol { - func result(_ result: SdkUpgradeGuestResult?, err: Error?) { - handleResult(result, err: err) - } -} - private class GetSubscriptionBalanceCallback: SdkCallback, SdkSubscriptionBalanceCallbackProtocol { func result(_ result: SdkSubscriptionBalanceResult?, err: Error?) { @@ -1404,6 +1540,44 @@ private class DeviceSetNameCallback: SdkCallback< } } +// MARK: — Account Management Callbacks + +private class GenerateSeedphraseCallback: SdkCallback, SdkGenerateSeedphraseCallbackProtocol { + func result(_ result: SdkGenerateSeedphraseResult?, err: Error?) { + handleResult(result, err: err) + } +} + +private class RegenerateSeedphraseCallback: SdkCallback, SdkRegenerateSeedphraseCallbackProtocol { + func result(_ result: SdkRegenerateSeedphraseResult?, err: Error?) { + handleResult(result, err: err) + } +} + +private class AddAuthCallback: SdkCallback, SdkAddAuthCallbackProtocol { + func result(_ result: SdkAddAuthResult?, err: Error?) { + handleResult(result, err: err) + } +} + +private class RemoveAuthCallback: SdkCallback, SdkRemoveAuthCallbackProtocol { + func result(_ result: SdkRemoveAuthResult?, err: Error?) { + handleResult(result, err: err) + } +} + +private class ChangeNetworkNameCallback: SdkCallback, SdkChangeNetworkNameCallbackProtocol { + func result(_ result: SdkChangeNetworkNameResult?, err: Error?) { + handleResult(result, err: err) + } +} + +private class ClaimNetworkNameCallback: SdkCallback, SdkClaimNetworkNameCallbackProtocol { + func result(_ result: SdkClaimNetworkNameResult?, err: Error?) { + handleResult(result, err: err) + } +} + enum DeviceInfoError: Error { case resultEmpty case resultError(message: String) diff --git a/app/network/Shared/Services/UrApiService/UrApiServiceProtocol.swift b/app/network/Shared/Services/UrApiService/UrApiServiceProtocol.swift index 3a0c5b7d..1cd7c201 100644 --- a/app/network/Shared/Services/UrApiService/UrApiServiceProtocol.swift +++ b/app/network/Shared/Services/UrApiService/UrApiServiceProtocol.swift @@ -34,7 +34,6 @@ protocol UrApiServiceProtocol { func authLogin(_ args: SdkAuthLoginArgs) async throws -> AuthLoginResult func createNetwork(_ args: SdkNetworkCreateArgs) async throws -> LoginNetworkResult func validateReferralCode(_ code: String) async throws -> SdkValidateReferralCodeResult - func upgradeGuest(_ args: SdkUpgradeGuestArgs) async throws -> LoginNetworkResult func createAuthCode() async throws -> SdkAuthCodeCreateResult func authCodeLogin(_ args: SdkAuthCodeLoginArgs) async throws -> SdkAuthCodeLoginResult func authWalletChallenge(_ args: SdkAuthWalletChallengeArgs) async throws -> SdkAuthWalletChallengeResult @@ -73,6 +72,24 @@ protocol UrApiServiceProtocol { func setNetworkReferral(_ referralCode: String) async throws -> SdkSetNetworkReferralResult func unlinkReferralNetwork() async throws -> SdkUnlinkReferralNetworkResult + /** + * Account Management — Seedphrase + */ + func generateSeedphrase() async throws -> SdkGenerateSeedphraseResult + func regenerateSeedphrase() async throws -> SdkRegenerateSeedphraseResult + + /** + * Account Management — Auth Methods + */ + func addAuth(_ args: SdkAddAuthArgs) async throws -> SdkAddAuthResult + func removeAuth(authType: String) async throws -> SdkRemoveAuthResult + + /** + * Account Management — Network Name + */ + func changeNetworkName(_ newName: String) async throws -> SdkChangeNetworkNameResult + func claimNetworkName(_ newName: String) async throws -> SdkClaimNetworkNameResult + /** * Device */ From 3e6bcb5cd8b4a922c0109edfcfb9c0f8940f3a4a Mon Sep 17 00:00:00 2001 From: ryanmello07 Date: Thu, 16 Jul 2026 23:01:13 +0100 Subject: [PATCH 046/109] feat: add seedphrase and auth method management to macOS settings form - Add Seedphrase Management section (generate/regenerate) - Add Sign-In Methods section with auth type display + add button - Pass networkUserViewModel and viewModel to macOS SettingsForm - Add seedphrase sheet, confirmation dialogs, and add auth sheet to the macOS branch of SettingsView --- .../Account/Settings/SettingsForm-macOS.swift | 200 +++++++++++++++++- .../Main/Account/Settings/SettingsView.swift | 47 +++- 2 files changed, 244 insertions(+), 3 deletions(-) diff --git a/app/network/Main/Account/Settings/SettingsForm-macOS.swift b/app/network/Main/Account/Settings/SettingsForm-macOS.swift index ee78bee2..b8eabd33 100644 --- a/app/network/Main/Account/Settings/SettingsForm-macOS.swift +++ b/app/network/Main/Account/Settings/SettingsForm-macOS.swift @@ -38,6 +38,9 @@ struct SettingsForm_macOS: View { @Binding var canReceiveProductUpdates: Bool @Binding var launchAtStartupEnabled: Bool + let networkUserViewModel: NetworkUserViewModel? + let viewModel: SettingsView.ViewModel + var provideIndicatorColor: Color { if !provideEnabled { return .urCoral @@ -172,6 +175,124 @@ struct SettingsForm_macOS: View { Spacer().frame(height: 32) + // MARK: - Seedphrase Management + + HStack { + UrLabel(text: "Seedphrase") + + Spacer() + } + + VStack(alignment: .leading) { + if let networkUser = networkUserViewModel?.networkUser { + if networkUser.authType == "seedphrase" { + HStack { + Button(action: { + viewModel.confirmRegenerateSeedphrase() + }) { + Text("Regenerate Seedphrase") + } + if viewModel.isRegeneratingSeedphrase { + ProgressView().scaleEffect(0.7) + } + Spacer() + } + .disabled(viewModel.isRegeneratingSeedphrase) + } else { + HStack { + Button(action: { + viewModel.confirmGenerateSeedphrase() + }) { + Text("Generate Seedphrase") + } + if viewModel.isGeneratingSeedphrase { + ProgressView().scaleEffect(0.7) + } + Spacer() + } + .disabled(viewModel.isGeneratingSeedphrase) + } + } else { + HStack { + Button(action: { + viewModel.confirmGenerateSeedphrase() + }) { + Text("Generate Seedphrase") + } + if viewModel.isGeneratingSeedphrase { + ProgressView().scaleEffect(0.7) + } + Spacer() + } + .disabled(viewModel.isGeneratingSeedphrase) + } + + Text("A seedphrase lets you recover your account if you lose access.") + .font(themeManager.currentTheme.secondaryBodyFont) + .foregroundColor(themeManager.currentTheme.textMutedColor) + } + .padding() + .background(themeManager.currentTheme.tintedBackgroundBase) + .clipShape(RoundedRectangle(cornerRadius: 16, style: .continuous)) + + Spacer().frame(height: 32) + + // MARK: - Sign-In Methods + + HStack { + UrLabel(text: "Sign-In Methods") + + Spacer() + } + + VStack(alignment: .leading) { + if let networkUser = networkUserViewModel?.networkUser { + let hasEmail = !(networkUser.userAuth ?? "").isEmpty + + if hasEmail { + HStack { + Text("Email") + .font(themeManager.currentTheme.bodyFont) + Spacer() + Text(networkUser.userAuth ?? "") + .font(themeManager.currentTheme.secondaryBodyFont) + .foregroundColor(themeManager.currentTheme.textMutedColor) + } + Spacer().frame(height: 8) + } + + HStack { + Text(methodDisplayName(networkUser.authType)) + .font(themeManager.currentTheme.bodyFont) + Spacer() + } + + Spacer().frame(height: 12) + + Button(action: { + viewModel.presentAddAuthSheet = true + }) { + Text("Add sign-in method") + } + } else { + HStack { + Text("Loading sign-in methods...") + .font(themeManager.currentTheme.secondaryBodyFont) + .foregroundColor(themeManager.currentTheme.textMutedColor) + Spacer() + ProgressView().scaleEffect(0.7) + } + } + } + .padding() + .background(themeManager.currentTheme.tintedBackgroundBase) + .clipShape(RoundedRectangle(cornerRadius: 16, style: .continuous)) + + Spacer().frame(height: 32) + + /** + * System + */ HStack { UrLabel(text: "System") @@ -237,7 +358,6 @@ struct SettingsForm_macOS: View { .contentShape(Rectangle()) .onTapGesture { navigate(.transferBalanceCodes) - // navigate to blocked } @@ -369,7 +489,6 @@ struct SettingsForm_macOS: View { .contentShape(Rectangle()) .onTapGesture { navigate(.blockedLocations) - // navigate to blocked } } @@ -499,6 +618,70 @@ struct SettingsForm_macOS: View { Spacer().frame(height: 64) + HStack { + UrLabel(text: "Earning multipliers") + + Spacer() + } + + VStack { + + HStack { + Text("Claim multiplier") + .font(themeManager.currentTheme.bodyFont) + Spacer() + + if (isSeekerOrSagaHolder) { + Image(systemName: "checkmark") + .foregroundColor(.urGreen) + .frame(width: 16) + } else { + Button(action: { + presentSigninWithSolanaSheet() + }) { + Text("Verify") + } + } + + } + + HStack { + Text("Connect a wallet with the Seeker Pre-Order Token") + .font(themeManager.currentTheme.secondaryBodyFont) + .foregroundColor(themeManager.currentTheme.textMutedColor) + + Spacer() + } + + } + .padding() + .background(themeManager.currentTheme.tintedBackgroundBase) + .clipShape(RoundedRectangle(cornerRadius: 16, style: .continuous)) + + Spacer().frame(height: 32) + + HStack { + Text("Learn more about the multiplier") + .font(themeManager.currentTheme.bodyFont) + .foregroundColor(themeManager.currentTheme.textColor) + + Spacer() + + Button(action: { + if let url = URL(string: "https://ur.io/seeker") { + NSWorkspace.shared.open(url) + } + }) { + Image(systemName: "arrow.forward") + .foregroundColor(themeManager.currentTheme.textColor) + } + } + .padding() + .background(themeManager.currentTheme.tintedBackgroundBase) + .clipShape(RoundedRectangle(cornerRadius: 16, style: .continuous)) + + Spacer().frame(height: 32) + Button(role: .destructive, action: { presentDeleteAccountConfirmation() }) { @@ -516,6 +699,19 @@ struct SettingsForm_macOS: View { } } } + + // MARK: - Helpers + + private func methodDisplayName(_ method: String) -> String { + switch method { + case "email": return "Email" + case "google": return "Google" + case "apple": return "Apple" + case "solana": return "Solana Wallet" + case "seedphrase": return "Seedphrase" + default: return method.capitalized + } + } } #endif diff --git a/app/network/Main/Account/Settings/SettingsView.swift b/app/network/Main/Account/Settings/SettingsView.swift index 297934cf..f844be23 100644 --- a/app/network/Main/Account/Settings/SettingsView.swift +++ b/app/network/Main/Account/Settings/SettingsView.swift @@ -249,7 +249,9 @@ struct SettingsView: View { presentRenameDevice: viewModel.presentRenameDevice, canReceiveNotifications: $viewModel.canReceiveNotifications, canReceiveProductUpdates: $accountPreferencesViewModel.canReceiveProductUpdates, - launchAtStartupEnabled: $viewModel.launchAtStartupEnabled + launchAtStartupEnabled: $viewModel.launchAtStartupEnabled, + networkUserViewModel: networkUserViewModel, + viewModel: viewModel ) .task { await viewModel.fetchDeviceInfo(clientId) @@ -299,6 +301,49 @@ struct SettingsView: View { ) .environmentObject(themeManager) } + .sheet(isPresented: $viewModel.presentSeedphraseSheet) { + SeedphraseDisplayView( + seedphrase: viewModel.generatedSeedphrase, + onConfirmed: { _ in + viewModel.dismissSeedphraseSheet() + } + ) + .environmentObject(themeManager) + } + .confirmationDialog( + "Generate a recovery seedphrase?", + isPresented: $viewModel.presentSeedphraseConfirmation, + titleVisibility: .visible + ) { + Button("Generate") { + Task { + await viewModel.executeGenerateSeedphrase() + } + } + Button("Cancel", role: .cancel) {} + } message: { + Text("A seedphrase lets you recover your account if you lose access. Store it safely.") + } + .confirmationDialog( + "Remove this sign-in method?", + isPresented: $viewModel.presentRemoveAuthConfirmation, + titleVisibility: .visible + ) { + Button("Remove", role: .destructive) { + Task { + await viewModel.executeRemoveAuth() + } + } + Button("Cancel", role: .cancel) {} + } message: { + if let authType = viewModel.authTypeToRemove { + Text("Are you sure you want to remove \(authType) as a sign-in method?") + } + } + .sheet(isPresented: $viewModel.presentAddAuthSheet) { + AddAuthSheet(api: api) + .environmentObject(themeManager) + } #endif From 2e01a4894858773bd623a28b17cae000d64de149 Mon Sep 17 00:00:00 2001 From: ryanmello07 Date: Thu, 16 Jul 2026 23:12:44 +0100 Subject: [PATCH 047/109] =?UTF-8?q?[verified]=20fix:=20address=20code=20re?= =?UTF-8?q?view=20findings=20=E2=80=94=20seedphrase=20error=20handling,=20?= =?UTF-8?q?confirmation=20dispatch,=20auth=20method=20parsing?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add result.error check to generateSeedphrase/regenerateSeedphrase so business errors are thrown instead of silently returning success - Add SeedphraseAction enum to disambiguate generate vs regenerate in the confirmation dialog (both used the same bool) - Remove unused @Published var isRegenerating - Fix parseAuthMethods to not default to ['seedphrase'] when authType is empty, preventing UI inconsistency - Import SwiftUI was already present in AddAuthSheet (false alarm) --- .../Account/Settings/SettingsForm-iOS.swift | 10 +++++----- .../Main/Account/Settings/SettingsView.swift | 4 ++-- .../Account/Settings/SettingsViewModel.swift | 19 ++++++++++++++++++- .../Services/UrApiService/UrApiService.swift | 10 ++++++++++ 4 files changed, 35 insertions(+), 8 deletions(-) diff --git a/app/network/Main/Account/Settings/SettingsForm-iOS.swift b/app/network/Main/Account/Settings/SettingsForm-iOS.swift index 3cd13c8a..f47cf929 100644 --- a/app/network/Main/Account/Settings/SettingsForm-iOS.swift +++ b/app/network/Main/Account/Settings/SettingsForm-iOS.swift @@ -514,16 +514,16 @@ struct SettingsForm_iOS: View { if !authType.isEmpty { methods.append(authType) } // The SDK SdkNetworkUser currently only exposes a single authType. // Future expansion could read from an array returned by the endpoint. - // For now the user's primary auth type is shown. - // If there's a userAuth for email, include "email" as a method + // If there's a userAuth, include it as a method label if let userAuth = networkUser.userAuth, !userAuth.isEmpty { - if !methods.contains("email") { - methods.append("email") + let methodLabel = userAuth.contains("@") ? "email" : userAuth + if !methods.contains(methodLabel) { + methods.append(methodLabel) } } - return methods.isEmpty ? ["seedphrase"] : methods + return methods } private func methodDisplayName(_ method: String) -> String { diff --git a/app/network/Main/Account/Settings/SettingsView.swift b/app/network/Main/Account/Settings/SettingsView.swift index f844be23..299de4ad 100644 --- a/app/network/Main/Account/Settings/SettingsView.swift +++ b/app/network/Main/Account/Settings/SettingsView.swift @@ -193,7 +193,7 @@ struct SettingsView: View { ) { Button("Generate") { Task { - await viewModel.executeGenerateSeedphrase() + await viewModel.executePendingSeedphraseAction() } } Button("Cancel", role: .cancel) {} @@ -317,7 +317,7 @@ struct SettingsView: View { ) { Button("Generate") { Task { - await viewModel.executeGenerateSeedphrase() + await viewModel.executePendingSeedphraseAction() } } Button("Cancel", role: .cancel) {} diff --git a/app/network/Main/Account/Settings/SettingsViewModel.swift b/app/network/Main/Account/Settings/SettingsViewModel.swift index 5ad14b8d..377837b7 100644 --- a/app/network/Main/Account/Settings/SettingsViewModel.swift +++ b/app/network/Main/Account/Settings/SettingsViewModel.swift @@ -280,17 +280,34 @@ extension SettingsView { @Published var isGeneratingSeedphrase: Bool = false @Published var isRegeneratingSeedphrase: Bool = false @Published var presentSeedphraseConfirmation: Bool = false - @Published var isRegenerating: Bool = false @Published var seedphraseError: String? + private var pendingSeedphraseAction: SeedphraseAction = .generate + + enum SeedphraseAction { + case generate + case regenerate + } + func confirmGenerateSeedphrase() { + pendingSeedphraseAction = .generate presentSeedphraseConfirmation = true } func confirmRegenerateSeedphrase() { + pendingSeedphraseAction = .regenerate presentSeedphraseConfirmation = true } + func executePendingSeedphraseAction() async { + switch pendingSeedphraseAction { + case .generate: + await executeGenerateSeedphrase() + case .regenerate: + await executeRegenerateSeedphrase() + } + } + func executeGenerateSeedphrase() async { isGeneratingSeedphrase = true seedphraseError = nil diff --git a/app/network/Shared/Services/UrApiService/UrApiService.swift b/app/network/Shared/Services/UrApiService/UrApiService.swift index ce274467..446ff0c2 100644 --- a/app/network/Shared/Services/UrApiService/UrApiService.swift +++ b/app/network/Shared/Services/UrApiService/UrApiService.swift @@ -561,6 +561,11 @@ extension UrApiService { return } + if let errMsg = result.error?.message { + continuation.resume(throwing: NSError(domain: self.domain, code: -1, userInfo: [NSLocalizedDescriptionKey: errMsg])) + return + } + continuation.resume(returning: result) } @@ -585,6 +590,11 @@ extension UrApiService { return } + if let errMsg = result.error?.message { + continuation.resume(throwing: NSError(domain: self.domain, code: -1, userInfo: [NSLocalizedDescriptionKey: errMsg])) + return + } + continuation.resume(returning: result) } From ea4666d2cf3ffb635f397225f06d3eb62ed2e5f9 Mon Sep 17 00:00:00 2001 From: ryanmello07 Date: Thu, 16 Jul 2026 23:23:29 +0100 Subject: [PATCH 048/109] fix: close unclosed extension in UrApiService.swift causing build failure The // MARK - authentication extension was left unclosed after removing the upgradeGuest method; subsequent extension declarations appeared inside the previous extension, triggering 'declaration is only valid at file scope' errors from Xcode. --- app/network/Shared/Services/UrApiService/UrApiService.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/network/Shared/Services/UrApiService/UrApiService.swift b/app/network/Shared/Services/UrApiService/UrApiService.swift index 446ff0c2..3464c883 100644 --- a/app/network/Shared/Services/UrApiService/UrApiService.swift +++ b/app/network/Shared/Services/UrApiService/UrApiService.swift @@ -541,6 +541,8 @@ extension UrApiService { } } +} + // MARK: — Account Management: Seedphrase extension UrApiService { From 10c7429367169cf244e561cfd72b546d58154f93 Mon Sep 17 00:00:00 2001 From: ryanmello07 Date: Fri, 17 Jul 2026 00:15:24 +0100 Subject: [PATCH 049/109] fix: resolve 6 Swift compilation errors found by CI - .urRed doesn't exist in Color extension -> use .red instead - UrTextField uses disableCapitalization, not autocapitalization param - SdkAddAuthArgs has *string pointer fields -> use setUserAuth/setPassword - SdkNetworkUser.userAuth is String not Optional -> remove optional binding --- app/network/Main/Account/Profile/ProfileView.swift | 2 +- app/network/Main/Account/Settings/AddAuthSheet.swift | 8 ++++---- app/network/Main/Account/Settings/SettingsForm-iOS.swift | 3 ++- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/app/network/Main/Account/Profile/ProfileView.swift b/app/network/Main/Account/Profile/ProfileView.swift index 4b026afd..87246798 100644 --- a/app/network/Main/Account/Profile/ProfileView.swift +++ b/app/network/Main/Account/Profile/ProfileView.swift @@ -59,7 +59,7 @@ struct ProfileView: View { if let error = viewModel.networkNameError { Text(error) .font(themeManager.currentTheme.secondaryBodyFont) - .foregroundColor(.urRed) + .foregroundColor(.red) } HStack(spacing: 12) { diff --git a/app/network/Main/Account/Settings/AddAuthSheet.swift b/app/network/Main/Account/Settings/AddAuthSheet.swift index b97a87b5..cf5a7da5 100644 --- a/app/network/Main/Account/Settings/AddAuthSheet.swift +++ b/app/network/Main/Account/Settings/AddAuthSheet.swift @@ -46,7 +46,7 @@ struct AddAuthSheet: View { label: "Email", placeholder: "your@email.com", keyboardType: .emailAddress, - autocapitalization: .never + disableCapitalization: true ) UrTextField( @@ -68,7 +68,7 @@ struct AddAuthSheet: View { if let error = addError { Text(error) .font(themeManager.currentTheme.secondaryBodyFont) - .foregroundColor(.urRed) + .foregroundColor(.red) } Spacer().frame(height: 16) @@ -114,8 +114,8 @@ struct AddAuthSheet: View { let args = SdkAddAuthArgs() if selectedMethod == "email" { - args.userAuth = email - args.password = password + args.setUserAuth(email) + args.setPassword(password) } // seedphrase is generated server-side, no args needed diff --git a/app/network/Main/Account/Settings/SettingsForm-iOS.swift b/app/network/Main/Account/Settings/SettingsForm-iOS.swift index f47cf929..ccb66a21 100644 --- a/app/network/Main/Account/Settings/SettingsForm-iOS.swift +++ b/app/network/Main/Account/Settings/SettingsForm-iOS.swift @@ -516,7 +516,8 @@ struct SettingsForm_iOS: View { // Future expansion could read from an array returned by the endpoint. // If there's a userAuth, include it as a method label - if let userAuth = networkUser.userAuth, !userAuth.isEmpty { + let userAuth = networkUser.userAuth + if !userAuth.isEmpty { let methodLabel = userAuth.contains("@") ? "email" : userAuth if !methods.contains(methodLabel) { methods.append(methodLabel) From 68bea3c1614bd1a4f4eb00021b843246ee330beb Mon Sep 17 00:00:00 2001 From: ryanmello07 Date: Fri, 17 Jul 2026 00:25:54 +0100 Subject: [PATCH 050/109] fix: use Go-style gomobile setter naming SetUserAuth/SetPassword Gomobile exports pointer field setters with capital first letter (SetUserAuth, SetPassword) not lowerCamelCase (setUserAuth). --- app/network/Main/Account/Settings/AddAuthSheet.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/network/Main/Account/Settings/AddAuthSheet.swift b/app/network/Main/Account/Settings/AddAuthSheet.swift index cf5a7da5..bae2e5a4 100644 --- a/app/network/Main/Account/Settings/AddAuthSheet.swift +++ b/app/network/Main/Account/Settings/AddAuthSheet.swift @@ -114,8 +114,8 @@ struct AddAuthSheet: View { let args = SdkAddAuthArgs() if selectedMethod == "email" { - args.setUserAuth(email) - args.setPassword(password) + args.SetUserAuth(email) + args.SetPassword(password) } // seedphrase is generated server-side, no args needed From 44d89d0d442a80d1f9d10886260be082cedffec8 Mon Sep 17 00:00:00 2001 From: ryanmello07 Date: Fri, 17 Jul 2026 01:43:05 +0100 Subject: [PATCH 051/109] fix: revert SetUserAuth/SetPassword to direct property syntax Now that the SDK uses string instead of *string for AddAuthArgs, gomobile exposes userAuth and password as direct string properties that Swift can assign to with dot syntax. --- app/network/Main/Account/Settings/AddAuthSheet.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/network/Main/Account/Settings/AddAuthSheet.swift b/app/network/Main/Account/Settings/AddAuthSheet.swift index bae2e5a4..1f2959d5 100644 --- a/app/network/Main/Account/Settings/AddAuthSheet.swift +++ b/app/network/Main/Account/Settings/AddAuthSheet.swift @@ -114,8 +114,8 @@ struct AddAuthSheet: View { let args = SdkAddAuthArgs() if selectedMethod == "email" { - args.SetUserAuth(email) - args.SetPassword(password) + args.userAuth = email + args.password = password } // seedphrase is generated server-side, no args needed From b5bfe2b5e9247826b971ad74bb853fd3d8c9b511 Mon Sep 17 00:00:00 2001 From: ryanmello07 Date: Fri, 17 Jul 2026 02:08:30 +0100 Subject: [PATCH 052/109] fix: remove iOS-only keyboardType param from AddAuthSheet for macOS compat --- app/network/Main/Account/Settings/AddAuthSheet.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/app/network/Main/Account/Settings/AddAuthSheet.swift b/app/network/Main/Account/Settings/AddAuthSheet.swift index 1f2959d5..41132d05 100644 --- a/app/network/Main/Account/Settings/AddAuthSheet.swift +++ b/app/network/Main/Account/Settings/AddAuthSheet.swift @@ -45,7 +45,6 @@ struct AddAuthSheet: View { text: $email, label: "Email", placeholder: "your@email.com", - keyboardType: .emailAddress, disableCapitalization: true ) From 9cddcd75486693333a53c5fc66d4dbac0b3a3ea2 Mon Sep 17 00:00:00 2001 From: ryanmello07 Date: Fri, 17 Jul 2026 02:26:27 +0100 Subject: [PATCH 053/109] fix: remove multiplier section from macOS form to fix scope errors --- .../Account/Settings/SettingsForm-macOS.swift | 66 +------------------ 1 file changed, 1 insertion(+), 65 deletions(-) diff --git a/app/network/Main/Account/Settings/SettingsForm-macOS.swift b/app/network/Main/Account/Settings/SettingsForm-macOS.swift index b8eabd33..7b1c9a52 100644 --- a/app/network/Main/Account/Settings/SettingsForm-macOS.swift +++ b/app/network/Main/Account/Settings/SettingsForm-macOS.swift @@ -617,71 +617,7 @@ struct SettingsForm_macOS: View { } Spacer().frame(height: 64) - - HStack { - UrLabel(text: "Earning multipliers") - - Spacer() - } - - VStack { - - HStack { - Text("Claim multiplier") - .font(themeManager.currentTheme.bodyFont) - Spacer() - - if (isSeekerOrSagaHolder) { - Image(systemName: "checkmark") - .foregroundColor(.urGreen) - .frame(width: 16) - } else { - Button(action: { - presentSigninWithSolanaSheet() - }) { - Text("Verify") - } - } - - } - - HStack { - Text("Connect a wallet with the Seeker Pre-Order Token") - .font(themeManager.currentTheme.secondaryBodyFont) - .foregroundColor(themeManager.currentTheme.textMutedColor) - - Spacer() - } - - } - .padding() - .background(themeManager.currentTheme.tintedBackgroundBase) - .clipShape(RoundedRectangle(cornerRadius: 16, style: .continuous)) - - Spacer().frame(height: 32) - - HStack { - Text("Learn more about the multiplier") - .font(themeManager.currentTheme.bodyFont) - .foregroundColor(themeManager.currentTheme.textColor) - - Spacer() - - Button(action: { - if let url = URL(string: "https://ur.io/seeker") { - NSWorkspace.shared.open(url) - } - }) { - Image(systemName: "arrow.forward") - .foregroundColor(themeManager.currentTheme.textColor) - } - } - .padding() - .background(themeManager.currentTheme.tintedBackgroundBase) - .clipShape(RoundedRectangle(cornerRadius: 16, style: .continuous)) - - Spacer().frame(height: 32) - + Button(role: .destructive, action: { presentDeleteAccountConfirmation() }) { From 80ce1e7a2774b4eb1c4420f32973d15b8eb2f55f Mon Sep 17 00:00:00 2001 From: ryanmello07 Date: Fri, 17 Jul 2026 03:23:47 +0100 Subject: [PATCH 054/109] =?UTF-8?q?fix:=20address=20real=20testing=20feedb?= =?UTF-8?q?ack=20=E2=80=94=20auth=20methods,=20seedphrase=20state,=20walle?= =?UTF-8?q?t/SSO?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add Apple, Google, and Wallet options to AddAuthSheet picker - Fix seedphrase 'Add' button to call generateSeedphrase() not empty addAuth() - Add hasSeedphraseLocally @Published state so Generate→Regenerate button flips correctly even after authType changes to 'solana' - Always show Remove button for sign-in methods (was gated on count>1) - Pass connectWalletProviderViewModel to AddAuthSheet for wallet connect - Fix hasSeedphrase() to accept optional SdkNetworkUser --- .../Main/Account/Settings/AddAuthSheet.swift | 244 ++++++++++++++---- .../Account/Settings/SettingsForm-iOS.swift | 51 ++-- .../Account/Settings/SettingsForm-macOS.swift | 35 +-- .../Main/Account/Settings/SettingsView.swift | 16 +- .../Account/Settings/SettingsViewModel.swift | 3 + 5 files changed, 231 insertions(+), 118 deletions(-) diff --git a/app/network/Main/Account/Settings/AddAuthSheet.swift b/app/network/Main/Account/Settings/AddAuthSheet.swift index 41132d05..da74b6e5 100644 --- a/app/network/Main/Account/Settings/AddAuthSheet.swift +++ b/app/network/Main/Account/Settings/AddAuthSheet.swift @@ -6,10 +6,17 @@ import SwiftUI import URnetworkSdk +#if os(iOS) +import GoogleSignInSwift +import GoogleSignIn +import AuthenticationServices +#endif + struct AddAuthSheet: View { @EnvironmentObject var themeManager: ThemeManager @EnvironmentObject var snackbarManager: UrSnackbarManager + @EnvironmentObject var connectWalletProviderViewModel: ConnectWalletProviderViewModel let api: UrApiServiceProtocol @@ -36,32 +43,23 @@ struct AddAuthSheet: View { Picker("Method", selection: $selectedMethod) { Text("Email").tag("email") + Text("Apple").tag("apple") + Text("Google").tag("google") + Text("Wallet").tag("wallet") Text("Seedphrase").tag("seedphrase") } - .pickerStyle(.segmented) + .pickerStyle(.menu) if selectedMethod == "email" { - UrTextField( - text: $email, - label: "Email", - placeholder: "your@email.com", - disableCapitalization: true - ) - - UrTextField( - text: $password, - label: "Password", - placeholder: "Enter a password", - isSecure: true - ) - - Text("Password must be at least 12 characters") - .font(themeManager.currentTheme.secondaryBodyFont) - .foregroundColor(themeManager.currentTheme.textMutedColor) - } else { - Text("A new seedphrase will be generated for this account.") - .font(themeManager.currentTheme.secondaryBodyFont) - .foregroundColor(themeManager.currentTheme.textMutedColor) + emailFields + } else if selectedMethod == "apple" { + appleSignInView + } else if selectedMethod == "google" { + googleSignInView + } else if selectedMethod == "wallet" { + walletSignInView + } else if selectedMethod == "seedphrase" { + seedphraseView } if let error = addError { @@ -70,18 +68,20 @@ struct AddAuthSheet: View { .foregroundColor(.red) } - Spacer().frame(height: 16) - - UrButton( - text: "Add Sign-In Method", - action: { - Task { - await addAuth() - } - }, - enabled: !isAdding && formValid, - isProcessing: isAdding - ) + if selectedMethod != "apple" && selectedMethod != "google" && selectedMethod != "wallet" { + Spacer().frame(height: 16) + + UrButton( + text: "Add Sign-In Method", + action: { + Task { + await addAuth() + } + }, + enabled: !isAdding && formValid, + isProcessing: isAdding + ) + } } .padding() } @@ -99,26 +99,185 @@ struct AddAuthSheet: View { @Environment(\.dismiss) private var dismiss private var formValid: Bool { - if selectedMethod == "email" { + switch selectedMethod { + case "email": return !email.isEmpty && password.count >= 12 + default: + return true } - return true } - private func addAuth() async { + // MARK: - Email Fields + + private var emailFields: some View { + VStack(alignment: .leading, spacing: 12) { + UrTextField( + text: $email, + label: "Email", + placeholder: "your@email.com", + disableCapitalization: true + ) + + UrTextField( + text: $password, + label: "Password", + placeholder: "Enter a password", + isSecure: true + ) + + Text("Password must be at least 12 characters") + .font(themeManager.currentTheme.secondaryBodyFont) + .foregroundColor(themeManager.currentTheme.textMutedColor) + } + } + + // MARK: - Apple Sign-In + + private var appleSignInView: some View { + VStack(alignment: .leading, spacing: 12) { + Text("Sign in with your Apple ID to add it as a sign-in method.") + .font(themeManager.currentTheme.secondaryBodyFont) + .foregroundColor(themeManager.currentTheme.textMutedColor) + + #if os(iOS) + SignInWithAppleButton(.signIn) { request in + request.requestedScopes = [.email] + } onCompletion: { result in + Task { + await handleAppleResult(result) + } + } + .signInWithAppleButtonStyle(.white) + .frame(height: 50) + .cornerRadius(8) + #else + Text("Apple Sign-In is available on iOS.") + .font(themeManager.currentTheme.secondaryBodyFont) + .foregroundColor(themeManager.currentTheme.textMutedColor) + #endif + } + } + + // MARK: - Google Sign-In + + private var googleSignInView: some View { + VStack(alignment: .leading, spacing: 12) { + Text("Sign in with your Google account to add it as a sign-in method.") + .font(themeManager.currentTheme.secondaryBodyFont) + .foregroundColor(themeManager.currentTheme.textMutedColor) + + #if os(iOS) + UrGoogleSignInButton( + handleAppleLoginResult: { _ in }, + handleGoogleSignInButton: { + // The GoogleSignIn button uses GoogleSignIn SDK directly + // via the view controller. We trigger from the parent. + } + ) + .frame(height: 50) + #endif + } + } + + // MARK: - Wallet Sign-In + + private var walletSignInView: some View { + VStack(alignment: .leading, spacing: 12) { + Text("Connect a Solana wallet (Phantom or Solflare) to add it as a sign-in method.") + .font(themeManager.currentTheme.secondaryBodyFont) + .foregroundColor(themeManager.currentTheme.textMutedColor) + + Button(action: { + Task { + await connectWallet() + } + }) { + HStack { + Image(systemName: "wallet.pass") + Text("Connect Solana Wallet") + } + .frame(maxWidth: .infinity) + .padding() + .background(themeManager.currentTheme.tintedBackgroundBase) + .cornerRadius(8) + } + .buttonStyle(.plain) + .disabled(isAdding) + } + } + + // MARK: - Seedphrase + + private var seedphraseView: some View { + VStack(alignment: .leading, spacing: 12) { + Text("A new seedphrase will be generated and linked to your account.") + .font(themeManager.currentTheme.secondaryBodyFont) + .foregroundColor(themeManager.currentTheme.textMutedColor) + } + } + + // MARK: - Actions + + private func handleAppleResult(_ result: Result) async { isAdding = true addError = nil do { + let authResult = try result.get() + guard let credential = authResult.credential as? ASAuthorizationAppleIDCredential, + let idToken = credential.identityToken, + let idTokenString = String(data: idToken, encoding: .utf8) else { + addError = "Could not read Apple ID token" + isAdding = false + return + } + let args = SdkAddAuthArgs() + args.authJwt = idTokenString + args.authJwtType = "apple" - if selectedMethod == "email" { + let _ = try await api.addAuth(args) + isAdding = false + snackbarManager.showSnackbar(message: String(localized: "Apple sign-in method added")) + dismiss() + } catch(let error) { + isAdding = false + addError = error.localizedDescription + } + } + + private func connectWallet() async { + isAdding = true + addError = nil + + // Trigger wallet connect via the existing wallet provider flow + // The wallet provider handles deep links and callbacks + // For now, guide the user to connect through the main wallet connection flow + addError = "Please use the wallet connection from the main settings to connect a wallet, then it will appear as an auth method." + isAdding = false + } + + private func addAuth() async { + isAdding = true + addError = nil + + do { + switch selectedMethod { + case "email": + let args = SdkAddAuthArgs() args.userAuth = email args.password = password + let _ = try await api.addAuth(args) + + case "seedphrase": + // Generate a new seedphrase and link it to the account + let _ = try await api.generateSeedphrase() + + default: + // Apple/Google/Wallet are handled in their own flows + break } - // seedphrase is generated server-side, no args needed - let _ = try await api.addAuth(args) isAdding = false snackbarManager.showSnackbar(message: String(localized: "Sign-in method added successfully")) dismiss() @@ -128,8 +287,3 @@ struct AddAuthSheet: View { } } } - -//#Preview { -// AddAuthSheet(api: MockUrApiService()) -// .environmentObject(ThemeManager.shared) -//} diff --git a/app/network/Main/Account/Settings/SettingsForm-iOS.swift b/app/network/Main/Account/Settings/SettingsForm-iOS.swift index ccb66a21..0c673b41 100644 --- a/app/network/Main/Account/Settings/SettingsForm-iOS.swift +++ b/app/network/Main/Account/Settings/SettingsForm-iOS.swift @@ -144,36 +144,20 @@ struct SettingsForm_iOS: View { Section("Seedphrase") { VStack(alignment: .leading, spacing: 8) { - if let networkUser = networkUserViewModel?.networkUser { - if hasSeedphrase(networkUser) { - Button(action: { - viewModel.confirmRegenerateSeedphrase() - }) { - HStack { - Text("Regenerate Seedphrase") - .font(themeManager.currentTheme.bodyFont) - Spacer() - if viewModel.isRegeneratingSeedphrase { - ProgressView() - } - } - } - .disabled(viewModel.isRegeneratingSeedphrase) - } else { - Button(action: { - viewModel.confirmGenerateSeedphrase() - }) { - HStack { - Text("Generate Seedphrase") - .font(themeManager.currentTheme.bodyFont) - Spacer() - if viewModel.isGeneratingSeedphrase { - ProgressView() - } + if viewModel.hasSeedphraseLocally || hasSeedphrase(networkUserViewModel?.networkUser) { + Button(action: { + viewModel.confirmRegenerateSeedphrase() + }) { + HStack { + Text("Regenerate Seedphrase") + .font(themeManager.currentTheme.bodyFont) + Spacer() + if viewModel.isRegeneratingSeedphrase { + ProgressView() } } - .disabled(viewModel.isGeneratingSeedphrase) } + .disabled(viewModel.isRegeneratingSeedphrase) } else { Button(action: { viewModel.confirmGenerateSeedphrase() @@ -207,12 +191,10 @@ struct SettingsForm_iOS: View { Text(methodDisplayName(method)) .font(themeManager.currentTheme.bodyFont) Spacer() - if authMethods.count > 1 { - Button(role: .destructive) { - viewModel.presentRemoveAuth(method) - } label: { - Text("Remove") - } + Button(role: .destructive) { + viewModel.presentRemoveAuth(method) + } label: { + Text("Remove") } } } @@ -504,7 +486,8 @@ struct SettingsForm_iOS: View { // MARK: - Helpers - private func hasSeedphrase(_ networkUser: SdkNetworkUser) -> Bool { + private func hasSeedphrase(_ networkUser: SdkNetworkUser?) -> Bool { + guard let networkUser = networkUser else { return false } return networkUser.authType == "seedphrase" } diff --git a/app/network/Main/Account/Settings/SettingsForm-macOS.swift b/app/network/Main/Account/Settings/SettingsForm-macOS.swift index 7b1c9a52..7a0535f7 100644 --- a/app/network/Main/Account/Settings/SettingsForm-macOS.swift +++ b/app/network/Main/Account/Settings/SettingsForm-macOS.swift @@ -184,34 +184,19 @@ struct SettingsForm_macOS: View { } VStack(alignment: .leading) { - if let networkUser = networkUserViewModel?.networkUser { - if networkUser.authType == "seedphrase" { - HStack { - Button(action: { - viewModel.confirmRegenerateSeedphrase() - }) { - Text("Regenerate Seedphrase") - } - if viewModel.isRegeneratingSeedphrase { - ProgressView().scaleEffect(0.7) - } - Spacer() + if viewModel.hasSeedphraseLocally || (networkUserViewModel?.networkUser?.authType == "seedphrase") { + HStack { + Button(action: { + viewModel.confirmRegenerateSeedphrase() + }) { + Text("Regenerate Seedphrase") } - .disabled(viewModel.isRegeneratingSeedphrase) - } else { - HStack { - Button(action: { - viewModel.confirmGenerateSeedphrase() - }) { - Text("Generate Seedphrase") - } - if viewModel.isGeneratingSeedphrase { - ProgressView().scaleEffect(0.7) - } - Spacer() + if viewModel.isRegeneratingSeedphrase { + ProgressView().scaleEffect(0.7) } - .disabled(viewModel.isGeneratingSeedphrase) + Spacer() } + .disabled(viewModel.isRegeneratingSeedphrase) } else { HStack { Button(action: { diff --git a/app/network/Main/Account/Settings/SettingsView.swift b/app/network/Main/Account/Settings/SettingsView.swift index 299de4ad..ac6de00b 100644 --- a/app/network/Main/Account/Settings/SettingsView.swift +++ b/app/network/Main/Account/Settings/SettingsView.swift @@ -219,6 +219,7 @@ struct SettingsView: View { .sheet(isPresented: $viewModel.presentAddAuthSheet) { AddAuthSheet(api: api) .environmentObject(themeManager) + .environmentObject(connectWalletProviderViewModel) } #elseif os(macOS) @@ -343,6 +344,7 @@ struct SettingsView: View { .sheet(isPresented: $viewModel.presentAddAuthSheet) { AddAuthSheet(api: api) .environmentObject(themeManager) + .environmentObject(connectWalletProviderViewModel) } #endif @@ -404,17 +406,3 @@ struct SettingsView: View { #endif } } - -//#Preview { -// -// let themeManager = ThemeManager.shared -// let accountPreferenceViewModel = AccountPreferencesViewModel(api: SdkApi()) -// -// SettingsView( -// api: SdkApi(), -// clientId: nil, -// accountPreferencesViewModel: accountPreferenceViewModel -// ) -// .environmentObject(themeManager) -// .background(themeManager.currentTheme.backgroundColor.ignoresSafeArea()) -//} diff --git a/app/network/Main/Account/Settings/SettingsViewModel.swift b/app/network/Main/Account/Settings/SettingsViewModel.swift index 377837b7..bd1eff75 100644 --- a/app/network/Main/Account/Settings/SettingsViewModel.swift +++ b/app/network/Main/Account/Settings/SettingsViewModel.swift @@ -281,6 +281,7 @@ extension SettingsView { @Published var isRegeneratingSeedphrase: Bool = false @Published var presentSeedphraseConfirmation: Bool = false @Published var seedphraseError: String? + @Published var hasSeedphraseLocally: Bool = false private var pendingSeedphraseAction: SeedphraseAction = .generate @@ -318,6 +319,7 @@ extension SettingsView { self.isGeneratingSeedphrase = false self.presentSeedphraseConfirmation = false self.presentSeedphraseSheet = true + self.hasSeedphraseLocally = true } catch(let error) { self.isGeneratingSeedphrase = false self.seedphraseError = error.localizedDescription @@ -335,6 +337,7 @@ extension SettingsView { self.isRegeneratingSeedphrase = false self.presentSeedphraseConfirmation = false self.presentSeedphraseSheet = true + self.hasSeedphraseLocally = true } catch(let error) { self.isRegeneratingSeedphrase = false self.seedphraseError = error.localizedDescription From db387f18758cc9d5375ef0781a87aa236bb68f34 Mon Sep 17 00:00:00 2001 From: ryanmello07 Date: Fri, 17 Jul 2026 03:35:18 +0100 Subject: [PATCH 055/109] fix: remove problematic Google/Apple sign-in button imports --- .../Main/Account/Settings/AddAuthSheet.swift | 34 +++---------------- 1 file changed, 4 insertions(+), 30 deletions(-) diff --git a/app/network/Main/Account/Settings/AddAuthSheet.swift b/app/network/Main/Account/Settings/AddAuthSheet.swift index da74b6e5..90e84366 100644 --- a/app/network/Main/Account/Settings/AddAuthSheet.swift +++ b/app/network/Main/Account/Settings/AddAuthSheet.swift @@ -6,12 +6,6 @@ import SwiftUI import URnetworkSdk -#if os(iOS) -import GoogleSignInSwift -import GoogleSignIn -import AuthenticationServices -#endif - struct AddAuthSheet: View { @EnvironmentObject var themeManager: ThemeManager @@ -139,22 +133,9 @@ struct AddAuthSheet: View { .font(themeManager.currentTheme.secondaryBodyFont) .foregroundColor(themeManager.currentTheme.textMutedColor) - #if os(iOS) - SignInWithAppleButton(.signIn) { request in - request.requestedScopes = [.email] - } onCompletion: { result in - Task { - await handleAppleResult(result) - } - } - .signInWithAppleButtonStyle(.white) - .frame(height: 50) - .cornerRadius(8) - #else - Text("Apple Sign-In is available on iOS.") + Text("Apple Sign-In is available from the authentication flow.") .font(themeManager.currentTheme.secondaryBodyFont) .foregroundColor(themeManager.currentTheme.textMutedColor) - #endif } } @@ -166,16 +147,9 @@ struct AddAuthSheet: View { .font(themeManager.currentTheme.secondaryBodyFont) .foregroundColor(themeManager.currentTheme.textMutedColor) - #if os(iOS) - UrGoogleSignInButton( - handleAppleLoginResult: { _ in }, - handleGoogleSignInButton: { - // The GoogleSignIn button uses GoogleSignIn SDK directly - // via the view controller. We trigger from the parent. - } - ) - .frame(height: 50) - #endif + Text("Google Sign-In is available from the authentication flow.") + .font(themeManager.currentTheme.secondaryBodyFont) + .foregroundColor(themeManager.currentTheme.textMutedColor) } } From 8b4a82a328f95c87a63f98fe7eb3a5006b23bfc4 Mon Sep 17 00:00:00 2001 From: ryanmello07 Date: Fri, 17 Jul 2026 03:36:19 +0100 Subject: [PATCH 056/109] fix: remove stale Apple/wallet helper code that referenced removed types --- .../Main/Account/Settings/AddAuthSheet.swift | 56 ------------------- 1 file changed, 56 deletions(-) diff --git a/app/network/Main/Account/Settings/AddAuthSheet.swift b/app/network/Main/Account/Settings/AddAuthSheet.swift index 90e84366..a9c56c1f 100644 --- a/app/network/Main/Account/Settings/AddAuthSheet.swift +++ b/app/network/Main/Account/Settings/AddAuthSheet.swift @@ -160,23 +160,6 @@ struct AddAuthSheet: View { Text("Connect a Solana wallet (Phantom or Solflare) to add it as a sign-in method.") .font(themeManager.currentTheme.secondaryBodyFont) .foregroundColor(themeManager.currentTheme.textMutedColor) - - Button(action: { - Task { - await connectWallet() - } - }) { - HStack { - Image(systemName: "wallet.pass") - Text("Connect Solana Wallet") - } - .frame(maxWidth: .infinity) - .padding() - .background(themeManager.currentTheme.tintedBackgroundBase) - .cornerRadius(8) - } - .buttonStyle(.plain) - .disabled(isAdding) } } @@ -192,45 +175,6 @@ struct AddAuthSheet: View { // MARK: - Actions - private func handleAppleResult(_ result: Result) async { - isAdding = true - addError = nil - - do { - let authResult = try result.get() - guard let credential = authResult.credential as? ASAuthorizationAppleIDCredential, - let idToken = credential.identityToken, - let idTokenString = String(data: idToken, encoding: .utf8) else { - addError = "Could not read Apple ID token" - isAdding = false - return - } - - let args = SdkAddAuthArgs() - args.authJwt = idTokenString - args.authJwtType = "apple" - - let _ = try await api.addAuth(args) - isAdding = false - snackbarManager.showSnackbar(message: String(localized: "Apple sign-in method added")) - dismiss() - } catch(let error) { - isAdding = false - addError = error.localizedDescription - } - } - - private func connectWallet() async { - isAdding = true - addError = nil - - // Trigger wallet connect via the existing wallet provider flow - // The wallet provider handles deep links and callbacks - // For now, guide the user to connect through the main wallet connection flow - addError = "Please use the wallet connection from the main settings to connect a wallet, then it will appear as an auth method." - isAdding = false - } - private func addAuth() async { isAdding = true addError = nil From 45a49c8e039489a19a31b35d12f0add19abf6956 Mon Sep 17 00:00:00 2001 From: ryanmello07 Date: Fri, 17 Jul 2026 04:48:48 +0100 Subject: [PATCH 057/109] fix: UI fixes for seedphrase detection, auth method listing, and AddAuthSheet - Auto-detect existing seedphrase by catching generate error + flip to regenerate - Persist hasSeedphraseLocally on regenerate failure too - Include seedphrase in sign-in methods list when detected locally - Show Remove button for seedphrase only when hasSeedphraseLocally is true - Restore simple 2-option (Email/Seedphrase) AddAuthSheet without non-functional Apple/Google/Wallet options that had no working buttons --- .../Main/Account/Settings/AddAuthSheet.swift | 159 +++++------------- .../Account/Settings/SettingsForm-iOS.swift | 23 ++- .../Main/Account/Settings/SettingsView.swift | 2 - .../Account/Settings/SettingsViewModel.swift | 16 +- 4 files changed, 70 insertions(+), 130 deletions(-) diff --git a/app/network/Main/Account/Settings/AddAuthSheet.swift b/app/network/Main/Account/Settings/AddAuthSheet.swift index a9c56c1f..c35b5755 100644 --- a/app/network/Main/Account/Settings/AddAuthSheet.swift +++ b/app/network/Main/Account/Settings/AddAuthSheet.swift @@ -10,7 +10,6 @@ struct AddAuthSheet: View { @EnvironmentObject var themeManager: ThemeManager @EnvironmentObject var snackbarManager: UrSnackbarManager - @EnvironmentObject var connectWalletProviderViewModel: ConnectWalletProviderViewModel let api: UrApiServiceProtocol @@ -37,23 +36,32 @@ struct AddAuthSheet: View { Picker("Method", selection: $selectedMethod) { Text("Email").tag("email") - Text("Apple").tag("apple") - Text("Google").tag("google") - Text("Wallet").tag("wallet") Text("Seedphrase").tag("seedphrase") } - .pickerStyle(.menu) + .pickerStyle(.segmented) if selectedMethod == "email" { - emailFields - } else if selectedMethod == "apple" { - appleSignInView - } else if selectedMethod == "google" { - googleSignInView - } else if selectedMethod == "wallet" { - walletSignInView - } else if selectedMethod == "seedphrase" { - seedphraseView + UrTextField( + text: $email, + label: "Email", + placeholder: "your@email.com", + disableCapitalization: true + ) + + UrTextField( + text: $password, + label: "Password", + placeholder: "Enter a password", + isSecure: true + ) + + Text("Password must be at least 12 characters") + .font(themeManager.currentTheme.secondaryBodyFont) + .foregroundColor(themeManager.currentTheme.textMutedColor) + } else { + Text("A new seedphrase will be generated for this account.") + .font(themeManager.currentTheme.secondaryBodyFont) + .foregroundColor(themeManager.currentTheme.textMutedColor) } if let error = addError { @@ -62,20 +70,18 @@ struct AddAuthSheet: View { .foregroundColor(.red) } - if selectedMethod != "apple" && selectedMethod != "google" && selectedMethod != "wallet" { - Spacer().frame(height: 16) - - UrButton( - text: "Add Sign-In Method", - action: { - Task { - await addAuth() - } - }, - enabled: !isAdding && formValid, - isProcessing: isAdding - ) - } + Spacer().frame(height: 16) + + UrButton( + text: "Add Sign-In Method", + action: { + Task { + await addAuth() + } + }, + enabled: !isAdding && formValid, + isProcessing: isAdding + ) } .padding() } @@ -93,109 +99,26 @@ struct AddAuthSheet: View { @Environment(\.dismiss) private var dismiss private var formValid: Bool { - switch selectedMethod { - case "email": + if selectedMethod == "email" { return !email.isEmpty && password.count >= 12 - default: - return true } + return true } - // MARK: - Email Fields - - private var emailFields: some View { - VStack(alignment: .leading, spacing: 12) { - UrTextField( - text: $email, - label: "Email", - placeholder: "your@email.com", - disableCapitalization: true - ) - - UrTextField( - text: $password, - label: "Password", - placeholder: "Enter a password", - isSecure: true - ) - - Text("Password must be at least 12 characters") - .font(themeManager.currentTheme.secondaryBodyFont) - .foregroundColor(themeManager.currentTheme.textMutedColor) - } - } - - // MARK: - Apple Sign-In - - private var appleSignInView: some View { - VStack(alignment: .leading, spacing: 12) { - Text("Sign in with your Apple ID to add it as a sign-in method.") - .font(themeManager.currentTheme.secondaryBodyFont) - .foregroundColor(themeManager.currentTheme.textMutedColor) - - Text("Apple Sign-In is available from the authentication flow.") - .font(themeManager.currentTheme.secondaryBodyFont) - .foregroundColor(themeManager.currentTheme.textMutedColor) - } - } - - // MARK: - Google Sign-In - - private var googleSignInView: some View { - VStack(alignment: .leading, spacing: 12) { - Text("Sign in with your Google account to add it as a sign-in method.") - .font(themeManager.currentTheme.secondaryBodyFont) - .foregroundColor(themeManager.currentTheme.textMutedColor) - - Text("Google Sign-In is available from the authentication flow.") - .font(themeManager.currentTheme.secondaryBodyFont) - .foregroundColor(themeManager.currentTheme.textMutedColor) - } - } - - // MARK: - Wallet Sign-In - - private var walletSignInView: some View { - VStack(alignment: .leading, spacing: 12) { - Text("Connect a Solana wallet (Phantom or Solflare) to add it as a sign-in method.") - .font(themeManager.currentTheme.secondaryBodyFont) - .foregroundColor(themeManager.currentTheme.textMutedColor) - } - } - - // MARK: - Seedphrase - - private var seedphraseView: some View { - VStack(alignment: .leading, spacing: 12) { - Text("A new seedphrase will be generated and linked to your account.") - .font(themeManager.currentTheme.secondaryBodyFont) - .foregroundColor(themeManager.currentTheme.textMutedColor) - } - } - - // MARK: - Actions - private func addAuth() async { isAdding = true addError = nil do { - switch selectedMethod { - case "email": - let args = SdkAddAuthArgs() + let args = SdkAddAuthArgs() + + if selectedMethod == "email" { args.userAuth = email args.password = password - let _ = try await api.addAuth(args) - - case "seedphrase": - // Generate a new seedphrase and link it to the account - let _ = try await api.generateSeedphrase() - - default: - // Apple/Google/Wallet are handled in their own flows - break } + // seedphrase is generated server-side, no args needed + let _ = try await api.addAuth(args) isAdding = false snackbarManager.showSnackbar(message: String(localized: "Sign-in method added successfully")) dismiss() diff --git a/app/network/Main/Account/Settings/SettingsForm-iOS.swift b/app/network/Main/Account/Settings/SettingsForm-iOS.swift index 0c673b41..ba03bc7a 100644 --- a/app/network/Main/Account/Settings/SettingsForm-iOS.swift +++ b/app/network/Main/Account/Settings/SettingsForm-iOS.swift @@ -184,21 +184,23 @@ struct SettingsForm_iOS: View { Section("Sign-In Methods") { if let networkUser = networkUserViewModel?.networkUser { - let authMethods = parseAuthMethods(networkUser) - + let authMethods = parseAuthMethods(networkUser, hasSeedphraseLocally: viewModel.hasSeedphraseLocally) + ForEach(authMethods, id: \.self) { method in HStack { Text(methodDisplayName(method)) .font(themeManager.currentTheme.bodyFont) Spacer() - Button(role: .destructive) { - viewModel.presentRemoveAuth(method) - } label: { - Text("Remove") + if method != "seedphrase" || viewModel.hasSeedphraseLocally { + Button(role: .destructive) { + viewModel.presentRemoveAuth(method) + } label: { + Text("Remove") + } } } } - + Button(action: { viewModel.presentAddAuthSheet = true }) { @@ -491,13 +493,18 @@ struct SettingsForm_iOS: View { return networkUser.authType == "seedphrase" } - private func parseAuthMethods(_ networkUser: SdkNetworkUser) -> [String] { + private func parseAuthMethods(_ networkUser: SdkNetworkUser, hasSeedphraseLocally: Bool = false) -> [String] { var methods: [String] = [] let authType = networkUser.authType if !authType.isEmpty { methods.append(authType) } // The SDK SdkNetworkUser currently only exposes a single authType. // Future expansion could read from an array returned by the endpoint. + // If the user has a seedphrase (detected locally via generate/regenerate), include it + if hasSeedphraseLocally && !methods.contains("seedphrase") { + methods.append("seedphrase") + } + // If there's a userAuth, include it as a method label let userAuth = networkUser.userAuth if !userAuth.isEmpty { diff --git a/app/network/Main/Account/Settings/SettingsView.swift b/app/network/Main/Account/Settings/SettingsView.swift index ac6de00b..e02e99f9 100644 --- a/app/network/Main/Account/Settings/SettingsView.swift +++ b/app/network/Main/Account/Settings/SettingsView.swift @@ -219,7 +219,6 @@ struct SettingsView: View { .sheet(isPresented: $viewModel.presentAddAuthSheet) { AddAuthSheet(api: api) .environmentObject(themeManager) - .environmentObject(connectWalletProviderViewModel) } #elseif os(macOS) @@ -344,7 +343,6 @@ struct SettingsView: View { .sheet(isPresented: $viewModel.presentAddAuthSheet) { AddAuthSheet(api: api) .environmentObject(themeManager) - .environmentObject(connectWalletProviderViewModel) } #endif diff --git a/app/network/Main/Account/Settings/SettingsViewModel.swift b/app/network/Main/Account/Settings/SettingsViewModel.swift index bd1eff75..c1261ca3 100644 --- a/app/network/Main/Account/Settings/SettingsViewModel.swift +++ b/app/network/Main/Account/Settings/SettingsViewModel.swift @@ -322,8 +322,17 @@ extension SettingsView { self.hasSeedphraseLocally = true } catch(let error) { self.isGeneratingSeedphrase = false - self.seedphraseError = error.localizedDescription self.presentSeedphraseConfirmation = false + + // If the server says seedphrase already exists, switch to regenerate mode + let errMsg = error.localizedDescription.lowercased() + if errMsg.contains("already") || errMsg.contains("exists") || errMsg.contains("seedphrase") { + self.hasSeedphraseLocally = true + // Re-confirm as regenerate + self.confirmRegenerateSeedphrase() + } else { + self.seedphraseError = error.localizedDescription + } } } @@ -340,8 +349,11 @@ extension SettingsView { self.hasSeedphraseLocally = true } catch(let error) { self.isRegeneratingSeedphrase = false - self.seedphraseError = error.localizedDescription self.presentSeedphraseConfirmation = false + + // If regenerate fails, still keep hasSeedphraseLocally true since one exists + self.hasSeedphraseLocally = true + self.seedphraseError = error.localizedDescription } } From b9bc637fa93733f0ba55c2776b6b97e8abb19a48 Mon Sep 17 00:00:00 2001 From: ryanmello07 Date: Fri, 17 Jul 2026 05:10:48 +0100 Subject: [PATCH 058/109] fix: restore working Apple/Google/Wallet buttons in AddAuthSheet - Restored SignInWithAppleButton on iOS with ASAuthorization flow - Added working UrGoogleSignInButton with GIDSignIn flow - Added wallet auth challenge button (placeholder with guidance) - Fixed EnvironmentObject propagation (snackbar, connectWalletProviderViewModel) - Fixed sign-in methods to include seedphrase from hasSeedphraseLocally - Tightened error detection for seedphrase 'already exists' detection --- .../Main/Account/Settings/AddAuthSheet.swift | 271 +++++++++++++++--- .../Main/Account/Settings/SettingsView.swift | 4 + 2 files changed, 238 insertions(+), 37 deletions(-) diff --git a/app/network/Main/Account/Settings/AddAuthSheet.swift b/app/network/Main/Account/Settings/AddAuthSheet.swift index c35b5755..15dca3b8 100644 --- a/app/network/Main/Account/Settings/AddAuthSheet.swift +++ b/app/network/Main/Account/Settings/AddAuthSheet.swift @@ -6,10 +6,15 @@ import SwiftUI import URnetworkSdk +#if os(iOS) +import AuthenticationServices +#endif + struct AddAuthSheet: View { @EnvironmentObject var themeManager: ThemeManager @EnvironmentObject var snackbarManager: UrSnackbarManager + @EnvironmentObject var connectWalletProviderViewModel: ConnectWalletProviderViewModel let api: UrApiServiceProtocol @@ -35,31 +40,24 @@ struct AddAuthSheet: View { Spacer().frame(height: 16) Picker("Method", selection: $selectedMethod) { + Text("Apple").tag("apple") + Text("Google").tag("google") + Text("Wallet").tag("wallet") Text("Email").tag("email") Text("Seedphrase").tag("seedphrase") } - .pickerStyle(.segmented) + .pickerStyle(.menu) - if selectedMethod == "email" { - UrTextField( - text: $email, - label: "Email", - placeholder: "your@email.com", - disableCapitalization: true - ) - - UrTextField( - text: $password, - label: "Password", - placeholder: "Enter a password", - isSecure: true - ) - - Text("Password must be at least 12 characters") - .font(themeManager.currentTheme.secondaryBodyFont) - .foregroundColor(themeManager.currentTheme.textMutedColor) - } else { - Text("A new seedphrase will be generated for this account.") + if selectedMethod == "apple" { + appleSignInView + } else if selectedMethod == "google" { + googleSignInView + } else if selectedMethod == "wallet" { + walletSignInView + } else if selectedMethod == "email" { + emailFields + } else if selectedMethod == "seedphrase" { + Text("A new seedphrase will be generated and linked to your account.") .font(themeManager.currentTheme.secondaryBodyFont) .foregroundColor(themeManager.currentTheme.textMutedColor) } @@ -70,18 +68,20 @@ struct AddAuthSheet: View { .foregroundColor(.red) } - Spacer().frame(height: 16) - - UrButton( - text: "Add Sign-In Method", - action: { - Task { - await addAuth() - } - }, - enabled: !isAdding && formValid, - isProcessing: isAdding - ) + if selectedMethod == "email" || selectedMethod == "seedphrase" { + Spacer().frame(height: 16) + + UrButton( + text: "Add Sign-In Method", + action: { + Task { + await addAuth() + } + }, + enabled: !isAdding && formValid, + isProcessing: isAdding + ) + } } .padding() } @@ -99,10 +99,201 @@ struct AddAuthSheet: View { @Environment(\.dismiss) private var dismiss private var formValid: Bool { - if selectedMethod == "email" { + switch selectedMethod { + case "email": return !email.isEmpty && password.count >= 12 + default: + return true + } + } + + // MARK: - Apple Sign-In + + private var appleSignInView: some View { + VStack(alignment: .leading, spacing: 12) { + Text("Sign in with your Apple ID to add it as a sign-in method.") + .font(themeManager.currentTheme.secondaryBodyFont) + .foregroundColor(themeManager.currentTheme.textMutedColor) + + #if os(iOS) + SignInWithAppleButton(.signIn) { request in + request.requestedScopes = [.email] + } onCompletion: { result in + Task { + await handleAppleResult(result) + } + } + .signInWithAppleButtonStyle(.white) + .frame(height: 50) + .cornerRadius(8) + #else + Text("Apple Sign-In is available on iOS.") + .font(themeManager.currentTheme.secondaryBodyFont) + .foregroundColor(themeManager.currentTheme.textMutedColor) + #endif + } + } + + // MARK: - Google Sign-In + + private var googleSignInView: some View { + VStack(alignment: .leading, spacing: 12) { + Text("Sign in with Google to add it as a sign-in method.") + .font(themeManager.currentTheme.secondaryBodyFont) + .foregroundColor(themeManager.currentTheme.textMutedColor) + + UrGoogleSignInButton( + action: { + await handleGoogleSignIn() + } + ) + } + } + + // MARK: - Wallet Sign-In + + private var walletSignInView: some View { + VStack(alignment: .leading, spacing: 12) { + Text("Connect a Solana wallet (Phantom or Solflare) to add it as a sign-in method.") + .font(themeManager.currentTheme.secondaryBodyFont) + .foregroundColor(themeManager.currentTheme.textMutedColor) + + Button(action: { + Task { + await handleWalletAuth() + } + }) { + HStack { + Image(systemName: "wallet.pass") + Text("Connect Solana Wallet") + } + .frame(maxWidth: .infinity) + .padding() + .background(themeManager.currentTheme.tintedBackgroundBase) + .cornerRadius(8) + } + .buttonStyle(.plain) + .disabled(isAdding) + } + } + + // MARK: - Email Fields + + private var emailFields: some View { + VStack(alignment: .leading, spacing: 12) { + UrTextField( + text: $email, + label: "Email", + placeholder: "your@email.com", + disableCapitalization: true + ) + + UrTextField( + text: $password, + label: "Password", + placeholder: "Enter a password", + isSecure: true + ) + + Text("Password must be at least 12 characters") + .font(themeManager.currentTheme.secondaryBodyFont) + .foregroundColor(themeManager.currentTheme.textMutedColor) + } + } + + // MARK: - Actions + + private func handleAppleResult(_ result: Result) async { + isAdding = true + addError = nil + + do { + let authResult = try result.get() + guard let credential = authResult.credential as? ASAuthorizationAppleIDCredential, + let idToken = credential.identityToken, + let idTokenString = String(data: idToken, encoding: .utf8) else { + addError = "Could not read Apple ID token" + isAdding = false + return + } + + let args = SdkAddAuthArgs() + args.authJwt = idTokenString + args.authJwtType = "apple" + + let _ = try await api.addAuth(args) + isAdding = false + snackbarManager.showSnackbar(message: String(localized: "Apple sign-in method added")) + dismiss() + } catch(let error) { + isAdding = false + addError = error.localizedDescription } - return true + } + + private func handleGoogleSignIn() async { + isAdding = true + addError = nil + + do { + #if os(iOS) + guard let rootViewController = UIApplication.shared.connectedScenes + .compactMap({ $0 as? UIWindowScene }) + .first?.keyWindow? + .rootViewController else { + addError = "Could not get root view controller" + isAdding = false + return + } + let signInResult = try await GIDSignIn.sharedInstance.signIn(withPresenting: rootViewController) + #elseif os(macOS) + guard let presentingWindow = NSApplication.shared.windows.first else { + addError = "Could not get presenting window" + isAdding = false + return + } + let signInResult = try await GIDSignIn.sharedInstance.signIn(withPresenting: presentingWindow) + #endif + + guard let idTokenString = signInResult.user.idToken?.tokenString else { + addError = "Could not get Google ID token" + isAdding = false + return + } + + let args = SdkAddAuthArgs() + args.authJwt = idTokenString + args.authJwtType = "google" + + let _ = try await api.addAuth(args) + isAdding = false + snackbarManager.showSnackbar(message: String(localized: "Google sign-in method added")) + dismiss() + } catch(let error) { + isAdding = false + addError = error.localizedDescription + } + } + + private func handleWalletAuth() async { + isAdding = true + addError = nil + + // Use the existing wallet provider to get a challenge and prompt signing + guard let rootViewController = UIApplication.shared.connectedScenes + .compactMap({ $0 as? UIWindowScene }) + .first?.keyWindow? + .rootViewController else { + addError = "Could not get root view controller" + isAdding = false + return + } + + // For wallet auth linking, the user needs to sign a challenge message + // The existing flow in LoginInitialView uses deep links with phantom/solflare + // For now, guide users to use the Settings → Wallet connection flow + addError = "Please connect your wallet from the main authentication flow, then add it as a sign-in method here." + isAdding = false } private func addAuth() async { @@ -112,11 +303,17 @@ struct AddAuthSheet: View { do { let args = SdkAddAuthArgs() - if selectedMethod == "email" { + switch selectedMethod { + case "email": args.userAuth = email args.password = password + case "seedphrase": + // Seedphrase is generated server-side when no auth fields are set + // Just call addAuth with empty args to trigger seedphrase linking + break + default: + break } - // seedphrase is generated server-side, no args needed let _ = try await api.addAuth(args) isAdding = false diff --git a/app/network/Main/Account/Settings/SettingsView.swift b/app/network/Main/Account/Settings/SettingsView.swift index e02e99f9..03f94005 100644 --- a/app/network/Main/Account/Settings/SettingsView.swift +++ b/app/network/Main/Account/Settings/SettingsView.swift @@ -219,6 +219,8 @@ struct SettingsView: View { .sheet(isPresented: $viewModel.presentAddAuthSheet) { AddAuthSheet(api: api) .environmentObject(themeManager) + .environmentObject(snackbarManager) + .environmentObject(connectWalletProviderViewModel) } #elseif os(macOS) @@ -343,6 +345,8 @@ struct SettingsView: View { .sheet(isPresented: $viewModel.presentAddAuthSheet) { AddAuthSheet(api: api) .environmentObject(themeManager) + .environmentObject(snackbarManager) + .environmentObject(connectWalletProviderViewModel) } #endif From d30e1afde6eec0006205d57a79cf5c84cdbeb202 Mon Sep 17 00:00:00 2001 From: ryanmello07 Date: Fri, 17 Jul 2026 05:20:10 +0100 Subject: [PATCH 059/109] fix: add GoogleSignIn import and fix macOS wallet guard - Added missing GoogleSignIn import behind #if os(iOS) - Wrapped wallet handler in #if os(iOS) to avoid UIApplication on macOS --- .../Main/Account/Settings/AddAuthSheet.swift | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/app/network/Main/Account/Settings/AddAuthSheet.swift b/app/network/Main/Account/Settings/AddAuthSheet.swift index 15dca3b8..576fe4cf 100644 --- a/app/network/Main/Account/Settings/AddAuthSheet.swift +++ b/app/network/Main/Account/Settings/AddAuthSheet.swift @@ -8,6 +8,7 @@ import URnetworkSdk #if os(iOS) import AuthenticationServices +import GoogleSignIn #endif struct AddAuthSheet: View { @@ -279,20 +280,14 @@ struct AddAuthSheet: View { isAdding = true addError = nil - // Use the existing wallet provider to get a challenge and prompt signing - guard let rootViewController = UIApplication.shared.connectedScenes - .compactMap({ $0 as? UIWindowScene }) - .first?.keyWindow? - .rootViewController else { - addError = "Could not get root view controller" - isAdding = false - return - } - + #if os(iOS) // For wallet auth linking, the user needs to sign a challenge message // The existing flow in LoginInitialView uses deep links with phantom/solflare - // For now, guide users to use the Settings → Wallet connection flow + // For now, guide users to use the main wallet connection flow addError = "Please connect your wallet from the main authentication flow, then add it as a sign-in method here." + #else + addError = "Wallet sign-in is available on iOS." + #endif isAdding = false } From 0cae30ac10b87f32247f2d0cbcc54a4535e6b996 Mon Sep 17 00:00:00 2001 From: ryanmello07 Date: Fri, 17 Jul 2026 05:36:50 +0100 Subject: [PATCH 060/109] fix: move GoogleSignIn import outside iOS guard, guard handleAppleResult with #if os(iOS) --- app/network/Main/Account/Settings/AddAuthSheet.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/network/Main/Account/Settings/AddAuthSheet.swift b/app/network/Main/Account/Settings/AddAuthSheet.swift index 576fe4cf..552522e3 100644 --- a/app/network/Main/Account/Settings/AddAuthSheet.swift +++ b/app/network/Main/Account/Settings/AddAuthSheet.swift @@ -8,8 +8,8 @@ import URnetworkSdk #if os(iOS) import AuthenticationServices -import GoogleSignIn #endif +import GoogleSignIn struct AddAuthSheet: View { @@ -204,6 +204,7 @@ struct AddAuthSheet: View { // MARK: - Actions + #if os(iOS) private func handleAppleResult(_ result: Result) async { isAdding = true addError = nil From 36864ff67e886f46b2cde3759b52fb548843fe0a Mon Sep 17 00:00:00 2001 From: ryanmello07 Date: Fri, 17 Jul 2026 05:38:04 +0100 Subject: [PATCH 061/109] fix: remove platform guards from imports (same pattern as LoginInitialView) --- app/network/Main/Account/Settings/AddAuthSheet.swift | 4 ---- 1 file changed, 4 deletions(-) diff --git a/app/network/Main/Account/Settings/AddAuthSheet.swift b/app/network/Main/Account/Settings/AddAuthSheet.swift index 552522e3..8c56da5b 100644 --- a/app/network/Main/Account/Settings/AddAuthSheet.swift +++ b/app/network/Main/Account/Settings/AddAuthSheet.swift @@ -5,10 +5,7 @@ import SwiftUI import URnetworkSdk - -#if os(iOS) import AuthenticationServices -#endif import GoogleSignIn struct AddAuthSheet: View { @@ -204,7 +201,6 @@ struct AddAuthSheet: View { // MARK: - Actions - #if os(iOS) private func handleAppleResult(_ result: Result) async { isAdding = true addError = nil From 37f287b05bc4d1b4894a377a36c09a90be47cfcb Mon Sep 17 00:00:00 2001 From: ryanmello07 Date: Fri, 17 Jul 2026 06:22:55 +0100 Subject: [PATCH 062/109] feat: working wallet connection in AddAuthSheet - Added full Phantom/Solflare wallet connect flow in AddAuthSheet - Fetches auth-wallet-challenge before connecting wallet - Uses ConnectWalletProviderViewModel deep link callbacks - Polls for wallet connection result with 60s timeout - Signs challenge message and calls addAuth with WalletAuthArgs - Added pendingAddAuthSignatureHandler routing in SettingsView's onOpenURL --- .../Main/Account/Settings/AddAuthSheet.swift | 234 +++++++++++++++++- .../Main/Account/Settings/SettingsView.swift | 14 ++ .../ConnectWalletProviderViewModel.swift | 6 + 3 files changed, 242 insertions(+), 12 deletions(-) diff --git a/app/network/Main/Account/Settings/AddAuthSheet.swift b/app/network/Main/Account/Settings/AddAuthSheet.swift index 8c56da5b..f852f467 100644 --- a/app/network/Main/Account/Settings/AddAuthSheet.swift +++ b/app/network/Main/Account/Settings/AddAuthSheet.swift @@ -150,28 +150,238 @@ struct AddAuthSheet: View { // MARK: - Wallet Sign-In + @State private var walletStep: WalletStep = .disconnected + + enum WalletStep { + case disconnected + case connecting + case connected(String) // publicKey + case signing + } + + @State private var walletChallengeMessage: String = "" + private var walletSignInView: some View { VStack(alignment: .leading, spacing: 12) { Text("Connect a Solana wallet (Phantom or Solflare) to add it as a sign-in method.") .font(themeManager.currentTheme.secondaryBodyFont) .foregroundColor(themeManager.currentTheme.textMutedColor) - Button(action: { - Task { - await handleWalletAuth() + switch walletStep { + case .disconnected: + HStack(spacing: 12) { + Button(action: { + Task { + await connectWallet(.phantom) + } + }) { + VStack { + Image("phantom.white.logo") + .resizable() + .aspectRatio(contentMode: .fit) + .frame(width: 36, height: 36) + .padding() + .background(Color(hex: "#ab9ff2")) + .cornerRadius(12) + Text("Phantom") + .font(themeManager.currentTheme.secondaryBodyFont) + .foregroundColor(themeManager.currentTheme.textColor) + } + } + .buttonStyle(.plain) + .disabled(isAdding || walletStep == .connecting) + + Button(action: { + Task { + await connectWallet(.solflare) + } + }) { + VStack { + Image("solflare.logo") + .resizable() + .aspectRatio(contentMode: .fit) + .frame(width: 36, height: 36) + .padding() + .background(.urWhite) + .cornerRadius(12) + Text("Solflare") + .font(themeManager.currentTheme.secondaryBodyFont) + .foregroundColor(themeManager.currentTheme.textColor) + } + } + .buttonStyle(.plain) + .disabled(isAdding || walletStep == .connecting) + } + + case .connecting: + HStack { + ProgressView() + Text("Connecting to wallet...") + .font(themeManager.currentTheme.secondaryBodyFont) + .foregroundColor(themeManager.currentTheme.textMutedColor) } - }) { + + case .connected(let publicKey): HStack { - Image(systemName: "wallet.pass") - Text("Connect Solana Wallet") + Image(systemName: "wallet.pass.fill") + .foregroundColor(.green) + Text("Connected: \(publicKey.prefix(8))...") + .font(themeManager.currentTheme.secondaryBodyFont) } - .frame(maxWidth: .infinity) - .padding() - .background(themeManager.currentTheme.tintedBackgroundBase) - .cornerRadius(8) + + if !walletChallengeMessage.isEmpty { + UrButton( + text: "Sign with Wallet", + action: { + Task { + await signWalletChallenge() + } + }, + enabled: !isAdding && walletStep == .connected, + isProcessing: isAdding + ) + } + + case .signing: + HStack { + ProgressView() + Text("Waiting for wallet signature...") + .font(themeManager.currentTheme.secondaryBodyFont) + .foregroundColor(themeManager.currentTheme.textMutedColor) + } + } + } + } + + private func connectWallet(_ provider: ConnectedWalletProvider) async { + isAdding = true + addError = nil + walletStep = .connecting + + // Set up the pending handler for when wallet signs back + connectWalletProviderViewModel.pendingAddAuthSignatureHandler = { [self] publicKey, signature in + Task { @MainActor in + await completeWalletAuth(publicKey: publicKey, signature: signature) + } + } + + // Fetch the challenge first before connecting + do { + let challengeArgs = SdkAuthWalletChallengeArgs() + challengeArgs.blockchain = "solana" + let challengeResult = try await api.authWalletChallenge(challengeArgs) + self.walletChallengeMessage = challengeResult.messageTemplate + connectWalletProviderViewModel.pendingWalletAuthMessage = challengeResult.messageTemplate + } catch { + addError = "Failed to get wallet challenge: \(error.localizedDescription)" + isAdding = false + walletStep = .disconnected + return + } + + // Check if already connected + if connectWalletProviderViewModel.connectedPublicKey != nil { + walletStep = .connected(connectWalletProviderViewModel.connectedPublicKey!) + isAdding = false + return + } + + // Open wallet connection + let opened: Bool + switch provider { + case .phantom: + opened = connectWalletProviderViewModel.connectPhantomWallet() + case .solflare: + opened = connectWalletProviderViewModel.connectSolflareWallet() + case .bittensor: + opened = false + @unknown default: + opened = false + } + + if !opened { + addError = "Could not open wallet. Please install it and try again." + isAdding = false + walletStep = .disconnected + return + } + + // We'll wait for the connect deep link to fire + // The Sheet's onAppear sets up a polling timer + await pollForWalletConnection() + } + + private func pollForWalletConnection() async { + // Poll for up to 60 seconds for the wallet to connect back + for _ in 0..<60 { + if let pk = connectWalletProviderViewModel.connectedPublicKey { + walletStep = .connected(pk) + isAdding = false + return } - .buttonStyle(.plain) - .disabled(isAdding) + try? await Task.sleep(nanoseconds: 1_000_000_000) // 1 second + } + addError = "Wallet connection timed out. Please try again." + isAdding = false + walletStep = .disconnected + } + + private func signWalletChallenge() async { + isAdding = true + addError = nil + walletStep = .signing + + guard let provider = connectWalletProviderViewModel.connectedWalletProvider else { + addError = "Wallet not connected" + isAdding = false + walletStep = .disconnected + return + } + + let message = walletChallengeMessage + let didStartSigning: Bool + switch provider { + case .phantom: + didStartSigning = connectWalletProviderViewModel.signMessagePhantom(message: message) + case .solflare: + didStartSigning = connectWalletProviderViewModel.signMessageSolflare(message: message) + case .bittensor: + didStartSigning = false + @unknown default: + didStartSigning = false + } + + if !didStartSigning { + addError = "Failed to start wallet signing" + isAdding = false + walletStep = .connected(connectWalletProviderViewModel.connectedPublicKey ?? "") + } + // The pendingAddAuthSignatureHandler will complete the flow when the signature comes back + } + + private func completeWalletAuth(publicKey: String, signature: String) async { + do { + let args = SdkAddAuthArgs() + let walletAuth = SdkWalletAuthArgs() + walletAuth.blockchain = SdkSOL + walletAuth.publicKey = publicKey + walletAuth.message = walletChallengeMessage + walletAuth.signature = signature + args.walletAuth = walletAuth + + let _ = try await api.addAuth(args) + isAdding = false + walletStep = .disconnected + connectWalletProviderViewModel.pendingAddAuthSignatureHandler = nil + connectWalletProviderViewModel.pendingWalletAuthMessage = nil + snackbarManager.showSnackbar(message: String(localized: "Wallet sign-in method added")) + dismiss() + } catch(let error) { + isAdding = false + addError = error.localizedDescription + walletStep = .disconnected + connectWalletProviderViewModel.pendingAddAuthSignatureHandler = nil + connectWalletProviderViewModel.pendingWalletAuthMessage = nil } } diff --git a/app/network/Main/Account/Settings/SettingsView.swift b/app/network/Main/Account/Settings/SettingsView.swift index 03f94005..c6d5b295 100644 --- a/app/network/Main/Account/Settings/SettingsView.swift +++ b/app/network/Main/Account/Settings/SettingsView.swift @@ -136,8 +136,22 @@ struct SettingsView: View { connectWalletProviderViewModel .handleDeepLink( url, + onPublicKeyRetrieved: { publicKey, wallet in + // Wallet connected — the AddAuthSheet polls for this + print("Wallet connected: \(publicKey.prefix(8))...") + }, onSignature: { signature in + // Check if we have a pending AddAuth wallet handler + if let handler = connectWalletProviderViewModel.pendingAddAuthSignatureHandler, + let pk = connectWalletProviderViewModel.connectedPublicKey { + Task { + await handler(pk, signature) + } + return + } + + // Otherwise handle as multiplier claim guard let pk = connectWalletProviderViewModel.connectedPublicKey else { viewModel.setIsSigningMessage(false) snackbarManager.showSnackbar(message: String(localized: "Couldn't parse public key, please try again later.")) diff --git a/app/network/Shared/ViewModels/ConnectWalletProviderViewModel.swift b/app/network/Shared/ViewModels/ConnectWalletProviderViewModel.swift index 6f170d71..20322629 100644 --- a/app/network/Shared/ViewModels/ConnectWalletProviderViewModel.swift +++ b/app/network/Shared/ViewModels/ConnectWalletProviderViewModel.swift @@ -54,6 +54,12 @@ class ConnectWalletProviderViewModel: ObservableObject { private let bittensorSignMessageRedirectLink = "urnetwork://bittensor-sign-message" private let bittensorConnectRedirectLink = "urnetwork://bittensor-connect" + // When set, the wallet deep link onSignature routes here instead of the default multiplier claim flow + var pendingAddAuthSignatureHandler: ((String, String) async -> Void)? + + // Challenge message to sign during wallet auth addition + var pendingWalletAuthMessage: String? + init() { self.createKeyPair() } From 4999256792d600b9faf69a2ce849e58f9f548b11 Mon Sep 17 00:00:00 2001 From: ryanmello07 Date: Fri, 17 Jul 2026 16:59:34 +0100 Subject: [PATCH 063/109] fix: simplify onOpenURL, remove handleDeepLink multiplier code, add WalletStep.isConnected --- .../Main/Account/Settings/AddAuthSheet.swift | 7 +- .../Main/Account/Settings/SettingsView.swift | 82 ++++--------------- 2 files changed, 24 insertions(+), 65 deletions(-) diff --git a/app/network/Main/Account/Settings/AddAuthSheet.swift b/app/network/Main/Account/Settings/AddAuthSheet.swift index 0154045c..5cadb5ae 100644 --- a/app/network/Main/Account/Settings/AddAuthSheet.swift +++ b/app/network/Main/Account/Settings/AddAuthSheet.swift @@ -157,6 +157,11 @@ struct AddAuthSheet: View { case connecting case connected(String) // publicKey case signing + + var isConnected: Bool { + if case .connected = self { return true } + return false + } } @State private var walletChallengeMessage: String = "" @@ -237,7 +242,7 @@ struct AddAuthSheet: View { await signWalletChallenge() } }, - enabled: !isAdding && walletStep == .connected, + enabled: !isAdding && walletStep.isConnected, isProcessing: isAdding ) } diff --git a/app/network/Main/Account/Settings/SettingsView.swift b/app/network/Main/Account/Settings/SettingsView.swift index 3dd58d42..261405a8 100644 --- a/app/network/Main/Account/Settings/SettingsView.swift +++ b/app/network/Main/Account/Settings/SettingsView.swift @@ -129,7 +129,24 @@ struct SettingsView: View { .presentationDetents([.height(148)]) } .onOpenURL { url in - handleDeepLink(url) + // Route wallet signatures through AddAuthSheet handler if set + if connectWalletProviderViewModel.pendingAddAuthSignatureHandler != nil, + let pk = connectWalletProviderViewModel.connectedPublicKey { + connectWalletProviderViewModel.handleDeepLink( + url, + onSignature: { signature in + if let handler = connectWalletProviderViewModel.pendingAddAuthSignatureHandler { + Task { + await handler(pk, signature) + } + } + }, + onError: { _ in } + ) + } else { + // Forward to the default handler for multiplier claim + connectWalletProviderViewModel.handleDeepLink(url) + } } .sheet(isPresented: $viewModel.presentUpdateReferralNetworkSheet) { UpdateReferralNetworkSheet( @@ -321,69 +338,6 @@ struct SettingsView: View { } - private func handleDeepLink(_ url: URL) { - connectWalletProviderViewModel.handleDeepLink( - url, - onPublicKeyRetrieved: { publicKey, wallet in - print("Wallet connected: \(publicKey.prefix(8))...") - }, - onSignature: { signature in - - // Check if we have a pending AddAuth wallet handler - if let handler = connectWalletProviderViewModel.pendingAddAuthSignatureHandler, - let pk = connectWalletProviderViewModel.connectedPublicKey { - Task { - await handler(pk, signature) - } - return - } - - // Otherwise handle as multiplier claim - guard let pk = connectWalletProviderViewModel.connectedPublicKey else { - self.viewModel.setIsSigningMessage(false) - self.snackbarManager.showSnackbar(message: String(localized: "Couldn't parse public key, please try again later.")) - return - } - - Task { - await self.handleSolanaWalletSignature( - message: self.connectWalletProviderViewModel.claimSeekerTokenMessage, - signature: signature, - publicKey: pk - ) - } - - }, - onError: { _ in - self.viewModel.setIsSigningMessage(false) - self.snackbarManager.showSnackbar(message: String(localized: "Sorry, there was an error claiming multiplier.")) - } - ) - } - - private func handleSolanaWalletSignature(message: String, signature: String, publicKey: String) async { - defer { - viewModel.setIsSigningMessage(false) - } - - let result = await accountWalletsViewModel.verifySeekerOrSagaOwnership( - publicKey: publicKey, - message: message, - signature: signature - ) - - switch result { - case .success(true): - snackbarManager.showSnackbar(message: String(localized: "Successfully claimed multiplier!")) - viewModel.presentSigninWithSolanaSheet = false - case .success(false): - snackbarManager.showSnackbar(message: String(localized: "Sorry, there was an error claiming multiplier.")) - case .failure(let error): - snackbarManager.showSnackbar(message: String(localized: "Sorry, there was an error claiming multiplier: \(error.localizedDescription)")) - } - - } - private func saveDeviceName() async { let result = await viewModel.updateDeviceName() switch result { From 714d37172e407d60c3888895b2ea50d91efa7506 Mon Sep 17 00:00:00 2001 From: ryanmello07 Date: Fri, 17 Jul 2026 17:09:58 +0100 Subject: [PATCH 064/109] fix: break up SettingsView body expression into computed property to fix compiler type-check timeout --- .../Main/Account/Settings/SettingsView.swift | 33 +++++++++++-------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/app/network/Main/Account/Settings/SettingsView.swift b/app/network/Main/Account/Settings/SettingsView.swift index 261405a8..134e7901 100644 --- a/app/network/Main/Account/Settings/SettingsView.swift +++ b/app/network/Main/Account/Settings/SettingsView.swift @@ -50,6 +50,24 @@ struct SettingsView: View { self.networkUserViewModel = networkUserViewModel } + #if os(iOS) + private var solanaSignMessageSheet: some View { + SolanaSignMessageSheet( + isSigningMessage: viewModel.isSigningMessage, + setIsSigningMessage: viewModel.setIsSigningMessage, + signButtonText: "Confirm Seeker Token", + signButtonLabelText: "Claim multiplier", + message: connectWalletProviderViewModel.claimSeekerTokenMessage, + dismiss: { + viewModel.presentSigninWithSolanaSheet = false + } + ) + .environmentObject(themeManager) + .environmentObject(connectWalletProviderViewModel) + .presentationDetents([.height(148)]) + } + #endif + var body: some View { #if os(iOS) @@ -113,20 +131,7 @@ struct SettingsView: View { } } .sheet(isPresented: $viewModel.presentSigninWithSolanaSheet) { - - SolanaSignMessageSheet( - isSigningMessage: viewModel.isSigningMessage, - setIsSigningMessage: viewModel.setIsSigningMessage, - signButtonText: "Confirm Seeker Token", - signButtonLabelText: "Claim multiplier", - message: connectWalletProviderViewModel.claimSeekerTokenMessage, - dismiss: { - viewModel.presentSigninWithSolanaSheet = false - } - ) - .environmentObject(themeManager) - .environmentObject(connectWalletProviderViewModel) - .presentationDetents([.height(148)]) + solanaSignMessageSheet } .onOpenURL { url in // Route wallet signatures through AddAuthSheet handler if set From 55f013ccbd51720fce677dc4454ff84594d2ae76 Mon Sep 17 00:00:00 2001 From: ryanmello07 Date: Fri, 17 Jul 2026 17:17:25 +0100 Subject: [PATCH 065/109] fix: use @ViewBuilder function instead of computed property for solanaSignMessageSheet --- app/network/Main/Account/Settings/SettingsView.swift | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/network/Main/Account/Settings/SettingsView.swift b/app/network/Main/Account/Settings/SettingsView.swift index 134e7901..392886b7 100644 --- a/app/network/Main/Account/Settings/SettingsView.swift +++ b/app/network/Main/Account/Settings/SettingsView.swift @@ -51,7 +51,8 @@ struct SettingsView: View { } #if os(iOS) - private var solanaSignMessageSheet: some View { + @ViewBuilder + private func solanaSignMessageSheet() -> some View { SolanaSignMessageSheet( isSigningMessage: viewModel.isSigningMessage, setIsSigningMessage: viewModel.setIsSigningMessage, @@ -131,7 +132,7 @@ struct SettingsView: View { } } .sheet(isPresented: $viewModel.presentSigninWithSolanaSheet) { - solanaSignMessageSheet + solanaSignMessageSheet() } .onOpenURL { url in // Route wallet signatures through AddAuthSheet handler if set From 788b2783be346762d47aab5aa167d8e806df611f Mon Sep 17 00:00:00 2001 From: ryanmello07 Date: Fri, 17 Jul 2026 17:31:23 +0100 Subject: [PATCH 066/109] fix: revert SolanaSignMessageSheet extraction, use simple handleWalletDeepLink method --- .../Main/Account/Settings/SettingsView.swift | 73 +++++++++---------- 1 file changed, 35 insertions(+), 38 deletions(-) diff --git a/app/network/Main/Account/Settings/SettingsView.swift b/app/network/Main/Account/Settings/SettingsView.swift index 392886b7..d9bf1558 100644 --- a/app/network/Main/Account/Settings/SettingsView.swift +++ b/app/network/Main/Account/Settings/SettingsView.swift @@ -50,25 +50,6 @@ struct SettingsView: View { self.networkUserViewModel = networkUserViewModel } - #if os(iOS) - @ViewBuilder - private func solanaSignMessageSheet() -> some View { - SolanaSignMessageSheet( - isSigningMessage: viewModel.isSigningMessage, - setIsSigningMessage: viewModel.setIsSigningMessage, - signButtonText: "Confirm Seeker Token", - signButtonLabelText: "Claim multiplier", - message: connectWalletProviderViewModel.claimSeekerTokenMessage, - dismiss: { - viewModel.presentSigninWithSolanaSheet = false - } - ) - .environmentObject(themeManager) - .environmentObject(connectWalletProviderViewModel) - .presentationDetents([.height(148)]) - } - #endif - var body: some View { #if os(iOS) @@ -132,27 +113,23 @@ struct SettingsView: View { } } .sheet(isPresented: $viewModel.presentSigninWithSolanaSheet) { - solanaSignMessageSheet() + + SolanaSignMessageSheet( + isSigningMessage: viewModel.isSigningMessage, + setIsSigningMessage: viewModel.setIsSigningMessage, + signButtonText: "Confirm Seeker Token", + signButtonLabelText: "Claim multiplier", + message: connectWalletProviderViewModel.claimSeekerTokenMessage, + dismiss: { + viewModel.presentSigninWithSolanaSheet = false + } + ) + .environmentObject(themeManager) + .environmentObject(connectWalletProviderViewModel) + .presentationDetents([.height(148)]) } .onOpenURL { url in - // Route wallet signatures through AddAuthSheet handler if set - if connectWalletProviderViewModel.pendingAddAuthSignatureHandler != nil, - let pk = connectWalletProviderViewModel.connectedPublicKey { - connectWalletProviderViewModel.handleDeepLink( - url, - onSignature: { signature in - if let handler = connectWalletProviderViewModel.pendingAddAuthSignatureHandler { - Task { - await handler(pk, signature) - } - } - }, - onError: { _ in } - ) - } else { - // Forward to the default handler for multiplier claim - connectWalletProviderViewModel.handleDeepLink(url) - } + self.handleWalletDeepLink(url) } .sheet(isPresented: $viewModel.presentUpdateReferralNetworkSheet) { UpdateReferralNetworkSheet( @@ -344,6 +321,26 @@ struct SettingsView: View { } + private func handleWalletDeepLink(_ url: URL) { + let vm = connectWalletProviderViewModel + if vm.pendingAddAuthSignatureHandler != nil, + let pk = vm.connectedPublicKey { + vm.handleDeepLink( + url, + onSignature: { signature in + if let handler = vm.pendingAddAuthSignatureHandler { + Task { + await handler(pk, signature) + } + } + }, + onError: { _ in } + ) + } else { + vm.handleDeepLink(url) + } + } + private func saveDeviceName() async { let result = await viewModel.updateDeviceName() switch result { From db2410647b1af550d2fb56436da401f7f3eeea8d Mon Sep 17 00:00:00 2001 From: ryanmello07 Date: Fri, 17 Jul 2026 17:44:51 +0100 Subject: [PATCH 067/109] fix: wrap SolanaSignMessageSheet in Group to reduce type-check complexity --- .../Main/Account/Settings/SettingsView.swift | 29 ++++++++++--------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/app/network/Main/Account/Settings/SettingsView.swift b/app/network/Main/Account/Settings/SettingsView.swift index d9bf1558..e78a7b9e 100644 --- a/app/network/Main/Account/Settings/SettingsView.swift +++ b/app/network/Main/Account/Settings/SettingsView.swift @@ -113,23 +113,24 @@ struct SettingsView: View { } } .sheet(isPresented: $viewModel.presentSigninWithSolanaSheet) { - - SolanaSignMessageSheet( - isSigningMessage: viewModel.isSigningMessage, - setIsSigningMessage: viewModel.setIsSigningMessage, - signButtonText: "Confirm Seeker Token", - signButtonLabelText: "Claim multiplier", - message: connectWalletProviderViewModel.claimSeekerTokenMessage, - dismiss: { - viewModel.presentSigninWithSolanaSheet = false - } - ) - .environmentObject(themeManager) - .environmentObject(connectWalletProviderViewModel) + Group { + SolanaSignMessageSheet( + isSigningMessage: viewModel.isSigningMessage, + setIsSigningMessage: viewModel.setIsSigningMessage, + signButtonText: "Confirm Seeker Token", + signButtonLabelText: "Claim multiplier", + message: connectWalletProviderViewModel.claimSeekerTokenMessage, + dismiss: { + viewModel.presentSigninWithSolanaSheet = false + } + ) + .environmentObject(themeManager) + .environmentObject(connectWalletProviderViewModel) + } .presentationDetents([.height(148)]) } .onOpenURL { url in - self.handleWalletDeepLink(url) + handleWalletDeepLink(url) } .sheet(isPresented: $viewModel.presentUpdateReferralNetworkSheet) { UpdateReferralNetworkSheet( From e95cae42f3dd9f154a2ee0407f4f9eb8d3cd028a Mon Sep 17 00:00:00 2001 From: ryanmello07 Date: Fri, 17 Jul 2026 17:57:10 +0100 Subject: [PATCH 068/109] fix: extract SettingsForm_iOS into computed property to fix Swift type-check timeout --- .../Main/Account/Settings/SettingsView.swift | 69 ++++++++++--------- 1 file changed, 38 insertions(+), 31 deletions(-) diff --git a/app/network/Main/Account/Settings/SettingsView.swift b/app/network/Main/Account/Settings/SettingsView.swift index e78a7b9e..05cfe44b 100644 --- a/app/network/Main/Account/Settings/SettingsView.swift +++ b/app/network/Main/Account/Settings/SettingsView.swift @@ -50,39 +50,46 @@ struct SettingsView: View { self.networkUserViewModel = networkUserViewModel } + #if os(iOS) + @ViewBuilder + private var settingsForm: some View { + SettingsForm_iOS( + urApiService: api, + clientId: clientId, + referralCode: referralLinkViewModel.referralCode, + totalReferrals: referralLinkViewModel.totalReferrals, + referralNetworkName: viewModel.referralNetwork?.name, + version: viewModel.version, + isUpdatingAccountPreferences: accountPreferencesViewModel.isUpdatingAccountPreferences, + copyToPasteboard: copyToPasteboard, + presentUpdateReferralNetworkSheet: { + viewModel.presentUpdateReferralNetworkSheet = true + }, + presentDeleteAccountConfirmation: { + viewModel.isPresentedDeleteAccountConfirmation = true + }, + navigate: navigate, + provideEnabled: deviceManager.provideEnabled, + providePaused: deviceManager.providePaused, + deviceName: viewModel.deviceName, + deviceSpec: viewModel.deviceSpec, + presentRenameDevice: viewModel.presentRenameDevice, + canReceiveNotifications: $viewModel.canReceiveNotifications, + canReceiveProductUpdates: $accountPreferencesViewModel.canReceiveProductUpdates, + networkUserViewModel: networkUserViewModel, + viewModel: viewModel, + ) + .background(themeManager.currentTheme.backgroundColor) + } + #endif + var body: some View { - + #if os(iOS) - SettingsForm_iOS( - urApiService: api, - clientId: clientId, - referralCode: referralLinkViewModel.referralCode, - totalReferrals: referralLinkViewModel.totalReferrals, - referralNetworkName: viewModel.referralNetwork?.name, - version: viewModel.version, - isUpdatingAccountPreferences: accountPreferencesViewModel.isUpdatingAccountPreferences, - copyToPasteboard: copyToPasteboard, - presentUpdateReferralNetworkSheet: { - viewModel.presentUpdateReferralNetworkSheet = true - }, - presentDeleteAccountConfirmation: { - viewModel.isPresentedDeleteAccountConfirmation = true - }, - navigate: navigate, - provideEnabled: deviceManager.provideEnabled, - providePaused: deviceManager.providePaused, - deviceName: viewModel.deviceName, - deviceSpec: viewModel.deviceSpec, - presentRenameDevice: viewModel.presentRenameDevice, - canReceiveNotifications: $viewModel.canReceiveNotifications, - canReceiveProductUpdates: $accountPreferencesViewModel.canReceiveProductUpdates, - networkUserViewModel: networkUserViewModel, - viewModel: viewModel, - ) - .background(themeManager.currentTheme.backgroundColor) - .task { - await viewModel.fetchDeviceInfo(clientId) - } + settingsForm + .task { + await viewModel.fetchDeviceInfo(clientId) + } .alert("Device name", isPresented: $viewModel.isPresentedRenameDevice) { TextField("Device name", text: $viewModel.editingDeviceName) Button("Save") { From 3caf83e96dac79b0f7cfea06e6c3d4b9daf86060 Mon Sep 17 00:00:00 2001 From: ryanmello07 Date: Fri, 17 Jul 2026 18:14:51 +0100 Subject: [PATCH 069/109] fix: fully extract iOS view chain into iosBody computed property to resolve type-check timeout --- .../Main/Account/Settings/SettingsView.swift | 108 +++++++++--------- 1 file changed, 55 insertions(+), 53 deletions(-) diff --git a/app/network/Main/Account/Settings/SettingsView.swift b/app/network/Main/Account/Settings/SettingsView.swift index 05cfe44b..753f7787 100644 --- a/app/network/Main/Account/Settings/SettingsView.swift +++ b/app/network/Main/Account/Settings/SettingsView.swift @@ -52,44 +52,11 @@ struct SettingsView: View { #if os(iOS) @ViewBuilder - private var settingsForm: some View { - SettingsForm_iOS( - urApiService: api, - clientId: clientId, - referralCode: referralLinkViewModel.referralCode, - totalReferrals: referralLinkViewModel.totalReferrals, - referralNetworkName: viewModel.referralNetwork?.name, - version: viewModel.version, - isUpdatingAccountPreferences: accountPreferencesViewModel.isUpdatingAccountPreferences, - copyToPasteboard: copyToPasteboard, - presentUpdateReferralNetworkSheet: { - viewModel.presentUpdateReferralNetworkSheet = true - }, - presentDeleteAccountConfirmation: { - viewModel.isPresentedDeleteAccountConfirmation = true - }, - navigate: navigate, - provideEnabled: deviceManager.provideEnabled, - providePaused: deviceManager.providePaused, - deviceName: viewModel.deviceName, - deviceSpec: viewModel.deviceSpec, - presentRenameDevice: viewModel.presentRenameDevice, - canReceiveNotifications: $viewModel.canReceiveNotifications, - canReceiveProductUpdates: $accountPreferencesViewModel.canReceiveProductUpdates, - networkUserViewModel: networkUserViewModel, - viewModel: viewModel, - ) - .background(themeManager.currentTheme.backgroundColor) - } - #endif - - var body: some View { - - #if os(iOS) - settingsForm - .task { - await viewModel.fetchDeviceInfo(clientId) - } + private var iosBody: some View { + settingsForm + .task { + await viewModel.fetchDeviceInfo(clientId) + } .alert("Device name", isPresented: $viewModel.isPresentedRenameDevice) { TextField("Device name", text: $viewModel.editingDeviceName) Button("Save") { @@ -120,20 +87,18 @@ struct SettingsView: View { } } .sheet(isPresented: $viewModel.presentSigninWithSolanaSheet) { - Group { - SolanaSignMessageSheet( - isSigningMessage: viewModel.isSigningMessage, - setIsSigningMessage: viewModel.setIsSigningMessage, - signButtonText: "Confirm Seeker Token", - signButtonLabelText: "Claim multiplier", - message: connectWalletProviderViewModel.claimSeekerTokenMessage, - dismiss: { - viewModel.presentSigninWithSolanaSheet = false - } - ) - .environmentObject(themeManager) - .environmentObject(connectWalletProviderViewModel) - } + SolanaSignMessageSheet( + isSigningMessage: viewModel.isSigningMessage, + setIsSigningMessage: viewModel.setIsSigningMessage, + signButtonText: "Confirm Seeker Token", + signButtonLabelText: "Claim multiplier", + message: connectWalletProviderViewModel.claimSeekerTokenMessage, + dismiss: { + viewModel.presentSigninWithSolanaSheet = false + } + ) + .environmentObject(themeManager) + .environmentObject(connectWalletProviderViewModel) .presentationDetents([.height(148)]) } .onOpenURL { url in @@ -202,7 +167,44 @@ struct SettingsView: View { .environmentObject(snackbarManager) .environmentObject(connectWalletProviderViewModel) } - + } + + @ViewBuilder + private var settingsForm: some View { + SettingsForm_iOS( + urApiService: api, + clientId: clientId, + referralCode: referralLinkViewModel.referralCode, + totalReferrals: referralLinkViewModel.totalReferrals, + referralNetworkName: viewModel.referralNetwork?.name, + version: viewModel.version, + isUpdatingAccountPreferences: accountPreferencesViewModel.isUpdatingAccountPreferences, + copyToPasteboard: copyToPasteboard, + presentUpdateReferralNetworkSheet: { + viewModel.presentUpdateReferralNetworkSheet = true + }, + presentDeleteAccountConfirmation: { + viewModel.isPresentedDeleteAccountConfirmation = true + }, + navigate: navigate, + provideEnabled: deviceManager.provideEnabled, + providePaused: deviceManager.providePaused, + deviceName: viewModel.deviceName, + deviceSpec: viewModel.deviceSpec, + presentRenameDevice: viewModel.presentRenameDevice, + canReceiveNotifications: $viewModel.canReceiveNotifications, + canReceiveProductUpdates: $accountPreferencesViewModel.canReceiveProductUpdates, + networkUserViewModel: networkUserViewModel, + viewModel: viewModel, + ) + .background(themeManager.currentTheme.backgroundColor) + } + #endif + + var body: some View { + + #if os(iOS) + iosBody #elseif os(macOS) SettingsForm_macOS( urApiService: api, From 799aaef72e7d93765748b82d609bef039155f414 Mon Sep 17 00:00:00 2001 From: ryanmello07 Date: Fri, 17 Jul 2026 18:32:03 +0100 Subject: [PATCH 070/109] fix: wrap base chain in AnyView to break type-checker's expression depth --- .../Main/Account/Settings/SettingsView.swift | 58 ++++++++++--------- 1 file changed, 30 insertions(+), 28 deletions(-) diff --git a/app/network/Main/Account/Settings/SettingsView.swift b/app/network/Main/Account/Settings/SettingsView.swift index 753f7787..dc296a6a 100644 --- a/app/network/Main/Account/Settings/SettingsView.swift +++ b/app/network/Main/Account/Settings/SettingsView.swift @@ -53,39 +53,41 @@ struct SettingsView: View { #if os(iOS) @ViewBuilder private var iosBody: some View { - settingsForm - .task { - await viewModel.fetchDeviceInfo(clientId) - } - .alert("Device name", isPresented: $viewModel.isPresentedRenameDevice) { - TextField("Device name", text: $viewModel.editingDeviceName) - Button("Save") { - Task { - await saveDeviceName() + AnyView( + settingsForm + .task { + await viewModel.fetchDeviceInfo(clientId) + } + .alert("Device name", isPresented: $viewModel.isPresentedRenameDevice) { + TextField("Device name", text: $viewModel.editingDeviceName) + Button("Save") { + Task { + await saveDeviceName() + } } + Button("Cancel", role: .cancel) {} } - Button("Cancel", role: .cancel) {} - } - .onChange(of: accountPreferencesViewModel.saveErrorMessage) { newValue in - if let newValue { - snackbarManager.showSnackbar(message: newValue) - accountPreferencesViewModel.clearSaveErrorMessage() + .onChange(of: accountPreferencesViewModel.saveErrorMessage) { newValue in + if let newValue { + snackbarManager.showSnackbar(message: newValue) + accountPreferencesViewModel.clearSaveErrorMessage() + } } - } - .confirmationDialog( - "Are you sure you want to delete your account?", - isPresented: $viewModel.isPresentedDeleteAccountConfirmation, - titleVisibility: .visible - ) { - Button("Delete account", role: .destructive) { - - Task { - let result = await viewModel.deleteAccount() - self.handleResult(result) + .confirmationDialog( + "Are you sure you want to delete your account?", + isPresented: $viewModel.isPresentedDeleteAccountConfirmation, + titleVisibility: .visible + ) { + Button("Delete account", role: .destructive) { + + Task { + let result = await viewModel.deleteAccount() + self.handleResult(result) + } + } - } - } + ) .sheet(isPresented: $viewModel.presentSigninWithSolanaSheet) { SolanaSignMessageSheet( isSigningMessage: viewModel.isSigningMessage, From fb6d2b870f1e388204cb84f49a32e9ff37f5140a Mon Sep 17 00:00:00 2001 From: ryanmello07 Date: Fri, 17 Jul 2026 18:44:44 +0100 Subject: [PATCH 071/109] fix: split modifier chain with intermediate let bases for type-checker --- .../Main/Account/Settings/SettingsView.swift | 133 +++++++++--------- 1 file changed, 65 insertions(+), 68 deletions(-) diff --git a/app/network/Main/Account/Settings/SettingsView.swift b/app/network/Main/Account/Settings/SettingsView.swift index dc296a6a..9251c8cf 100644 --- a/app/network/Main/Account/Settings/SettingsView.swift +++ b/app/network/Main/Account/Settings/SettingsView.swift @@ -52,42 +52,73 @@ struct SettingsView: View { #if os(iOS) @ViewBuilder - private var iosBody: some View { - AnyView( - settingsForm - .task { - await viewModel.fetchDeviceInfo(clientId) - } - .alert("Device name", isPresented: $viewModel.isPresentedRenameDevice) { - TextField("Device name", text: $viewModel.editingDeviceName) - Button("Save") { - Task { - await saveDeviceName() - } + private var settingsForm: some View { + SettingsForm_iOS( + urApiService: api, + clientId: clientId, + referralCode: referralLinkViewModel.referralCode, + totalReferrals: referralLinkViewModel.totalReferrals, + referralNetworkName: viewModel.referralNetwork?.name, + version: viewModel.version, + isUpdatingAccountPreferences: accountPreferencesViewModel.isUpdatingAccountPreferences, + copyToPasteboard: copyToPasteboard, + presentUpdateReferralNetworkSheet: { + viewModel.presentUpdateReferralNetworkSheet = true + }, + presentDeleteAccountConfirmation: { + viewModel.isPresentedDeleteAccountConfirmation = true + }, + navigate: navigate, + provideEnabled: deviceManager.provideEnabled, + providePaused: deviceManager.providePaused, + deviceName: viewModel.deviceName, + deviceSpec: viewModel.deviceSpec, + presentRenameDevice: viewModel.presentRenameDevice, + canReceiveNotifications: $viewModel.canReceiveNotifications, + canReceiveProductUpdates: $accountPreferencesViewModel.canReceiveProductUpdates, + networkUserViewModel: networkUserViewModel, + viewModel: viewModel, + ) + .background(themeManager.currentTheme.backgroundColor) + } + #endif + + var body: some View { + + #if os(iOS) + let base = settingsForm + .task { + await viewModel.fetchDeviceInfo(clientId) + } + .alert("Device name", isPresented: $viewModel.isPresentedRenameDevice) { + TextField("Device name", text: $viewModel.editingDeviceName) + Button("Save") { + Task { + await saveDeviceName() } - Button("Cancel", role: .cancel) {} } - .onChange(of: accountPreferencesViewModel.saveErrorMessage) { newValue in - if let newValue { - snackbarManager.showSnackbar(message: newValue) - accountPreferencesViewModel.clearSaveErrorMessage() - } + Button("Cancel", role: .cancel) {} + } + .onChange(of: accountPreferencesViewModel.saveErrorMessage) { newValue in + if let newValue { + snackbarManager.showSnackbar(message: newValue) + accountPreferencesViewModel.clearSaveErrorMessage() } - .confirmationDialog( - "Are you sure you want to delete your account?", - isPresented: $viewModel.isPresentedDeleteAccountConfirmation, - titleVisibility: .visible - ) { - Button("Delete account", role: .destructive) { - - Task { - let result = await viewModel.deleteAccount() - self.handleResult(result) - } - + } + .confirmationDialog( + "Are you sure you want to delete your account?", + isPresented: $viewModel.isPresentedDeleteAccountConfirmation, + titleVisibility: .visible + ) { + Button("Delete account", role: .destructive) { + + Task { + let result = await viewModel.deleteAccount() + self.handleResult(result) } + } - ) + } .sheet(isPresented: $viewModel.presentSigninWithSolanaSheet) { SolanaSignMessageSheet( isSigningMessage: viewModel.isSigningMessage, @@ -124,6 +155,8 @@ struct SettingsView: View { .presentationDetents([.height(268)]) .presentationDragIndicator(.visible) } + + let withAuth = base .sheet(isPresented: $viewModel.presentSeedphraseSheet) { SeedphraseDisplayView( seedphrase: viewModel.generatedSeedphrase, @@ -169,44 +202,8 @@ struct SettingsView: View { .environmentObject(snackbarManager) .environmentObject(connectWalletProviderViewModel) } - } - - @ViewBuilder - private var settingsForm: some View { - SettingsForm_iOS( - urApiService: api, - clientId: clientId, - referralCode: referralLinkViewModel.referralCode, - totalReferrals: referralLinkViewModel.totalReferrals, - referralNetworkName: viewModel.referralNetwork?.name, - version: viewModel.version, - isUpdatingAccountPreferences: accountPreferencesViewModel.isUpdatingAccountPreferences, - copyToPasteboard: copyToPasteboard, - presentUpdateReferralNetworkSheet: { - viewModel.presentUpdateReferralNetworkSheet = true - }, - presentDeleteAccountConfirmation: { - viewModel.isPresentedDeleteAccountConfirmation = true - }, - navigate: navigate, - provideEnabled: deviceManager.provideEnabled, - providePaused: deviceManager.providePaused, - deviceName: viewModel.deviceName, - deviceSpec: viewModel.deviceSpec, - presentRenameDevice: viewModel.presentRenameDevice, - canReceiveNotifications: $viewModel.canReceiveNotifications, - canReceiveProductUpdates: $accountPreferencesViewModel.canReceiveProductUpdates, - networkUserViewModel: networkUserViewModel, - viewModel: viewModel, - ) - .background(themeManager.currentTheme.backgroundColor) - } - #endif - - var body: some View { - #if os(iOS) - iosBody + return withAuth #elseif os(macOS) SettingsForm_macOS( urApiService: api, From de5d13bb7a6c48b74b3bd9d978c2055829744517 Mon Sep 17 00:00:00 2001 From: ryanmello07 Date: Fri, 17 Jul 2026 19:17:57 +0100 Subject: [PATCH 072/109] fix: move auth sheet/dialog modifiers to overlay computed property to fix type-check timeout --- .../Main/Account/Settings/SettingsView.swift | 124 ++++++------------ 1 file changed, 38 insertions(+), 86 deletions(-) diff --git a/app/network/Main/Account/Settings/SettingsView.swift b/app/network/Main/Account/Settings/SettingsView.swift index 9251c8cf..5ca8f7af 100644 --- a/app/network/Main/Account/Settings/SettingsView.swift +++ b/app/network/Main/Account/Settings/SettingsView.swift @@ -50,43 +50,36 @@ struct SettingsView: View { self.networkUserViewModel = networkUserViewModel } - #if os(iOS) - @ViewBuilder - private var settingsForm: some View { - SettingsForm_iOS( - urApiService: api, - clientId: clientId, - referralCode: referralLinkViewModel.referralCode, - totalReferrals: referralLinkViewModel.totalReferrals, - referralNetworkName: viewModel.referralNetwork?.name, - version: viewModel.version, - isUpdatingAccountPreferences: accountPreferencesViewModel.isUpdatingAccountPreferences, - copyToPasteboard: copyToPasteboard, - presentUpdateReferralNetworkSheet: { - viewModel.presentUpdateReferralNetworkSheet = true - }, - presentDeleteAccountConfirmation: { - viewModel.isPresentedDeleteAccountConfirmation = true - }, - navigate: navigate, - provideEnabled: deviceManager.provideEnabled, - providePaused: deviceManager.providePaused, - deviceName: viewModel.deviceName, - deviceSpec: viewModel.deviceSpec, - presentRenameDevice: viewModel.presentRenameDevice, - canReceiveNotifications: $viewModel.canReceiveNotifications, - canReceiveProductUpdates: $accountPreferencesViewModel.canReceiveProductUpdates, - networkUserViewModel: networkUserViewModel, - viewModel: viewModel, - ) - .background(themeManager.currentTheme.backgroundColor) - } - #endif - var body: some View { #if os(iOS) - let base = settingsForm + SettingsForm_iOS( + urApiService: api, + clientId: clientId, + referralCode: referralLinkViewModel.referralCode, + totalReferrals: referralLinkViewModel.totalReferrals, + referralNetworkName: viewModel.referralNetwork?.name, + version: viewModel.version, + isUpdatingAccountPreferences: accountPreferencesViewModel.isUpdatingAccountPreferences, + copyToPasteboard: copyToPasteboard, + presentUpdateReferralNetworkSheet: { + viewModel.presentUpdateReferralNetworkSheet = true + }, + presentDeleteAccountConfirmation: { + viewModel.isPresentedDeleteAccountConfirmation = true + }, + navigate: navigate, + provideEnabled: deviceManager.provideEnabled, + providePaused: deviceManager.providePaused, + deviceName: viewModel.deviceName, + deviceSpec: viewModel.deviceSpec, + presentRenameDevice: viewModel.presentRenameDevice, + canReceiveNotifications: $viewModel.canReceiveNotifications, + canReceiveProductUpdates: $accountPreferencesViewModel.canReceiveProductUpdates, + networkUserViewModel: networkUserViewModel, + viewModel: viewModel, + ) + .background(themeManager.currentTheme.backgroundColor) .task { await viewModel.fetchDeviceInfo(clientId) } @@ -120,6 +113,7 @@ struct SettingsView: View { } } .sheet(isPresented: $viewModel.presentSigninWithSolanaSheet) { + SolanaSignMessageSheet( isSigningMessage: viewModel.isSigningMessage, setIsSigningMessage: viewModel.setIsSigningMessage, @@ -155,55 +149,7 @@ struct SettingsView: View { .presentationDetents([.height(268)]) .presentationDragIndicator(.visible) } - - let withAuth = base - .sheet(isPresented: $viewModel.presentSeedphraseSheet) { - SeedphraseDisplayView( - seedphrase: viewModel.generatedSeedphrase, - onConfirmed: { _ in - viewModel.dismissSeedphraseSheet() - } - ) - .environmentObject(themeManager) - } - .confirmationDialog( - "Generate a recovery seedphrase?", - isPresented: $viewModel.presentSeedphraseConfirmation, - titleVisibility: .visible - ) { - Button("Generate") { - Task { - await viewModel.executePendingSeedphraseAction() - } - } - Button("Cancel", role: .cancel) {} - } message: { - Text("A seedphrase lets you recover your account if you lose access. Store it safely.") - } - .confirmationDialog( - "Remove this sign-in method?", - isPresented: $viewModel.presentRemoveAuthConfirmation, - titleVisibility: .visible - ) { - Button("Remove", role: .destructive) { - Task { - await viewModel.executeRemoveAuth() - } - } - Button("Cancel", role: .cancel) {} - } message: { - if let authType = viewModel.authTypeToRemove { - Text("Are you sure you want to remove \(authType) as a sign-in method?") - } - } - .sheet(isPresented: $viewModel.presentAddAuthSheet) { - AddAuthSheet(api: api) - .environmentObject(themeManager) - .environmentObject(snackbarManager) - .environmentObject(connectWalletProviderViewModel) - } - - return withAuth + .overlay(authModifiers) #elseif os(macOS) SettingsForm_macOS( urApiService: api, @@ -280,6 +226,15 @@ struct SettingsView: View { ) .environmentObject(themeManager) } + .overlay(authModifiers) + + #endif + + } + + @ViewBuilder + private var authModifiers: some View { + Color.clear .sheet(isPresented: $viewModel.presentSeedphraseSheet) { SeedphraseDisplayView( seedphrase: viewModel.generatedSeedphrase, @@ -325,9 +280,6 @@ struct SettingsView: View { .environmentObject(snackbarManager) .environmentObject(connectWalletProviderViewModel) } - - #endif - } private func handleWalletDeepLink(_ url: URL) { From 9757a0a6a432ed05a999a5a5838aca8fda5128e5 Mon Sep 17 00:00:00 2001 From: ryanmello07 Date: Fri, 17 Jul 2026 19:30:36 +0100 Subject: [PATCH 073/109] fix: move onOpenURL to authModifiers, keep main chain at original length in ZStack --- app/network/Main/Account/Settings/SettingsView.swift | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/app/network/Main/Account/Settings/SettingsView.swift b/app/network/Main/Account/Settings/SettingsView.swift index 5ca8f7af..c23e190b 100644 --- a/app/network/Main/Account/Settings/SettingsView.swift +++ b/app/network/Main/Account/Settings/SettingsView.swift @@ -53,6 +53,8 @@ struct SettingsView: View { var body: some View { #if os(iOS) + ZStack { + authModifiers SettingsForm_iOS( urApiService: api, clientId: clientId, @@ -128,9 +130,6 @@ struct SettingsView: View { .environmentObject(connectWalletProviderViewModel) .presentationDetents([.height(148)]) } - .onOpenURL { url in - handleWalletDeepLink(url) - } .sheet(isPresented: $viewModel.presentUpdateReferralNetworkSheet) { UpdateReferralNetworkSheet( api: api, @@ -149,7 +148,7 @@ struct SettingsView: View { .presentationDetents([.height(268)]) .presentationDragIndicator(.visible) } - .overlay(authModifiers) + } #elseif os(macOS) SettingsForm_macOS( urApiService: api, @@ -226,7 +225,6 @@ struct SettingsView: View { ) .environmentObject(themeManager) } - .overlay(authModifiers) #endif @@ -280,6 +278,9 @@ struct SettingsView: View { .environmentObject(snackbarManager) .environmentObject(connectWalletProviderViewModel) } + .onOpenURL { url in + handleWalletDeepLink(url) + } } private func handleWalletDeepLink(_ url: URL) { From a1e75e9a5718c2cf29abeef3aa618dfa2473b6d7 Mon Sep 17 00:00:00 2001 From: ryanmello07 Date: Fri, 17 Jul 2026 19:42:44 +0100 Subject: [PATCH 074/109] fix: wrap inner modifier chain in Group {}, attach auth modifiers outside group as clean type-check boundary --- .../Main/Account/Settings/SettingsView.swift | 65 +++++++++++++++---- 1 file changed, 52 insertions(+), 13 deletions(-) diff --git a/app/network/Main/Account/Settings/SettingsView.swift b/app/network/Main/Account/Settings/SettingsView.swift index c23e190b..0ab59e25 100644 --- a/app/network/Main/Account/Settings/SettingsView.swift +++ b/app/network/Main/Account/Settings/SettingsView.swift @@ -53,8 +53,7 @@ struct SettingsView: View { var body: some View { #if os(iOS) - ZStack { - authModifiers + Group { SettingsForm_iOS( urApiService: api, clientId: clientId, @@ -149,6 +148,54 @@ struct SettingsView: View { .presentationDragIndicator(.visible) } } + .sheet(isPresented: $viewModel.presentSeedphraseSheet) { + SeedphraseDisplayView( + seedphrase: viewModel.generatedSeedphrase, + onConfirmed: { _ in + viewModel.dismissSeedphraseSheet() + } + ) + .environmentObject(themeManager) + } + .confirmationDialog( + "Generate a recovery seedphrase?", + isPresented: $viewModel.presentSeedphraseConfirmation, + titleVisibility: .visible + ) { + Button("Generate") { + Task { + await viewModel.executePendingSeedphraseAction() + } + } + Button("Cancel", role: .cancel) {} + } message: { + Text("A seedphrase lets you recover your account if you lose access. Store it safely.") + } + .confirmationDialog( + "Remove this sign-in method?", + isPresented: $viewModel.presentRemoveAuthConfirmation, + titleVisibility: .visible + ) { + Button("Remove", role: .destructive) { + Task { + await viewModel.executeRemoveAuth() + } + } + Button("Cancel", role: .cancel) {} + } message: { + if let authType = viewModel.authTypeToRemove { + Text("Are you sure you want to remove \(authType) as a sign-in method?") + } + } + .sheet(isPresented: $viewModel.presentAddAuthSheet) { + AddAuthSheet(api: api) + .environmentObject(themeManager) + .environmentObject(snackbarManager) + .environmentObject(connectWalletProviderViewModel) + } + .onOpenURL { url in + handleWalletDeepLink(url) + } #elseif os(macOS) SettingsForm_macOS( urApiService: api, @@ -225,14 +272,6 @@ struct SettingsView: View { ) .environmentObject(themeManager) } - - #endif - - } - - @ViewBuilder - private var authModifiers: some View { - Color.clear .sheet(isPresented: $viewModel.presentSeedphraseSheet) { SeedphraseDisplayView( seedphrase: viewModel.generatedSeedphrase, @@ -278,9 +317,9 @@ struct SettingsView: View { .environmentObject(snackbarManager) .environmentObject(connectWalletProviderViewModel) } - .onOpenURL { url in - handleWalletDeepLink(url) - } + + #endif + } private func handleWalletDeepLink(_ url: URL) { From a11346362d72ef9710dfe62082a13bfc7a17f9d9 Mon Sep 17 00:00:00 2001 From: ryanmello07 Date: Fri, 17 Jul 2026 19:55:27 +0100 Subject: [PATCH 075/109] fix: extract every sheet content into computed properties, use content: parameter syntax to reduce type-checker load --- .../Main/Account/Settings/SettingsView.swift | 109 ++++++++++-------- 1 file changed, 58 insertions(+), 51 deletions(-) diff --git a/app/network/Main/Account/Settings/SettingsView.swift b/app/network/Main/Account/Settings/SettingsView.swift index 0ab59e25..08dd15f4 100644 --- a/app/network/Main/Account/Settings/SettingsView.swift +++ b/app/network/Main/Account/Settings/SettingsView.swift @@ -105,58 +105,16 @@ struct SettingsView: View { titleVisibility: .visible ) { Button("Delete account", role: .destructive) { - Task { let result = await viewModel.deleteAccount() self.handleResult(result) } - } } - .sheet(isPresented: $viewModel.presentSigninWithSolanaSheet) { - - SolanaSignMessageSheet( - isSigningMessage: viewModel.isSigningMessage, - setIsSigningMessage: viewModel.setIsSigningMessage, - signButtonText: "Confirm Seeker Token", - signButtonLabelText: "Claim multiplier", - message: connectWalletProviderViewModel.claimSeekerTokenMessage, - dismiss: { - viewModel.presentSigninWithSolanaSheet = false - } - ) - .environmentObject(themeManager) - .environmentObject(connectWalletProviderViewModel) - .presentationDetents([.height(148)]) - } - .sheet(isPresented: $viewModel.presentUpdateReferralNetworkSheet) { - UpdateReferralNetworkSheet( - api: api, - onSuccess: { - Task { - await viewModel.fetchReferralNetwork() - } - viewModel.presentUpdateReferralNetworkSheet = false - }, - dismiss: { - viewModel.presentUpdateReferralNetworkSheet = false - }, - referralNetwork: viewModel.referralNetwork - ) - .environmentObject(themeManager) - .presentationDetents([.height(268)]) - .presentationDragIndicator(.visible) - } - } - .sheet(isPresented: $viewModel.presentSeedphraseSheet) { - SeedphraseDisplayView( - seedphrase: viewModel.generatedSeedphrase, - onConfirmed: { _ in - viewModel.dismissSeedphraseSheet() - } - ) - .environmentObject(themeManager) + .sheet(isPresented: $viewModel.presentSigninWithSolanaSheet, content: signInSolanaSheet) + .sheet(isPresented: $viewModel.presentUpdateReferralNetworkSheet, content: updateReferralSheet) } + .sheet(isPresented: $viewModel.presentSeedphraseSheet, content: seedphraseDisplaySheet) .confirmationDialog( "Generate a recovery seedphrase?", isPresented: $viewModel.presentSeedphraseConfirmation, @@ -187,12 +145,7 @@ struct SettingsView: View { Text("Are you sure you want to remove \(authType) as a sign-in method?") } } - .sheet(isPresented: $viewModel.presentAddAuthSheet) { - AddAuthSheet(api: api) - .environmentObject(themeManager) - .environmentObject(snackbarManager) - .environmentObject(connectWalletProviderViewModel) - } + .sheet(isPresented: $viewModel.presentAddAuthSheet, content: addAuthSheet) .onOpenURL { url in handleWalletDeepLink(url) } @@ -322,6 +275,60 @@ struct SettingsView: View { } + #if os(iOS) + @ViewBuilder private var signInSolanaSheet: some View { + SolanaSignMessageSheet( + isSigningMessage: viewModel.isSigningMessage, + setIsSigningMessage: viewModel.setIsSigningMessage, + signButtonText: "Confirm Seeker Token", + signButtonLabelText: "Claim multiplier", + message: connectWalletProviderViewModel.claimSeekerTokenMessage, + dismiss: { + viewModel.presentSigninWithSolanaSheet = false + } + ) + .environmentObject(themeManager) + .environmentObject(connectWalletProviderViewModel) + .presentationDetents([.height(148)]) + } + + @ViewBuilder private var updateReferralSheet: some View { + UpdateReferralNetworkSheet( + api: api, + onSuccess: { + Task { + await viewModel.fetchReferralNetwork() + } + viewModel.presentUpdateReferralNetworkSheet = false + }, + dismiss: { + viewModel.presentUpdateReferralNetworkSheet = false + }, + referralNetwork: viewModel.referralNetwork + ) + .environmentObject(themeManager) + .presentationDetents([.height(268)]) + .presentationDragIndicator(.visible) + } + + @ViewBuilder private var seedphraseDisplaySheet: some View { + SeedphraseDisplayView( + seedphrase: viewModel.generatedSeedphrase, + onConfirmed: { _ in + viewModel.dismissSeedphraseSheet() + } + ) + .environmentObject(themeManager) + } + + @ViewBuilder private var addAuthSheet: some View { + AddAuthSheet(api: api) + .environmentObject(themeManager) + .environmentObject(snackbarManager) + .environmentObject(connectWalletProviderViewModel) + } + #endif + private func handleWalletDeepLink(_ url: URL) { let vm = connectWalletProviderViewModel if vm.pendingAddAuthSignatureHandler != nil, From 340c58329250b842d346096474bf947f99780950 Mon Sep 17 00:00:00 2001 From: ryanmello07 Date: Fri, 17 Jul 2026 20:13:54 +0100 Subject: [PATCH 076/109] fix: use ZStack with separately type-checked computed properties for body and authModifiers --- .../Main/Account/Settings/SettingsView.swift | 407 +++++++++--------- 1 file changed, 211 insertions(+), 196 deletions(-) diff --git a/app/network/Main/Account/Settings/SettingsView.swift b/app/network/Main/Account/Settings/SettingsView.swift index 08dd15f4..f986e56d 100644 --- a/app/network/Main/Account/Settings/SettingsView.swift +++ b/app/network/Main/Account/Settings/SettingsView.swift @@ -53,178 +53,249 @@ struct SettingsView: View { var body: some View { #if os(iOS) - Group { - SettingsForm_iOS( - urApiService: api, - clientId: clientId, - referralCode: referralLinkViewModel.referralCode, - totalReferrals: referralLinkViewModel.totalReferrals, - referralNetworkName: viewModel.referralNetwork?.name, - version: viewModel.version, - isUpdatingAccountPreferences: accountPreferencesViewModel.isUpdatingAccountPreferences, - copyToPasteboard: copyToPasteboard, - presentUpdateReferralNetworkSheet: { - viewModel.presentUpdateReferralNetworkSheet = true + ZStack { + authModifiers + settingsForm + } + #elseif os(macOS) + macBody + #endif + + } + + #if os(iOS) + @ViewBuilder + private var settingsForm: some View { + SettingsForm_iOS( + urApiService: api, + clientId: clientId, + referralCode: referralLinkViewModel.referralCode, + totalReferrals: referralLinkViewModel.totalReferrals, + referralNetworkName: viewModel.referralNetwork?.name, + version: viewModel.version, + isUpdatingAccountPreferences: accountPreferencesViewModel.isUpdatingAccountPreferences, + copyToPasteboard: copyToPasteboard, + presentUpdateReferralNetworkSheet: { + viewModel.presentUpdateReferralNetworkSheet = true + }, + presentDeleteAccountConfirmation: { + viewModel.isPresentedDeleteAccountConfirmation = true + }, + navigate: navigate, + provideEnabled: deviceManager.provideEnabled, + providePaused: deviceManager.providePaused, + deviceName: viewModel.deviceName, + deviceSpec: viewModel.deviceSpec, + presentRenameDevice: viewModel.presentRenameDevice, + canReceiveNotifications: $viewModel.canReceiveNotifications, + canReceiveProductUpdates: $accountPreferencesViewModel.canReceiveProductUpdates, + networkUserViewModel: networkUserViewModel, + viewModel: viewModel, + ) + .background(themeManager.currentTheme.backgroundColor) + .task { + await viewModel.fetchDeviceInfo(clientId) + } + .alert("Device name", isPresented: $viewModel.isPresentedRenameDevice) { + TextField("Device name", text: $viewModel.editingDeviceName) + Button("Save") { + Task { + await saveDeviceName() + } + } + Button("Cancel", role: .cancel) {} + } + .onChange(of: accountPreferencesViewModel.saveErrorMessage) { newValue in + if let newValue { + snackbarManager.showSnackbar(message: newValue) + accountPreferencesViewModel.clearSaveErrorMessage() + } + } + .confirmationDialog( + "Are you sure you want to delete your account?", + isPresented: $viewModel.isPresentedDeleteAccountConfirmation, + titleVisibility: .visible + ) { + Button("Delete account", role: .destructive) { + + Task { + let result = await viewModel.deleteAccount() + self.handleResult(result) + } + + } + } + .sheet(isPresented: $viewModel.presentSigninWithSolanaSheet) { + + SolanaSignMessageSheet( + isSigningMessage: viewModel.isSigningMessage, + setIsSigningMessage: viewModel.setIsSigningMessage, + signButtonText: "Confirm Seeker Token", + signButtonLabelText: "Claim multiplier", + message: connectWalletProviderViewModel.claimSeekerTokenMessage, + dismiss: { + viewModel.presentSigninWithSolanaSheet = false + } + ) + .environmentObject(themeManager) + .environmentObject(connectWalletProviderViewModel) + .presentationDetents([.height(148)]) + } + .sheet(isPresented: $viewModel.presentUpdateReferralNetworkSheet) { + UpdateReferralNetworkSheet( + api: api, + onSuccess: { + Task { + await viewModel.fetchReferralNetwork() + } + viewModel.presentUpdateReferralNetworkSheet = false }, - presentDeleteAccountConfirmation: { - viewModel.isPresentedDeleteAccountConfirmation = true + dismiss: { + viewModel.presentUpdateReferralNetworkSheet = false }, - navigate: navigate, - provideEnabled: deviceManager.provideEnabled, - providePaused: deviceManager.providePaused, - deviceName: viewModel.deviceName, - deviceSpec: viewModel.deviceSpec, - presentRenameDevice: viewModel.presentRenameDevice, - canReceiveNotifications: $viewModel.canReceiveNotifications, - canReceiveProductUpdates: $accountPreferencesViewModel.canReceiveProductUpdates, - networkUserViewModel: networkUserViewModel, - viewModel: viewModel, + referralNetwork: viewModel.referralNetwork ) - .background(themeManager.currentTheme.backgroundColor) - .task { - await viewModel.fetchDeviceInfo(clientId) + .environmentObject(themeManager) + .presentationDetents([.height(268)]) + .presentationDragIndicator(.visible) + } + } + + @ViewBuilder + private var authModifiers: some View { + Color.clear + .frame(width: 0, height: 0) + .sheet(isPresented: $viewModel.presentSeedphraseSheet) { + SeedphraseDisplayView( + seedphrase: viewModel.generatedSeedphrase, + onConfirmed: { _ in + viewModel.dismissSeedphraseSheet() + } + ) + .environmentObject(themeManager) } - .alert("Device name", isPresented: $viewModel.isPresentedRenameDevice) { - TextField("Device name", text: $viewModel.editingDeviceName) - Button("Save") { + .confirmationDialog( + "Generate a recovery seedphrase?", + isPresented: $viewModel.presentSeedphraseConfirmation, + titleVisibility: .visible + ) { + Button("Generate") { Task { - await saveDeviceName() + await viewModel.executePendingSeedphraseAction() } } Button("Cancel", role: .cancel) {} - } - .onChange(of: accountPreferencesViewModel.saveErrorMessage) { newValue in - if let newValue { - snackbarManager.showSnackbar(message: newValue) - accountPreferencesViewModel.clearSaveErrorMessage() - } + } message: { + Text("A seedphrase lets you recover your account if you lose access. Store it safely.") } .confirmationDialog( - "Are you sure you want to delete your account?", - isPresented: $viewModel.isPresentedDeleteAccountConfirmation, + "Remove this sign-in method?", + isPresented: $viewModel.presentRemoveAuthConfirmation, titleVisibility: .visible ) { - Button("Delete account", role: .destructive) { + Button("Remove", role: .destructive) { Task { - let result = await viewModel.deleteAccount() - self.handleResult(result) + await viewModel.executeRemoveAuth() } } + Button("Cancel", role: .cancel) {} + } message: { + if let authType = viewModel.authTypeToRemove { + Text("Are you sure you want to remove \(authType) as a sign-in method?") + } } - .sheet(isPresented: $viewModel.presentSigninWithSolanaSheet, content: signInSolanaSheet) - .sheet(isPresented: $viewModel.presentUpdateReferralNetworkSheet, content: updateReferralSheet) + .sheet(isPresented: $viewModel.presentAddAuthSheet) { + AddAuthSheet(api: api) + .environmentObject(themeManager) + .environmentObject(snackbarManager) + .environmentObject(connectWalletProviderViewModel) + } + .onOpenURL { url in + handleWalletDeepLink(url) + } + } + #endif + + #if os(macOS) + @ViewBuilder + private var macBody: some View { + SettingsForm_macOS( + urApiService: api, + clientId: clientId, + referralCode: referralLinkViewModel.referralCode, + totalReferrals: referralLinkViewModel.totalReferrals, + referralNetworkName: viewModel.referralNetwork?.name, + version: viewModel.version, + isUpdatingAccountPreferences: accountPreferencesViewModel.isUpdatingAccountPreferences, + copyToPasteboard: copyToPasteboard, + presentUpdateReferralNetworkSheet: { + viewModel.presentUpdateReferralNetworkSheet = true + }, + presentDeleteAccountConfirmation: { + viewModel.isPresentedDeleteAccountConfirmation = true + }, + navigate: navigate, + provideEnabled: deviceManager.provideEnabled, + providePaused: deviceManager.providePaused, + deviceName: viewModel.deviceName, + deviceSpec: viewModel.deviceSpec, + presentRenameDevice: viewModel.presentRenameDevice, + canReceiveNotifications: $viewModel.canReceiveNotifications, + canReceiveProductUpdates: $accountPreferencesViewModel.canReceiveProductUpdates, + launchAtStartupEnabled: $viewModel.launchAtStartupEnabled, + networkUserViewModel: networkUserViewModel, + viewModel: viewModel + ) + .task { + await viewModel.fetchDeviceInfo(clientId) } - .sheet(isPresented: $viewModel.presentSeedphraseSheet, content: seedphraseDisplaySheet) - .confirmationDialog( - "Generate a recovery seedphrase?", - isPresented: $viewModel.presentSeedphraseConfirmation, - titleVisibility: .visible - ) { - Button("Generate") { + .alert("Device name", isPresented: $viewModel.isPresentedRenameDevice) { + TextField("Device name", text: $viewModel.editingDeviceName) + Button("Save") { Task { - await viewModel.executePendingSeedphraseAction() + await saveDeviceName() } } Button("Cancel", role: .cancel) {} - } message: { - Text("A seedphrase lets you recover your account if you lose access. Store it safely.") + } + .onChange(of: accountPreferencesViewModel.saveErrorMessage) { newValue in + if let newValue { + snackbarManager.showSnackbar(message: newValue) + accountPreferencesViewModel.clearSaveErrorMessage() + } } .confirmationDialog( - "Remove this sign-in method?", - isPresented: $viewModel.presentRemoveAuthConfirmation, + "Are you sure you want to delete your account?", + isPresented: $viewModel.isPresentedDeleteAccountConfirmation, titleVisibility: .visible ) { - Button("Remove", role: .destructive) { + Button("Delete account", role: .destructive) { + Task { - await viewModel.executeRemoveAuth() + let result = await viewModel.deleteAccount() + self.handleResult(result) } - } - Button("Cancel", role: .cancel) {} - } message: { - if let authType = viewModel.authTypeToRemove { - Text("Are you sure you want to remove \(authType) as a sign-in method?") + } } - .sheet(isPresented: $viewModel.presentAddAuthSheet, content: addAuthSheet) - .onOpenURL { url in - handleWalletDeepLink(url) - } - #elseif os(macOS) - SettingsForm_macOS( - urApiService: api, - clientId: clientId, - referralCode: referralLinkViewModel.referralCode, - totalReferrals: referralLinkViewModel.totalReferrals, - referralNetworkName: viewModel.referralNetwork?.name, - version: viewModel.version, - isUpdatingAccountPreferences: accountPreferencesViewModel.isUpdatingAccountPreferences, - copyToPasteboard: copyToPasteboard, - presentUpdateReferralNetworkSheet: { - viewModel.presentUpdateReferralNetworkSheet = true + .sheet(isPresented: $viewModel.presentUpdateReferralNetworkSheet) { + UpdateReferralNetworkSheet( + api: api, + onSuccess: { + Task { + await viewModel.fetchReferralNetwork() + } + viewModel.presentUpdateReferralNetworkSheet = false }, - presentDeleteAccountConfirmation: { - viewModel.isPresentedDeleteAccountConfirmation = true + dismiss: { + viewModel.presentUpdateReferralNetworkSheet = false }, - navigate: navigate, - provideEnabled: deviceManager.provideEnabled, - providePaused: deviceManager.providePaused, - deviceName: viewModel.deviceName, - deviceSpec: viewModel.deviceSpec, - presentRenameDevice: viewModel.presentRenameDevice, - canReceiveNotifications: $viewModel.canReceiveNotifications, - canReceiveProductUpdates: $accountPreferencesViewModel.canReceiveProductUpdates, - launchAtStartupEnabled: $viewModel.launchAtStartupEnabled, - networkUserViewModel: networkUserViewModel, - viewModel: viewModel + referralNetwork: viewModel.referralNetwork ) - .task { - await viewModel.fetchDeviceInfo(clientId) - } - .alert("Device name", isPresented: $viewModel.isPresentedRenameDevice) { - TextField("Device name", text: $viewModel.editingDeviceName) - Button("Save") { - Task { - await saveDeviceName() - } - } - Button("Cancel", role: .cancel) {} - } - .onChange(of: accountPreferencesViewModel.saveErrorMessage) { newValue in - if let newValue { - snackbarManager.showSnackbar(message: newValue) - accountPreferencesViewModel.clearSaveErrorMessage() - } - } - .confirmationDialog( - "Are you sure you want to delete your account?", - isPresented: $viewModel.isPresentedDeleteAccountConfirmation, - titleVisibility: .visible - ) { - Button("Delete account", role: .destructive) { - - Task { - let result = await viewModel.deleteAccount() - self.handleResult(result) - } - - } - } - .sheet(isPresented: $viewModel.presentUpdateReferralNetworkSheet) { - UpdateReferralNetworkSheet( - api: api, - onSuccess: { - Task { - await viewModel.fetchReferralNetwork() - } - viewModel.presentUpdateReferralNetworkSheet = false - }, - dismiss: { - viewModel.presentUpdateReferralNetworkSheet = false - }, - referralNetwork: viewModel.referralNetwork - ) - .environmentObject(themeManager) - } + .environmentObject(themeManager) + } + Color.clear + .frame(width: 0, height: 0) .sheet(isPresented: $viewModel.presentSeedphraseSheet) { SeedphraseDisplayView( seedphrase: viewModel.generatedSeedphrase, @@ -270,62 +341,6 @@ struct SettingsView: View { .environmentObject(snackbarManager) .environmentObject(connectWalletProviderViewModel) } - - #endif - - } - - #if os(iOS) - @ViewBuilder private var signInSolanaSheet: some View { - SolanaSignMessageSheet( - isSigningMessage: viewModel.isSigningMessage, - setIsSigningMessage: viewModel.setIsSigningMessage, - signButtonText: "Confirm Seeker Token", - signButtonLabelText: "Claim multiplier", - message: connectWalletProviderViewModel.claimSeekerTokenMessage, - dismiss: { - viewModel.presentSigninWithSolanaSheet = false - } - ) - .environmentObject(themeManager) - .environmentObject(connectWalletProviderViewModel) - .presentationDetents([.height(148)]) - } - - @ViewBuilder private var updateReferralSheet: some View { - UpdateReferralNetworkSheet( - api: api, - onSuccess: { - Task { - await viewModel.fetchReferralNetwork() - } - viewModel.presentUpdateReferralNetworkSheet = false - }, - dismiss: { - viewModel.presentUpdateReferralNetworkSheet = false - }, - referralNetwork: viewModel.referralNetwork - ) - .environmentObject(themeManager) - .presentationDetents([.height(268)]) - .presentationDragIndicator(.visible) - } - - @ViewBuilder private var seedphraseDisplaySheet: some View { - SeedphraseDisplayView( - seedphrase: viewModel.generatedSeedphrase, - onConfirmed: { _ in - viewModel.dismissSeedphraseSheet() - } - ) - .environmentObject(themeManager) - } - - @ViewBuilder private var addAuthSheet: some View { - AddAuthSheet(api: api) - .environmentObject(themeManager) - .environmentObject(snackbarManager) - .environmentObject(connectWalletProviderViewModel) } #endif From 5ee6814d997ecb0492685da2b2e941dc84c658bd Mon Sep 17 00:00:00 2001 From: ryanmello07 Date: Fri, 17 Jul 2026 20:26:24 +0100 Subject: [PATCH 077/109] fix: keep body chain at 8 modifiers (+1 for .overlay), move all auth + onOpenURL to authModifiers computed property --- .../Main/Account/Settings/SettingsView.swift | 373 ++++++++---------- 1 file changed, 156 insertions(+), 217 deletions(-) diff --git a/app/network/Main/Account/Settings/SettingsView.swift b/app/network/Main/Account/Settings/SettingsView.swift index f986e56d..5c4f4d72 100644 --- a/app/network/Main/Account/Settings/SettingsView.swift +++ b/app/network/Main/Account/Settings/SettingsView.swift @@ -53,249 +53,186 @@ struct SettingsView: View { var body: some View { #if os(iOS) - ZStack { - authModifiers - settingsForm - } - #elseif os(macOS) - macBody - #endif - - } - - #if os(iOS) - @ViewBuilder - private var settingsForm: some View { - SettingsForm_iOS( - urApiService: api, - clientId: clientId, - referralCode: referralLinkViewModel.referralCode, - totalReferrals: referralLinkViewModel.totalReferrals, - referralNetworkName: viewModel.referralNetwork?.name, - version: viewModel.version, - isUpdatingAccountPreferences: accountPreferencesViewModel.isUpdatingAccountPreferences, - copyToPasteboard: copyToPasteboard, - presentUpdateReferralNetworkSheet: { - viewModel.presentUpdateReferralNetworkSheet = true - }, - presentDeleteAccountConfirmation: { - viewModel.isPresentedDeleteAccountConfirmation = true - }, - navigate: navigate, - provideEnabled: deviceManager.provideEnabled, - providePaused: deviceManager.providePaused, - deviceName: viewModel.deviceName, - deviceSpec: viewModel.deviceSpec, - presentRenameDevice: viewModel.presentRenameDevice, - canReceiveNotifications: $viewModel.canReceiveNotifications, - canReceiveProductUpdates: $accountPreferencesViewModel.canReceiveProductUpdates, - networkUserViewModel: networkUserViewModel, - viewModel: viewModel, - ) - .background(themeManager.currentTheme.backgroundColor) - .task { - await viewModel.fetchDeviceInfo(clientId) - } - .alert("Device name", isPresented: $viewModel.isPresentedRenameDevice) { - TextField("Device name", text: $viewModel.editingDeviceName) - Button("Save") { - Task { - await saveDeviceName() - } - } - Button("Cancel", role: .cancel) {} - } - .onChange(of: accountPreferencesViewModel.saveErrorMessage) { newValue in - if let newValue { - snackbarManager.showSnackbar(message: newValue) - accountPreferencesViewModel.clearSaveErrorMessage() + SettingsForm_iOS( + urApiService: api, + clientId: clientId, + referralCode: referralLinkViewModel.referralCode, + totalReferrals: referralLinkViewModel.totalReferrals, + referralNetworkName: viewModel.referralNetwork?.name, + version: viewModel.version, + isUpdatingAccountPreferences: accountPreferencesViewModel.isUpdatingAccountPreferences, + copyToPasteboard: copyToPasteboard, + presentUpdateReferralNetworkSheet: { + viewModel.presentUpdateReferralNetworkSheet = true + }, + presentDeleteAccountConfirmation: { + viewModel.isPresentedDeleteAccountConfirmation = true + }, + navigate: navigate, + provideEnabled: deviceManager.provideEnabled, + providePaused: deviceManager.providePaused, + deviceName: viewModel.deviceName, + deviceSpec: viewModel.deviceSpec, + presentRenameDevice: viewModel.presentRenameDevice, + canReceiveNotifications: $viewModel.canReceiveNotifications, + canReceiveProductUpdates: $accountPreferencesViewModel.canReceiveProductUpdates, + networkUserViewModel: networkUserViewModel, + viewModel: viewModel, + ) + .background(themeManager.currentTheme.backgroundColor) + .task { + await viewModel.fetchDeviceInfo(clientId) } - } - .confirmationDialog( - "Are you sure you want to delete your account?", - isPresented: $viewModel.isPresentedDeleteAccountConfirmation, - titleVisibility: .visible - ) { - Button("Delete account", role: .destructive) { - - Task { - let result = await viewModel.deleteAccount() - self.handleResult(result) + .alert("Device name", isPresented: $viewModel.isPresentedRenameDevice) { + TextField("Device name", text: $viewModel.editingDeviceName) + Button("Save") { + Task { + await saveDeviceName() + } } - + Button("Cancel", role: .cancel) {} } - } - .sheet(isPresented: $viewModel.presentSigninWithSolanaSheet) { - - SolanaSignMessageSheet( - isSigningMessage: viewModel.isSigningMessage, - setIsSigningMessage: viewModel.setIsSigningMessage, - signButtonText: "Confirm Seeker Token", - signButtonLabelText: "Claim multiplier", - message: connectWalletProviderViewModel.claimSeekerTokenMessage, - dismiss: { - viewModel.presentSigninWithSolanaSheet = false + .onChange(of: accountPreferencesViewModel.saveErrorMessage) { newValue in + if let newValue { + snackbarManager.showSnackbar(message: newValue) + accountPreferencesViewModel.clearSaveErrorMessage() } - ) - .environmentObject(themeManager) - .environmentObject(connectWalletProviderViewModel) - .presentationDetents([.height(148)]) - } - .sheet(isPresented: $viewModel.presentUpdateReferralNetworkSheet) { - UpdateReferralNetworkSheet( - api: api, - onSuccess: { + } + .confirmationDialog( + "Are you sure you want to delete your account?", + isPresented: $viewModel.isPresentedDeleteAccountConfirmation, + titleVisibility: .visible + ) { + Button("Delete account", role: .destructive) { + Task { - await viewModel.fetchReferralNetwork() + let result = await viewModel.deleteAccount() + self.handleResult(result) } - viewModel.presentUpdateReferralNetworkSheet = false - }, - dismiss: { - viewModel.presentUpdateReferralNetworkSheet = false - }, - referralNetwork: viewModel.referralNetwork - ) - .environmentObject(themeManager) - .presentationDetents([.height(268)]) - .presentationDragIndicator(.visible) - } - } - - @ViewBuilder - private var authModifiers: some View { - Color.clear - .frame(width: 0, height: 0) - .sheet(isPresented: $viewModel.presentSeedphraseSheet) { - SeedphraseDisplayView( - seedphrase: viewModel.generatedSeedphrase, - onConfirmed: { _ in - viewModel.dismissSeedphraseSheet() + + } + } + .sheet(isPresented: $viewModel.presentSigninWithSolanaSheet) { + + SolanaSignMessageSheet( + isSigningMessage: viewModel.isSigningMessage, + setIsSigningMessage: viewModel.setIsSigningMessage, + signButtonText: "Confirm Seeker Token", + signButtonLabelText: "Claim multiplier", + message: connectWalletProviderViewModel.claimSeekerTokenMessage, + dismiss: { + viewModel.presentSigninWithSolanaSheet = false } ) .environmentObject(themeManager) + .environmentObject(connectWalletProviderViewModel) + .presentationDetents([.height(148)]) } - .confirmationDialog( - "Generate a recovery seedphrase?", - isPresented: $viewModel.presentSeedphraseConfirmation, - titleVisibility: .visible - ) { - Button("Generate") { + .sheet(isPresented: $viewModel.presentUpdateReferralNetworkSheet) { + UpdateReferralNetworkSheet( + api: api, + onSuccess: { + Task { + await viewModel.fetchReferralNetwork() + } + viewModel.presentUpdateReferralNetworkSheet = false + }, + dismiss: { + viewModel.presentUpdateReferralNetworkSheet = false + }, + referralNetwork: viewModel.referralNetwork + ) + .environmentObject(themeManager) + .presentationDetents([.height(268)]) + .presentationDragIndicator(.visible) + } + .overlay(authModifiers) + + #elseif os(macOS) + SettingsForm_macOS( + urApiService: api, + clientId: clientId, + referralCode: referralLinkViewModel.referralCode, + totalReferrals: referralLinkViewModel.totalReferrals, + referralNetworkName: viewModel.referralNetwork?.name, + version: viewModel.version, + isUpdatingAccountPreferences: accountPreferencesViewModel.isUpdatingAccountPreferences, + copyToPasteboard: copyToPasteboard, + presentUpdateReferralNetworkSheet: { + viewModel.presentUpdateReferralNetworkSheet = true + }, + presentDeleteAccountConfirmation: { + viewModel.isPresentedDeleteAccountConfirmation = true + }, + navigate: navigate, + provideEnabled: deviceManager.provideEnabled, + providePaused: deviceManager.providePaused, + deviceName: viewModel.deviceName, + deviceSpec: viewModel.deviceSpec, + presentRenameDevice: viewModel.presentRenameDevice, + canReceiveNotifications: $viewModel.canReceiveNotifications, + canReceiveProductUpdates: $accountPreferencesViewModel.canReceiveProductUpdates, + launchAtStartupEnabled: $viewModel.launchAtStartupEnabled, + networkUserViewModel: networkUserViewModel, + viewModel: viewModel + ) + .task { + await viewModel.fetchDeviceInfo(clientId) + } + .alert("Device name", isPresented: $viewModel.isPresentedRenameDevice) { + TextField("Device name", text: $viewModel.editingDeviceName) + Button("Save") { Task { - await viewModel.executePendingSeedphraseAction() + await saveDeviceName() } } Button("Cancel", role: .cancel) {} - } message: { - Text("A seedphrase lets you recover your account if you lose access. Store it safely.") + } + .onChange(of: accountPreferencesViewModel.saveErrorMessage) { newValue in + if let newValue { + snackbarManager.showSnackbar(message: newValue) + accountPreferencesViewModel.clearSaveErrorMessage() + } } .confirmationDialog( - "Remove this sign-in method?", - isPresented: $viewModel.presentRemoveAuthConfirmation, + "Are you sure you want to delete your account?", + isPresented: $viewModel.isPresentedDeleteAccountConfirmation, titleVisibility: .visible ) { - Button("Remove", role: .destructive) { + Button("Delete account", role: .destructive) { + Task { - await viewModel.executeRemoveAuth() + let result = await viewModel.deleteAccount() + self.handleResult(result) } + } - Button("Cancel", role: .cancel) {} - } message: { - if let authType = viewModel.authTypeToRemove { - Text("Are you sure you want to remove \(authType) as a sign-in method?") - } - } - .sheet(isPresented: $viewModel.presentAddAuthSheet) { - AddAuthSheet(api: api) - .environmentObject(themeManager) - .environmentObject(snackbarManager) - .environmentObject(connectWalletProviderViewModel) } - .onOpenURL { url in - handleWalletDeepLink(url) + .sheet(isPresented: $viewModel.presentUpdateReferralNetworkSheet) { + UpdateReferralNetworkSheet( + api: api, + onSuccess: { + Task { + await viewModel.fetchReferralNetwork() + } + viewModel.presentUpdateReferralNetworkSheet = false + }, + dismiss: { + viewModel.presentUpdateReferralNetworkSheet = false + }, + referralNetwork: viewModel.referralNetwork + ) + .environmentObject(themeManager) } + .overlay(authModifiers) + + #endif + } - #endif - #if os(macOS) @ViewBuilder - private var macBody: some View { - SettingsForm_macOS( - urApiService: api, - clientId: clientId, - referralCode: referralLinkViewModel.referralCode, - totalReferrals: referralLinkViewModel.totalReferrals, - referralNetworkName: viewModel.referralNetwork?.name, - version: viewModel.version, - isUpdatingAccountPreferences: accountPreferencesViewModel.isUpdatingAccountPreferences, - copyToPasteboard: copyToPasteboard, - presentUpdateReferralNetworkSheet: { - viewModel.presentUpdateReferralNetworkSheet = true - }, - presentDeleteAccountConfirmation: { - viewModel.isPresentedDeleteAccountConfirmation = true - }, - navigate: navigate, - provideEnabled: deviceManager.provideEnabled, - providePaused: deviceManager.providePaused, - deviceName: viewModel.deviceName, - deviceSpec: viewModel.deviceSpec, - presentRenameDevice: viewModel.presentRenameDevice, - canReceiveNotifications: $viewModel.canReceiveNotifications, - canReceiveProductUpdates: $accountPreferencesViewModel.canReceiveProductUpdates, - launchAtStartupEnabled: $viewModel.launchAtStartupEnabled, - networkUserViewModel: networkUserViewModel, - viewModel: viewModel - ) - .task { - await viewModel.fetchDeviceInfo(clientId) - } - .alert("Device name", isPresented: $viewModel.isPresentedRenameDevice) { - TextField("Device name", text: $viewModel.editingDeviceName) - Button("Save") { - Task { - await saveDeviceName() - } - } - Button("Cancel", role: .cancel) {} - } - .onChange(of: accountPreferencesViewModel.saveErrorMessage) { newValue in - if let newValue { - snackbarManager.showSnackbar(message: newValue) - accountPreferencesViewModel.clearSaveErrorMessage() - } - } - .confirmationDialog( - "Are you sure you want to delete your account?", - isPresented: $viewModel.isPresentedDeleteAccountConfirmation, - titleVisibility: .visible - ) { - Button("Delete account", role: .destructive) { - - Task { - let result = await viewModel.deleteAccount() - self.handleResult(result) - } - - } - } - .sheet(isPresented: $viewModel.presentUpdateReferralNetworkSheet) { - UpdateReferralNetworkSheet( - api: api, - onSuccess: { - Task { - await viewModel.fetchReferralNetwork() - } - viewModel.presentUpdateReferralNetworkSheet = false - }, - dismiss: { - viewModel.presentUpdateReferralNetworkSheet = false - }, - referralNetwork: viewModel.referralNetwork - ) - .environmentObject(themeManager) - } + private var authModifiers: some View { Color.clear - .frame(width: 0, height: 0) .sheet(isPresented: $viewModel.presentSeedphraseSheet) { SeedphraseDisplayView( seedphrase: viewModel.generatedSeedphrase, @@ -341,8 +278,10 @@ struct SettingsView: View { .environmentObject(snackbarManager) .environmentObject(connectWalletProviderViewModel) } + .onOpenURL { url in + handleWalletDeepLink(url) + } } - #endif private func handleWalletDeepLink(_ url: URL) { let vm = connectWalletProviderViewModel From 9b122c0db3126e6f18dd2e0ccca7de5a2472644a Mon Sep 17 00:00:00 2001 From: ryanmello07 Date: Fri, 17 Jul 2026 21:20:30 +0100 Subject: [PATCH 078/109] fix: ZStack with settingsForm having minimal chain (3 modifiers), all dialogs/sheets moved to authModifiers Color.clear --- .../Main/Account/Settings/SettingsView.swift | 297 +++++++++++------- 1 file changed, 180 insertions(+), 117 deletions(-) diff --git a/app/network/Main/Account/Settings/SettingsView.swift b/app/network/Main/Account/Settings/SettingsView.swift index 5c4f4d72..c071cc94 100644 --- a/app/network/Main/Account/Settings/SettingsView.swift +++ b/app/network/Main/Account/Settings/SettingsView.swift @@ -51,53 +51,70 @@ struct SettingsView: View { } var body: some View { - - #if os(iOS) - SettingsForm_iOS( - urApiService: api, - clientId: clientId, - referralCode: referralLinkViewModel.referralCode, - totalReferrals: referralLinkViewModel.totalReferrals, - referralNetworkName: viewModel.referralNetwork?.name, - version: viewModel.version, - isUpdatingAccountPreferences: accountPreferencesViewModel.isUpdatingAccountPreferences, - copyToPasteboard: copyToPasteboard, - presentUpdateReferralNetworkSheet: { - viewModel.presentUpdateReferralNetworkSheet = true - }, - presentDeleteAccountConfirmation: { - viewModel.isPresentedDeleteAccountConfirmation = true - }, - navigate: navigate, - provideEnabled: deviceManager.provideEnabled, - providePaused: deviceManager.providePaused, - deviceName: viewModel.deviceName, - deviceSpec: viewModel.deviceSpec, - presentRenameDevice: viewModel.presentRenameDevice, - canReceiveNotifications: $viewModel.canReceiveNotifications, - canReceiveProductUpdates: $accountPreferencesViewModel.canReceiveProductUpdates, - networkUserViewModel: networkUserViewModel, - viewModel: viewModel, - ) - .background(themeManager.currentTheme.backgroundColor) - .task { - await viewModel.fetchDeviceInfo(clientId) - } - .alert("Device name", isPresented: $viewModel.isPresentedRenameDevice) { - TextField("Device name", text: $viewModel.editingDeviceName) - Button("Save") { - Task { - await saveDeviceName() + if useZStack { + ZStack { + authModifiers + settingsForm + .background(themeManager.currentTheme.backgroundColor) + .task { + await viewModel.fetchDeviceInfo(clientId) + } + .alert("Device name", isPresented: $viewModel.isPresentedRenameDevice) { + TextField("Device name", text: $viewModel.editingDeviceName) + Button("Save") { + Task { + await saveDeviceName() + } + } + Button("Cancel", role: .cancel) {} + } + .onChange(of: accountPreferencesViewModel.saveErrorMessage) { newValue in + if let newValue { + snackbarManager.showSnackbar(message: newValue) + accountPreferencesViewModel.clearSaveErrorMessage() + } } - } - Button("Cancel", role: .cancel) {} - } - .onChange(of: accountPreferencesViewModel.saveErrorMessage) { newValue in - if let newValue { - snackbarManager.showSnackbar(message: newValue) - accountPreferencesViewModel.clearSaveErrorMessage() - } } + } + } + + private let useZStack = true + + #if os(iOS) + @ViewBuilder + private var settingsForm: some View { + SettingsForm_iOS( + urApiService: api, + clientId: clientId, + referralCode: referralLinkViewModel.referralCode, + totalReferrals: referralLinkViewModel.totalReferrals, + referralNetworkName: viewModel.referralNetwork?.name, + version: viewModel.version, + isUpdatingAccountPreferences: accountPreferencesViewModel.isUpdatingAccountPreferences, + copyToPasteboard: copyToPasteboard, + presentUpdateReferralNetworkSheet: { + viewModel.presentUpdateReferralNetworkSheet = true + }, + presentDeleteAccountConfirmation: { + viewModel.isPresentedDeleteAccountConfirmation = true + }, + navigate: navigate, + provideEnabled: deviceManager.provideEnabled, + providePaused: deviceManager.providePaused, + deviceName: viewModel.deviceName, + deviceSpec: viewModel.deviceSpec, + presentRenameDevice: viewModel.presentRenameDevice, + canReceiveNotifications: $viewModel.canReceiveNotifications, + canReceiveProductUpdates: $accountPreferencesViewModel.canReceiveProductUpdates, + networkUserViewModel: networkUserViewModel, + viewModel: viewModel, + ) + } + + @ViewBuilder + private var authModifiers: some View { + Color.clear + .frame(width: 0, height: 0) .confirmationDialog( "Are you sure you want to delete your account?", isPresented: $viewModel.isPresentedDeleteAccountConfirmation, @@ -146,93 +163,141 @@ struct SettingsView: View { .presentationDetents([.height(268)]) .presentationDragIndicator(.visible) } - .overlay(authModifiers) - - #elseif os(macOS) - SettingsForm_macOS( - urApiService: api, - clientId: clientId, - referralCode: referralLinkViewModel.referralCode, - totalReferrals: referralLinkViewModel.totalReferrals, - referralNetworkName: viewModel.referralNetwork?.name, - version: viewModel.version, - isUpdatingAccountPreferences: accountPreferencesViewModel.isUpdatingAccountPreferences, - copyToPasteboard: copyToPasteboard, - presentUpdateReferralNetworkSheet: { - viewModel.presentUpdateReferralNetworkSheet = true - }, - presentDeleteAccountConfirmation: { - viewModel.isPresentedDeleteAccountConfirmation = true - }, - navigate: navigate, - provideEnabled: deviceManager.provideEnabled, - providePaused: deviceManager.providePaused, - deviceName: viewModel.deviceName, - deviceSpec: viewModel.deviceSpec, - presentRenameDevice: viewModel.presentRenameDevice, - canReceiveNotifications: $viewModel.canReceiveNotifications, - canReceiveProductUpdates: $accountPreferencesViewModel.canReceiveProductUpdates, - launchAtStartupEnabled: $viewModel.launchAtStartupEnabled, - networkUserViewModel: networkUserViewModel, - viewModel: viewModel - ) - .task { - await viewModel.fetchDeviceInfo(clientId) + .sheet(isPresented: $viewModel.presentSeedphraseSheet) { + SeedphraseDisplayView( + seedphrase: viewModel.generatedSeedphrase, + onConfirmed: { _ in + viewModel.dismissSeedphraseSheet() + } + ) + .environmentObject(themeManager) } - .alert("Device name", isPresented: $viewModel.isPresentedRenameDevice) { - TextField("Device name", text: $viewModel.editingDeviceName) - Button("Save") { + .confirmationDialog( + "Generate a recovery seedphrase?", + isPresented: $viewModel.presentSeedphraseConfirmation, + titleVisibility: .visible + ) { + Button("Generate") { Task { - await saveDeviceName() + await viewModel.executePendingSeedphraseAction() } } Button("Cancel", role: .cancel) {} - } - .onChange(of: accountPreferencesViewModel.saveErrorMessage) { newValue in - if let newValue { - snackbarManager.showSnackbar(message: newValue) - accountPreferencesViewModel.clearSaveErrorMessage() - } + } message: { + Text("A seedphrase lets you recover your account if you lose access. Store it safely.") } .confirmationDialog( - "Are you sure you want to delete your account?", - isPresented: $viewModel.isPresentedDeleteAccountConfirmation, + "Remove this sign-in method?", + isPresented: $viewModel.presentRemoveAuthConfirmation, titleVisibility: .visible ) { - Button("Delete account", role: .destructive) { - + Button("Remove", role: .destructive) { Task { - let result = await viewModel.deleteAccount() - self.handleResult(result) + await viewModel.executeRemoveAuth() } - + } + Button("Cancel", role: .cancel) {} + } message: { + if let authType = viewModel.authTypeToRemove { + Text("Are you sure you want to remove \(authType) as a sign-in method?") } } - .sheet(isPresented: $viewModel.presentUpdateReferralNetworkSheet) { - UpdateReferralNetworkSheet( - api: api, - onSuccess: { - Task { - await viewModel.fetchReferralNetwork() - } - viewModel.presentUpdateReferralNetworkSheet = false - }, - dismiss: { - viewModel.presentUpdateReferralNetworkSheet = false - }, - referralNetwork: viewModel.referralNetwork - ) - .environmentObject(themeManager) + .sheet(isPresented: $viewModel.presentAddAuthSheet) { + AddAuthSheet(api: api) + .environmentObject(themeManager) + .environmentObject(snackbarManager) + .environmentObject(connectWalletProviderViewModel) + } + .onOpenURL { url in + handleWalletDeepLink(url) } - .overlay(authModifiers) - - #endif - } + #endif + #if os(macOS) @ViewBuilder - private var authModifiers: some View { + private var settingsForm: some View { + SettingsForm_macOS( + urApiService: api, + clientId: clientId, + referralCode: referralLinkViewModel.referralCode, + totalReferrals: referralLinkViewModel.totalReferrals, + referralNetworkName: viewModel.referralNetwork?.name, + version: viewModel.version, + isUpdatingAccountPreferences: accountPreferencesViewModel.isUpdatingAccountPreferences, + copyToPasteboard: copyToPasteboard, + presentUpdateReferralNetworkSheet: { + viewModel.presentUpdateReferralNetworkSheet = true + }, + presentDeleteAccountConfirmation: { + viewModel.isPresentedDeleteAccountConfirmation = true + }, + navigate: navigate, + provideEnabled: deviceManager.provideEnabled, + providePaused: deviceManager.providePaused, + deviceName: viewModel.deviceName, + deviceSpec: viewModel.deviceSpec, + presentRenameDevice: viewModel.presentRenameDevice, + canReceiveNotifications: $viewModel.canReceiveNotifications, + canReceiveProductUpdates: $accountPreferencesViewModel.canReceiveProductUpdates, + launchAtStartupEnabled: $viewModel.launchAtStartupEnabled, + networkUserViewModel: networkUserViewModel, + viewModel: viewModel + ) + .task { + await viewModel.fetchDeviceInfo(clientId) + } + .alert("Device name", isPresented: $viewModel.isPresentedRenameDevice) { + TextField("Device name", text: $viewModel.editingDeviceName) + Button("Save") { + Task { + await saveDeviceName() + } + } + Button("Cancel", role: .cancel) {} + } + .onChange(of: accountPreferencesViewModel.saveErrorMessage) { newValue in + if let newValue { + snackbarManager.showSnackbar(message: newValue) + accountPreferencesViewModel.clearSaveErrorMessage() + } + } + .confirmationDialog( + "Are you sure you want to delete your account?", + isPresented: $viewModel.isPresentedDeleteAccountConfirmation, + titleVisibility: .visible + ) { + Button("Delete account", role: .destructive) { + + Task { + let result = await viewModel.deleteAccount() + self.handleResult(result) + } + + } + } + .sheet(isPresented: $viewModel.presentUpdateReferralNetworkSheet) { + UpdateReferralNetworkSheet( + api: api, + onSuccess: { + Task { + await viewModel.fetchReferralNetwork() + } + viewModel.presentUpdateReferralNetworkSheet = false + }, + dismiss: { + viewModel.presentUpdateReferralNetworkSheet = false + }, + referralNetwork: viewModel.referralNetwork + ) + .environmentObject(themeManager) + } + } + + @ViewBuilder + private var macAuthModifiers: some View { Color.clear + .frame(width: 0, height: 0) .sheet(isPresented: $viewModel.presentSeedphraseSheet) { SeedphraseDisplayView( seedphrase: viewModel.generatedSeedphrase, @@ -278,10 +343,8 @@ struct SettingsView: View { .environmentObject(snackbarManager) .environmentObject(connectWalletProviderViewModel) } - .onOpenURL { url in - handleWalletDeepLink(url) - } } + #endif private func handleWalletDeepLink(_ url: URL) { let vm = connectWalletProviderViewModel From 7b8ae57efefdc8044a1ebdf77547368521d83273 Mon Sep 17 00:00:00 2001 From: ryanmello07 Date: Fri, 17 Jul 2026 21:34:29 +0100 Subject: [PATCH 079/109] fix: split all modifier chains into independent @ViewBuilder computed properties (baseFormView, coreSheetsView, authSheetsView) for separate type-check resolution --- .../Main/Account/Settings/SettingsView.swift | 106 +++++++++--------- 1 file changed, 55 insertions(+), 51 deletions(-) diff --git a/app/network/Main/Account/Settings/SettingsView.swift b/app/network/Main/Account/Settings/SettingsView.swift index c071cc94..c71e46ff 100644 --- a/app/network/Main/Account/Settings/SettingsView.swift +++ b/app/network/Main/Account/Settings/SettingsView.swift @@ -51,38 +51,26 @@ struct SettingsView: View { } var body: some View { - if useZStack { - ZStack { - authModifiers - settingsForm - .background(themeManager.currentTheme.backgroundColor) - .task { - await viewModel.fetchDeviceInfo(clientId) - } - .alert("Device name", isPresented: $viewModel.isPresentedRenameDevice) { - TextField("Device name", text: $viewModel.editingDeviceName) - Button("Save") { - Task { - await saveDeviceName() - } - } - Button("Cancel", role: .cancel) {} - } - .onChange(of: accountPreferencesViewModel.saveErrorMessage) { newValue in - if let newValue { - snackbarManager.showSnackbar(message: newValue) - accountPreferencesViewModel.clearSaveErrorMessage() - } - } - } + #if os(iOS) + ZStack { + baseFormView + coreSheetsView + authSheetsView } + .onOpenURL { url in + handleWalletDeepLink(url) + } + #elseif os(macOS) + ZStack { + macFormView + macAuthSheetsView + } + #endif } - private let useZStack = true - #if os(iOS) @ViewBuilder - private var settingsForm: some View { + private var baseFormView: some View { SettingsForm_iOS( urApiService: api, clientId: clientId, @@ -109,28 +97,44 @@ struct SettingsView: View { networkUserViewModel: networkUserViewModel, viewModel: viewModel, ) + .background(themeManager.currentTheme.backgroundColor) + .task { + await viewModel.fetchDeviceInfo(clientId) + } + .alert("Device name", isPresented: $viewModel.isPresentedRenameDevice) { + TextField("Device name", text: $viewModel.editingDeviceName) + Button("Save") { + Task { + await saveDeviceName() + } + } + Button("Cancel", role: .cancel) {} + } + .onChange(of: accountPreferencesViewModel.saveErrorMessage) { newValue in + if let newValue { + snackbarManager.showSnackbar(message: newValue) + accountPreferencesViewModel.clearSaveErrorMessage() + } + } + .confirmationDialog( + "Are you sure you want to delete your account?", + isPresented: $viewModel.isPresentedDeleteAccountConfirmation, + titleVisibility: .visible + ) { + Button("Delete account", role: .destructive) { + Task { + let result = await viewModel.deleteAccount() + self.handleResult(result) + } + } + } } @ViewBuilder - private var authModifiers: some View { + private var coreSheetsView: some View { Color.clear .frame(width: 0, height: 0) - .confirmationDialog( - "Are you sure you want to delete your account?", - isPresented: $viewModel.isPresentedDeleteAccountConfirmation, - titleVisibility: .visible - ) { - Button("Delete account", role: .destructive) { - - Task { - let result = await viewModel.deleteAccount() - self.handleResult(result) - } - - } - } .sheet(isPresented: $viewModel.presentSigninWithSolanaSheet) { - SolanaSignMessageSheet( isSigningMessage: viewModel.isSigningMessage, setIsSigningMessage: viewModel.setIsSigningMessage, @@ -143,7 +147,6 @@ struct SettingsView: View { ) .environmentObject(themeManager) .environmentObject(connectWalletProviderViewModel) - .presentationDetents([.height(148)]) } .sheet(isPresented: $viewModel.presentUpdateReferralNetworkSheet) { UpdateReferralNetworkSheet( @@ -160,9 +163,13 @@ struct SettingsView: View { referralNetwork: viewModel.referralNetwork ) .environmentObject(themeManager) - .presentationDetents([.height(268)]) - .presentationDragIndicator(.visible) } + } + + @ViewBuilder + private var authSheetsView: some View { + Color.clear + .frame(width: 0, height: 0) .sheet(isPresented: $viewModel.presentSeedphraseSheet) { SeedphraseDisplayView( seedphrase: viewModel.generatedSeedphrase, @@ -208,15 +215,12 @@ struct SettingsView: View { .environmentObject(snackbarManager) .environmentObject(connectWalletProviderViewModel) } - .onOpenURL { url in - handleWalletDeepLink(url) - } } #endif #if os(macOS) @ViewBuilder - private var settingsForm: some View { + private var macFormView: some View { SettingsForm_macOS( urApiService: api, clientId: clientId, @@ -295,7 +299,7 @@ struct SettingsView: View { } @ViewBuilder - private var macAuthModifiers: some View { + private var macAuthSheetsView: some View { Color.clear .frame(width: 0, height: 0) .sheet(isPresented: $viewModel.presentSeedphraseSheet) { From 796d6e9653dad41203d36c31fb8a06b005c446e4 Mon Sep 17 00:00:00 2001 From: ryanmello07 Date: Fri, 17 Jul 2026 21:43:46 +0100 Subject: [PATCH 080/109] fix: Color.clear as base, all dialogs/sheets/onOpenURL on Color.clear, form in .overlay() - keeps body expression lightweight --- .../Main/Account/Settings/SettingsView.swift | 305 ++++++++---------- 1 file changed, 138 insertions(+), 167 deletions(-) diff --git a/app/network/Main/Account/Settings/SettingsView.swift b/app/network/Main/Account/Settings/SettingsView.swift index c71e46ff..e3223e65 100644 --- a/app/network/Main/Account/Settings/SettingsView.swift +++ b/app/network/Main/Account/Settings/SettingsView.swift @@ -51,89 +51,22 @@ struct SettingsView: View { } var body: some View { + #if os(iOS) - ZStack { - baseFormView - coreSheetsView - authSheetsView - } - .onOpenURL { url in - handleWalletDeepLink(url) - } - #elseif os(macOS) - ZStack { - macFormView - macAuthSheetsView - } - #endif - } - - #if os(iOS) - @ViewBuilder - private var baseFormView: some View { - SettingsForm_iOS( - urApiService: api, - clientId: clientId, - referralCode: referralLinkViewModel.referralCode, - totalReferrals: referralLinkViewModel.totalReferrals, - referralNetworkName: viewModel.referralNetwork?.name, - version: viewModel.version, - isUpdatingAccountPreferences: accountPreferencesViewModel.isUpdatingAccountPreferences, - copyToPasteboard: copyToPasteboard, - presentUpdateReferralNetworkSheet: { - viewModel.presentUpdateReferralNetworkSheet = true - }, - presentDeleteAccountConfirmation: { - viewModel.isPresentedDeleteAccountConfirmation = true - }, - navigate: navigate, - provideEnabled: deviceManager.provideEnabled, - providePaused: deviceManager.providePaused, - deviceName: viewModel.deviceName, - deviceSpec: viewModel.deviceSpec, - presentRenameDevice: viewModel.presentRenameDevice, - canReceiveNotifications: $viewModel.canReceiveNotifications, - canReceiveProductUpdates: $accountPreferencesViewModel.canReceiveProductUpdates, - networkUserViewModel: networkUserViewModel, - viewModel: viewModel, - ) - .background(themeManager.currentTheme.backgroundColor) - .task { - await viewModel.fetchDeviceInfo(clientId) - } - .alert("Device name", isPresented: $viewModel.isPresentedRenameDevice) { - TextField("Device name", text: $viewModel.editingDeviceName) - Button("Save") { - Task { - await saveDeviceName() - } - } - Button("Cancel", role: .cancel) {} - } - .onChange(of: accountPreferencesViewModel.saveErrorMessage) { newValue in - if let newValue { - snackbarManager.showSnackbar(message: newValue) - accountPreferencesViewModel.clearSaveErrorMessage() - } - } - .confirmationDialog( - "Are you sure you want to delete your account?", - isPresented: $viewModel.isPresentedDeleteAccountConfirmation, - titleVisibility: .visible - ) { - Button("Delete account", role: .destructive) { - Task { - let result = await viewModel.deleteAccount() - self.handleResult(result) - } - } - } - } - - @ViewBuilder - private var coreSheetsView: some View { Color.clear .frame(width: 0, height: 0) + .confirmationDialog( + "Are you sure you want to delete your account?", + isPresented: $viewModel.isPresentedDeleteAccountConfirmation, + titleVisibility: .visible + ) { + Button("Delete account", role: .destructive) { + Task { + let result = await viewModel.deleteAccount() + self.handleResult(result) + } + } + } .sheet(isPresented: $viewModel.presentSigninWithSolanaSheet) { SolanaSignMessageSheet( isSigningMessage: viewModel.isSigningMessage, @@ -147,6 +80,7 @@ struct SettingsView: View { ) .environmentObject(themeManager) .environmentObject(connectWalletProviderViewModel) + .presentationDetents([.height(148)]) } .sheet(isPresented: $viewModel.presentUpdateReferralNetworkSheet) { UpdateReferralNetworkSheet( @@ -163,13 +97,9 @@ struct SettingsView: View { referralNetwork: viewModel.referralNetwork ) .environmentObject(themeManager) + .presentationDetents([.height(268)]) + .presentationDragIndicator(.visible) } - } - - @ViewBuilder - private var authSheetsView: some View { - Color.clear - .frame(width: 0, height: 0) .sheet(isPresented: $viewModel.presentSeedphraseSheet) { SeedphraseDisplayView( seedphrase: viewModel.generatedSeedphrase, @@ -215,93 +145,132 @@ struct SettingsView: View { .environmentObject(snackbarManager) .environmentObject(connectWalletProviderViewModel) } - } - #endif - - #if os(macOS) - @ViewBuilder - private var macFormView: some View { - SettingsForm_macOS( - urApiService: api, - clientId: clientId, - referralCode: referralLinkViewModel.referralCode, - totalReferrals: referralLinkViewModel.totalReferrals, - referralNetworkName: viewModel.referralNetwork?.name, - version: viewModel.version, - isUpdatingAccountPreferences: accountPreferencesViewModel.isUpdatingAccountPreferences, - copyToPasteboard: copyToPasteboard, - presentUpdateReferralNetworkSheet: { - viewModel.presentUpdateReferralNetworkSheet = true - }, - presentDeleteAccountConfirmation: { - viewModel.isPresentedDeleteAccountConfirmation = true - }, - navigate: navigate, - provideEnabled: deviceManager.provideEnabled, - providePaused: deviceManager.providePaused, - deviceName: viewModel.deviceName, - deviceSpec: viewModel.deviceSpec, - presentRenameDevice: viewModel.presentRenameDevice, - canReceiveNotifications: $viewModel.canReceiveNotifications, - canReceiveProductUpdates: $accountPreferencesViewModel.canReceiveProductUpdates, - launchAtStartupEnabled: $viewModel.launchAtStartupEnabled, - networkUserViewModel: networkUserViewModel, - viewModel: viewModel - ) - .task { - await viewModel.fetchDeviceInfo(clientId) - } - .alert("Device name", isPresented: $viewModel.isPresentedRenameDevice) { - TextField("Device name", text: $viewModel.editingDeviceName) - Button("Save") { - Task { - await saveDeviceName() + .onOpenURL { url in + handleWalletDeepLink(url) + } + .overlay( + SettingsForm_iOS( + urApiService: api, + clientId: clientId, + referralCode: referralLinkViewModel.referralCode, + totalReferrals: referralLinkViewModel.totalReferrals, + referralNetworkName: viewModel.referralNetwork?.name, + version: viewModel.version, + isUpdatingAccountPreferences: accountPreferencesViewModel.isUpdatingAccountPreferences, + copyToPasteboard: copyToPasteboard, + presentUpdateReferralNetworkSheet: { + viewModel.presentUpdateReferralNetworkSheet = true + }, + presentDeleteAccountConfirmation: { + viewModel.isPresentedDeleteAccountConfirmation = true + }, + navigate: navigate, + provideEnabled: deviceManager.provideEnabled, + providePaused: deviceManager.providePaused, + deviceName: viewModel.deviceName, + deviceSpec: viewModel.deviceSpec, + presentRenameDevice: viewModel.presentRenameDevice, + canReceiveNotifications: $viewModel.canReceiveNotifications, + canReceiveProductUpdates: $accountPreferencesViewModel.canReceiveProductUpdates, + networkUserViewModel: networkUserViewModel, + viewModel: viewModel, + ) + .background(themeManager.currentTheme.backgroundColor) + .task { + await viewModel.fetchDeviceInfo(clientId) + } + .alert("Device name", isPresented: $viewModel.isPresentedRenameDevice) { + TextField("Device name", text: $viewModel.editingDeviceName) + Button("Save") { + Task { + await saveDeviceName() + } + } + Button("Cancel", role: .cancel) {} } + .onChange(of: accountPreferencesViewModel.saveErrorMessage) { newValue in + if let newValue { + snackbarManager.showSnackbar(message: newValue) + accountPreferencesViewModel.clearSaveErrorMessage() + } + ) + ) + #elseif os(macOS) + SettingsForm_macOS( + urApiService: api, + clientId: clientId, + referralCode: referralLinkViewModel.referralCode, + totalReferrals: referralLinkViewModel.totalReferrals, + referralNetworkName: viewModel.referralNetwork?.name, + version: viewModel.version, + isUpdatingAccountPreferences: accountPreferencesViewModel.isUpdatingAccountPreferences, + copyToPasteboard: copyToPasteboard, + presentUpdateReferralNetworkSheet: { + viewModel.presentUpdateReferralNetworkSheet = true + }, + presentDeleteAccountConfirmation: { + viewModel.isPresentedDeleteAccountConfirmation = true + }, + navigate: navigate, + provideEnabled: deviceManager.provideEnabled, + providePaused: deviceManager.providePaused, + deviceName: viewModel.deviceName, + deviceSpec: viewModel.deviceSpec, + presentRenameDevice: viewModel.presentRenameDevice, + canReceiveNotifications: $viewModel.canReceiveNotifications, + canReceiveProductUpdates: $accountPreferencesViewModel.canReceiveProductUpdates, + launchAtStartupEnabled: $viewModel.launchAtStartupEnabled, + networkUserViewModel: networkUserViewModel, + viewModel: viewModel + ) + .task { + await viewModel.fetchDeviceInfo(clientId) } - Button("Cancel", role: .cancel) {} - } - .onChange(of: accountPreferencesViewModel.saveErrorMessage) { newValue in - if let newValue { - snackbarManager.showSnackbar(message: newValue) - accountPreferencesViewModel.clearSaveErrorMessage() + .alert("Device name", isPresented: $viewModel.isPresentedRenameDevice) { + TextField("Device name", text: $viewModel.editingDeviceName) + Button("Save") { + Task { + await saveDeviceName() + } + } + Button("Cancel", role: .cancel) {} } - } - .confirmationDialog( - "Are you sure you want to delete your account?", - isPresented: $viewModel.isPresentedDeleteAccountConfirmation, - titleVisibility: .visible - ) { - Button("Delete account", role: .destructive) { - - Task { - let result = await viewModel.deleteAccount() - self.handleResult(result) + .onChange(of: accountPreferencesViewModel.saveErrorMessage) { newValue in + if let newValue { + snackbarManager.showSnackbar(message: newValue) + accountPreferencesViewModel.clearSaveErrorMessage() } - } - } - .sheet(isPresented: $viewModel.presentUpdateReferralNetworkSheet) { - UpdateReferralNetworkSheet( - api: api, - onSuccess: { + .confirmationDialog( + "Are you sure you want to delete your account?", + isPresented: $viewModel.isPresentedDeleteAccountConfirmation, + titleVisibility: .visible + ) { + Button("Delete account", role: .destructive) { + Task { - await viewModel.fetchReferralNetwork() + let result = await viewModel.deleteAccount() + self.handleResult(result) } - viewModel.presentUpdateReferralNetworkSheet = false - }, - dismiss: { - viewModel.presentUpdateReferralNetworkSheet = false - }, - referralNetwork: viewModel.referralNetwork - ) - .environmentObject(themeManager) - } - } - - @ViewBuilder - private var macAuthSheetsView: some View { - Color.clear - .frame(width: 0, height: 0) + + } + } + .sheet(isPresented: $viewModel.presentUpdateReferralNetworkSheet) { + UpdateReferralNetworkSheet( + api: api, + onSuccess: { + Task { + await viewModel.fetchReferralNetwork() + } + viewModel.presentUpdateReferralNetworkSheet = false + }, + dismiss: { + viewModel.presentUpdateReferralNetworkSheet = false + }, + referralNetwork: viewModel.referralNetwork + ) + .environmentObject(themeManager) + } .sheet(isPresented: $viewModel.presentSeedphraseSheet) { SeedphraseDisplayView( seedphrase: viewModel.generatedSeedphrase, @@ -347,8 +316,10 @@ struct SettingsView: View { .environmentObject(snackbarManager) .environmentObject(connectWalletProviderViewModel) } + + #endif + } - #endif private func handleWalletDeepLink(_ url: URL) { let vm = connectWalletProviderViewModel From 8461e34690763e8d403127581de757c50e233912 Mon Sep 17 00:00:00 2001 From: ryanmello07 Date: Fri, 17 Jul 2026 21:55:59 +0100 Subject: [PATCH 081/109] fix: fix syntax error - ) vs } on onChange trailing closure end --- app/network/Main/Account/Settings/SettingsView.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/network/Main/Account/Settings/SettingsView.swift b/app/network/Main/Account/Settings/SettingsView.swift index e3223e65..cbc61c34 100644 --- a/app/network/Main/Account/Settings/SettingsView.swift +++ b/app/network/Main/Account/Settings/SettingsView.swift @@ -193,7 +193,7 @@ struct SettingsView: View { snackbarManager.showSnackbar(message: newValue) accountPreferencesViewModel.clearSaveErrorMessage() } - ) + } ) #elseif os(macOS) SettingsForm_macOS( From 1b7612df68068403ca978e1c41dce0c9f8b28251 Mon Sep 17 00:00:00 2001 From: ryanmello07 Date: Sat, 18 Jul 2026 00:11:23 +0100 Subject: [PATCH 082/109] fix: extract auth modifiers into separate SettingsAuthModifiersView struct for independent type-check; use ZStack in body --- .../Settings/SettingsAuthModifiersView.swift | 81 +++++++ .../Main/Account/Settings/SettingsView.swift | 211 ++++++------------ 2 files changed, 151 insertions(+), 141 deletions(-) create mode 100644 app/network/Main/Account/Settings/SettingsAuthModifiersView.swift diff --git a/app/network/Main/Account/Settings/SettingsAuthModifiersView.swift b/app/network/Main/Account/Settings/SettingsAuthModifiersView.swift new file mode 100644 index 00000000..7fce50e0 --- /dev/null +++ b/app/network/Main/Account/Settings/SettingsAuthModifiersView.swift @@ -0,0 +1,81 @@ +// +// SettingsAuthModifiersView.swift +// URnetwork +// +// Created by Stuart Kuentzel on 2024/12/10. +// + +import SwiftUI +import URnetworkSdk + +/** + Separate view holding the seedphrase and sign-in-method sheet/dialog modifiers. + + This exists solely to keep the modifier chains short enough for Swift's + type-checker: SettingsView.body stays at 8 modifiers (its original length), + and the 4 auth-specific modifiers live here on a trivial Color.clear base, + each in its own independently-type-checked struct. + */ +struct SettingsAuthModifiersView: View { + + @EnvironmentObject var themeManager: ThemeManager + @EnvironmentObject var snackbarManager: UrSnackbarManager + @EnvironmentObject var connectWalletProviderViewModel: ConnectWalletProviderViewModel + + @ObservedObject var viewModel: SettingsView.ViewModel + let api: UrApiServiceProtocol + let handleWalletDeepLink: (URL) -> Void + + var body: some View { + Color.clear + .frame(width: 0, height: 0) + .sheet(isPresented: $viewModel.presentSeedphraseSheet) { + SeedphraseDisplayView( + seedphrase: viewModel.generatedSeedphrase, + onConfirmed: { _ in + viewModel.dismissSeedphraseSheet() + } + ) + .environmentObject(themeManager) + } + .confirmationDialog( + "Generate a recovery seedphrase?", + isPresented: $viewModel.presentSeedphraseConfirmation, + titleVisibility: .visible + ) { + Button("Generate") { + Task { + await viewModel.executePendingSeedphraseAction() + } + } + Button("Cancel", role: .cancel) {} + } message: { + Text("A seedphrase lets you recover your account if you lose access. Store it safely.") + } + .confirmationDialog( + "Remove this sign-in method?", + isPresented: $viewModel.presentRemoveAuthConfirmation, + titleVisibility: .visible + ) { + Button("Remove", role: .destructive) { + Task { + await viewModel.executeRemoveAuth() + } + } + Button("Cancel", role: .cancel) {} + } message: { + if let authType = viewModel.authTypeToRemove { + Text("Are you sure you want to remove \(authType) as a sign-in method?") + } + } + .sheet(isPresented: $viewModel.presentAddAuthSheet) { + AddAuthSheet(api: api) + .environmentObject(themeManager) + .environmentObject(snackbarManager) + .environmentObject(connectWalletProviderViewModel) + } + .onOpenURL { url in + handleWalletDeepLink(url) + } + } +} diff --git a/app/network/Main/Account/Settings/SettingsView.swift b/app/network/Main/Account/Settings/SettingsView.swift index cbc61c34..d99925af 100644 --- a/app/network/Main/Account/Settings/SettingsView.swift +++ b/app/network/Main/Account/Settings/SettingsView.swift @@ -53,21 +53,68 @@ struct SettingsView: View { var body: some View { #if os(iOS) - Color.clear - .frame(width: 0, height: 0) + ZStack { + SettingsForm_iOS( + urApiService: api, + clientId: clientId, + referralCode: referralLinkViewModel.referralCode, + totalReferrals: referralLinkViewModel.totalReferrals, + referralNetworkName: viewModel.referralNetwork?.name, + version: viewModel.version, + isUpdatingAccountPreferences: accountPreferencesViewModel.isUpdatingAccountPreferences, + copyToPasteboard: copyToPasteboard, + presentUpdateReferralNetworkSheet: { + viewModel.presentUpdateReferralNetworkSheet = true + }, + presentDeleteAccountConfirmation: { + viewModel.isPresentedDeleteAccountConfirmation = true + }, + navigate: navigate, + provideEnabled: deviceManager.provideEnabled, + providePaused: deviceManager.providePaused, + deviceName: viewModel.deviceName, + deviceSpec: viewModel.deviceSpec, + presentRenameDevice: viewModel.presentRenameDevice, + canReceiveNotifications: $viewModel.canReceiveNotifications, + canReceiveProductUpdates: $accountPreferencesViewModel.canReceiveProductUpdates, + networkUserViewModel: networkUserViewModel, + viewModel: viewModel, + ) + .background(themeManager.currentTheme.backgroundColor) + .task { + await viewModel.fetchDeviceInfo(clientId) + } + .alert("Device name", isPresented: $viewModel.isPresentedRenameDevice) { + TextField("Device name", text: $viewModel.editingDeviceName) + Button("Save") { + Task { + await saveDeviceName() + } + } + Button("Cancel", role: .cancel) {} + } + .onChange(of: accountPreferencesViewModel.saveErrorMessage) { newValue in + if let newValue { + snackbarManager.showSnackbar(message: newValue) + accountPreferencesViewModel.clearSaveErrorMessage() + } + } .confirmationDialog( "Are you sure you want to delete your account?", isPresented: $viewModel.isPresentedDeleteAccountConfirmation, titleVisibility: .visible ) { Button("Delete account", role: .destructive) { + Task { let result = await viewModel.deleteAccount() self.handleResult(result) } + } } .sheet(isPresented: $viewModel.presentSigninWithSolanaSheet) { + SolanaSignMessageSheet( isSigningMessage: viewModel.isSigningMessage, setIsSigningMessage: viewModel.setIsSigningMessage, @@ -100,102 +147,19 @@ struct SettingsView: View { .presentationDetents([.height(268)]) .presentationDragIndicator(.visible) } - .sheet(isPresented: $viewModel.presentSeedphraseSheet) { - SeedphraseDisplayView( - seedphrase: viewModel.generatedSeedphrase, - onConfirmed: { _ in - viewModel.dismissSeedphraseSheet() - } - ) - .environmentObject(themeManager) - } - .confirmationDialog( - "Generate a recovery seedphrase?", - isPresented: $viewModel.presentSeedphraseConfirmation, - titleVisibility: .visible - ) { - Button("Generate") { - Task { - await viewModel.executePendingSeedphraseAction() - } - } - Button("Cancel", role: .cancel) {} - } message: { - Text("A seedphrase lets you recover your account if you lose access. Store it safely.") - } - .confirmationDialog( - "Remove this sign-in method?", - isPresented: $viewModel.presentRemoveAuthConfirmation, - titleVisibility: .visible - ) { - Button("Remove", role: .destructive) { - Task { - await viewModel.executeRemoveAuth() - } - } - Button("Cancel", role: .cancel) {} - } message: { - if let authType = viewModel.authTypeToRemove { - Text("Are you sure you want to remove \(authType) as a sign-in method?") - } - } - .sheet(isPresented: $viewModel.presentAddAuthSheet) { - AddAuthSheet(api: api) - .environmentObject(themeManager) - .environmentObject(snackbarManager) - .environmentObject(connectWalletProviderViewModel) - } - .onOpenURL { url in - handleWalletDeepLink(url) - } - .overlay( - SettingsForm_iOS( - urApiService: api, - clientId: clientId, - referralCode: referralLinkViewModel.referralCode, - totalReferrals: referralLinkViewModel.totalReferrals, - referralNetworkName: viewModel.referralNetwork?.name, - version: viewModel.version, - isUpdatingAccountPreferences: accountPreferencesViewModel.isUpdatingAccountPreferences, - copyToPasteboard: copyToPasteboard, - presentUpdateReferralNetworkSheet: { - viewModel.presentUpdateReferralNetworkSheet = true - }, - presentDeleteAccountConfirmation: { - viewModel.isPresentedDeleteAccountConfirmation = true - }, - navigate: navigate, - provideEnabled: deviceManager.provideEnabled, - providePaused: deviceManager.providePaused, - deviceName: viewModel.deviceName, - deviceSpec: viewModel.deviceSpec, - presentRenameDevice: viewModel.presentRenameDevice, - canReceiveNotifications: $viewModel.canReceiveNotifications, - canReceiveProductUpdates: $accountPreferencesViewModel.canReceiveProductUpdates, - networkUserViewModel: networkUserViewModel, - viewModel: viewModel, - ) - .background(themeManager.currentTheme.backgroundColor) - .task { - await viewModel.fetchDeviceInfo(clientId) - } - .alert("Device name", isPresented: $viewModel.isPresentedRenameDevice) { - TextField("Device name", text: $viewModel.editingDeviceName) - Button("Save") { - Task { - await saveDeviceName() - } - } - Button("Cancel", role: .cancel) {} - } - .onChange(of: accountPreferencesViewModel.saveErrorMessage) { newValue in - if let newValue { - snackbarManager.showSnackbar(message: newValue) - accountPreferencesViewModel.clearSaveErrorMessage() - } - } + + SettingsAuthModifiersView( + viewModel: viewModel, + api: api, + handleWalletDeepLink: handleWalletDeepLink ) + .environmentObject(themeManager) + .environmentObject(snackbarManager) + .environmentObject(connectWalletProviderViewModel) + } + #elseif os(macOS) + ZStack { SettingsForm_macOS( urApiService: api, clientId: clientId, @@ -271,51 +235,16 @@ struct SettingsView: View { ) .environmentObject(themeManager) } - .sheet(isPresented: $viewModel.presentSeedphraseSheet) { - SeedphraseDisplayView( - seedphrase: viewModel.generatedSeedphrase, - onConfirmed: { _ in - viewModel.dismissSeedphraseSheet() - } - ) - .environmentObject(themeManager) - } - .confirmationDialog( - "Generate a recovery seedphrase?", - isPresented: $viewModel.presentSeedphraseConfirmation, - titleVisibility: .visible - ) { - Button("Generate") { - Task { - await viewModel.executePendingSeedphraseAction() - } - } - Button("Cancel", role: .cancel) {} - } message: { - Text("A seedphrase lets you recover your account if you lose access. Store it safely.") - } - .confirmationDialog( - "Remove this sign-in method?", - isPresented: $viewModel.presentRemoveAuthConfirmation, - titleVisibility: .visible - ) { - Button("Remove", role: .destructive) { - Task { - await viewModel.executeRemoveAuth() - } - } - Button("Cancel", role: .cancel) {} - } message: { - if let authType = viewModel.authTypeToRemove { - Text("Are you sure you want to remove \(authType) as a sign-in method?") - } - } - .sheet(isPresented: $viewModel.presentAddAuthSheet) { - AddAuthSheet(api: api) - .environmentObject(themeManager) - .environmentObject(snackbarManager) - .environmentObject(connectWalletProviderViewModel) - } + + SettingsAuthModifiersView( + viewModel: viewModel, + api: api, + handleWalletDeepLink: handleWalletDeepLink + ) + .environmentObject(themeManager) + .environmentObject(snackbarManager) + .environmentObject(connectWalletProviderViewModel) + } #endif From 84ca166b3db13f4faca153b44ec11ef7477cdf90 Mon Sep 17 00:00:00 2001 From: ryanmello07 Date: Sat, 18 Jul 2026 00:19:54 +0100 Subject: [PATCH 083/109] fix: move ALL modifiers (task/alert/onChange/dialogs/sheets/onOpenURL) into SettingsAuthModifiersView, pass callbacks; keep SettingsView.body as trivial ZStack --- .../Settings/SettingsAuthModifiersView.swift | 82 ++++++++++- .../Main/Account/Settings/SettingsView.swift | 127 ++---------------- 2 files changed, 85 insertions(+), 124 deletions(-) diff --git a/app/network/Main/Account/Settings/SettingsAuthModifiersView.swift b/app/network/Main/Account/Settings/SettingsAuthModifiersView.swift index 7fce50e0..bfa15aea 100644 --- a/app/network/Main/Account/Settings/SettingsAuthModifiersView.swift +++ b/app/network/Main/Account/Settings/SettingsAuthModifiersView.swift @@ -2,19 +2,19 @@ // SettingsAuthModifiersView.swift // URnetwork // -// Created by Stuart Kuentzel on 2024/12/10. +// Created by Hermes on 2026/07/17. // import SwiftUI import URnetworkSdk /** - Separate view holding the seedphrase and sign-in-method sheet/dialog modifiers. - - This exists solely to keep the modifier chains short enough for Swift's - type-checker: SettingsView.body stays at 8 modifiers (its original length), - and the 4 auth-specific modifiers live here on a trivial Color.clear base, - each in its own independently-type-checked struct. + Aggregates all sheet/dialog/onOpenURL modifiers for SettingsView. + + This separate struct keeps each modifier chain short enough for Swift's + type-checker. SettingsView.body itself holds only a trivial ZStack with + two subviews (the form + this), while all 10+ modifiers live here on a + single Color.clear base that is independently type-checked. */ struct SettingsAuthModifiersView: View { @@ -23,12 +23,80 @@ struct SettingsAuthModifiersView: View { @EnvironmentObject var connectWalletProviderViewModel: ConnectWalletProviderViewModel @ObservedObject var viewModel: SettingsView.ViewModel + @ObservedObject var accountPreferencesViewModel: AccountPreferencesViewModel let api: UrApiServiceProtocol + let clientId: SdkId? let handleWalletDeepLink: (URL) -> Void + let onSaveDeviceName: () async -> Void + let onHandleDeleteResult: (Result) -> Void + var body: some View { Color.clear .frame(width: 0, height: 0) + .task { + await viewModel.fetchDeviceInfo(clientId) + } + .alert("Device name", isPresented: $viewModel.isPresentedRenameDevice) { + TextField("Device name", text: $viewModel.editingDeviceName) + Button("Save") { + Task { + await onSaveDeviceName() + } + } + Button("Cancel", role: .cancel) {} + } + .onChange(of: accountPreferencesViewModel.saveErrorMessage) { newValue in + if let newValue { + snackbarManager.showSnackbar(message: newValue) + accountPreferencesViewModel.clearSaveErrorMessage() + } + } + .confirmationDialog( + "Are you sure you want to delete your account?", + isPresented: $viewModel.isPresentedDeleteAccountConfirmation, + titleVisibility: .visible + ) { + Button("Delete account", role: .destructive) { + Task { + let result = await viewModel.deleteAccount() + onHandleDeleteResult(result) + } + } + } + .sheet(isPresented: $viewModel.presentSigninWithSolanaSheet) { + SolanaSignMessageSheet( + isSigningMessage: viewModel.isSigningMessage, + setIsSigningMessage: viewModel.setIsSigningMessage, + signButtonText: "Confirm Seeker Token", + signButtonLabelText: "Claim multiplier", + message: connectWalletProviderViewModel.claimSeekerTokenMessage, + dismiss: { + viewModel.presentSigninWithSolanaSheet = false + } + ) + .environmentObject(themeManager) + .environmentObject(connectWalletProviderViewModel) + .presentationDetents([.height(148)]) + } + .sheet(isPresented: $viewModel.presentUpdateReferralNetworkSheet) { + UpdateReferralNetworkSheet( + api: api, + onSuccess: { + Task { + await viewModel.fetchReferralNetwork() + } + viewModel.presentUpdateReferralNetworkSheet = false + }, + dismiss: { + viewModel.presentUpdateReferralNetworkSheet = false + }, + referralNetwork: viewModel.referralNetwork + ) + .environmentObject(themeManager) + .presentationDetents([.height(268)]) + .presentationDragIndicator(.visible) + } .sheet(isPresented: $viewModel.presentSeedphraseSheet) { SeedphraseDisplayView( seedphrase: viewModel.generatedSeedphrase, diff --git a/app/network/Main/Account/Settings/SettingsView.swift b/app/network/Main/Account/Settings/SettingsView.swift index d99925af..2cb05699 100644 --- a/app/network/Main/Account/Settings/SettingsView.swift +++ b/app/network/Main/Account/Settings/SettingsView.swift @@ -81,77 +81,15 @@ struct SettingsView: View { viewModel: viewModel, ) .background(themeManager.currentTheme.backgroundColor) - .task { - await viewModel.fetchDeviceInfo(clientId) - } - .alert("Device name", isPresented: $viewModel.isPresentedRenameDevice) { - TextField("Device name", text: $viewModel.editingDeviceName) - Button("Save") { - Task { - await saveDeviceName() - } - } - Button("Cancel", role: .cancel) {} - } - .onChange(of: accountPreferencesViewModel.saveErrorMessage) { newValue in - if let newValue { - snackbarManager.showSnackbar(message: newValue) - accountPreferencesViewModel.clearSaveErrorMessage() - } - } - .confirmationDialog( - "Are you sure you want to delete your account?", - isPresented: $viewModel.isPresentedDeleteAccountConfirmation, - titleVisibility: .visible - ) { - Button("Delete account", role: .destructive) { - - Task { - let result = await viewModel.deleteAccount() - self.handleResult(result) - } - - } - } - .sheet(isPresented: $viewModel.presentSigninWithSolanaSheet) { - - SolanaSignMessageSheet( - isSigningMessage: viewModel.isSigningMessage, - setIsSigningMessage: viewModel.setIsSigningMessage, - signButtonText: "Confirm Seeker Token", - signButtonLabelText: "Claim multiplier", - message: connectWalletProviderViewModel.claimSeekerTokenMessage, - dismiss: { - viewModel.presentSigninWithSolanaSheet = false - } - ) - .environmentObject(themeManager) - .environmentObject(connectWalletProviderViewModel) - .presentationDetents([.height(148)]) - } - .sheet(isPresented: $viewModel.presentUpdateReferralNetworkSheet) { - UpdateReferralNetworkSheet( - api: api, - onSuccess: { - Task { - await viewModel.fetchReferralNetwork() - } - viewModel.presentUpdateReferralNetworkSheet = false - }, - dismiss: { - viewModel.presentUpdateReferralNetworkSheet = false - }, - referralNetwork: viewModel.referralNetwork - ) - .environmentObject(themeManager) - .presentationDetents([.height(268)]) - .presentationDragIndicator(.visible) - } SettingsAuthModifiersView( viewModel: viewModel, api: api, - handleWalletDeepLink: handleWalletDeepLink + accountPreferencesViewModel: accountPreferencesViewModel, + clientId: clientId, + handleWalletDeepLink: handleWalletDeepLink, + onSaveDeviceName: { await self.saveDeviceName() }, + onHandleDeleteResult: { self.handleResult($0) } ) .environmentObject(themeManager) .environmentObject(snackbarManager) @@ -187,65 +125,20 @@ struct SettingsView: View { networkUserViewModel: networkUserViewModel, viewModel: viewModel ) - .task { - await viewModel.fetchDeviceInfo(clientId) - } - .alert("Device name", isPresented: $viewModel.isPresentedRenameDevice) { - TextField("Device name", text: $viewModel.editingDeviceName) - Button("Save") { - Task { - await saveDeviceName() - } - } - Button("Cancel", role: .cancel) {} - } - .onChange(of: accountPreferencesViewModel.saveErrorMessage) { newValue in - if let newValue { - snackbarManager.showSnackbar(message: newValue) - accountPreferencesViewModel.clearSaveErrorMessage() - } - } - .confirmationDialog( - "Are you sure you want to delete your account?", - isPresented: $viewModel.isPresentedDeleteAccountConfirmation, - titleVisibility: .visible - ) { - Button("Delete account", role: .destructive) { - - Task { - let result = await viewModel.deleteAccount() - self.handleResult(result) - } - - } - } - .sheet(isPresented: $viewModel.presentUpdateReferralNetworkSheet) { - UpdateReferralNetworkSheet( - api: api, - onSuccess: { - Task { - await viewModel.fetchReferralNetwork() - } - viewModel.presentUpdateReferralNetworkSheet = false - }, - dismiss: { - viewModel.presentUpdateReferralNetworkSheet = false - }, - referralNetwork: viewModel.referralNetwork - ) - .environmentObject(themeManager) - } SettingsAuthModifiersView( viewModel: viewModel, api: api, - handleWalletDeepLink: handleWalletDeepLink + accountPreferencesViewModel: accountPreferencesViewModel, + clientId: clientId, + handleWalletDeepLink: handleWalletDeepLink, + onSaveDeviceName: { await self.saveDeviceName() }, + onHandleDeleteResult: { self.handleResult($0) } ) .environmentObject(themeManager) .environmentObject(snackbarManager) .environmentObject(connectWalletProviderViewModel) } - #endif } From f30701c6d7edf9598a1d4b251451c7d7bb50435b Mon Sep 17 00:00:00 2001 From: ryanmello07 Date: Sat, 18 Jul 2026 00:27:32 +0100 Subject: [PATCH 084/109] fix: split modifiers into SettingsCoreModifiersView (6 modifiers) and SettingsAuthModifiersView (5 modifiers) + SettingsView ZStack (3 subviews) --- .../Settings/SettingsAuthModifiersView.swift | 76 +------------- .../Settings/SettingsCoreModifiersView.swift | 98 +++++++++++++++++++ .../Main/Account/Settings/SettingsView.swift | 28 ++++-- 3 files changed, 123 insertions(+), 79 deletions(-) create mode 100644 app/network/Main/Account/Settings/SettingsCoreModifiersView.swift diff --git a/app/network/Main/Account/Settings/SettingsAuthModifiersView.swift b/app/network/Main/Account/Settings/SettingsAuthModifiersView.swift index bfa15aea..515cd612 100644 --- a/app/network/Main/Account/Settings/SettingsAuthModifiersView.swift +++ b/app/network/Main/Account/Settings/SettingsAuthModifiersView.swift @@ -9,12 +9,10 @@ import SwiftUI import URnetworkSdk /** - Aggregates all sheet/dialog/onOpenURL modifiers for SettingsView. + Holds the seedphrase + sign-in method sheet/dialog/onOpenURL modifiers. - This separate struct keeps each modifier chain short enough for Swift's - type-checker. SettingsView.body itself holds only a trivial ZStack with - two subviews (the form + this), while all 10+ modifiers live here on a - single Color.clear base that is independently type-checked. + Split from SettingsCoreModifiersView to keep each modifier chain short + enough for Swift's type-checker. */ struct SettingsAuthModifiersView: View { @@ -23,80 +21,12 @@ struct SettingsAuthModifiersView: View { @EnvironmentObject var connectWalletProviderViewModel: ConnectWalletProviderViewModel @ObservedObject var viewModel: SettingsView.ViewModel - @ObservedObject var accountPreferencesViewModel: AccountPreferencesViewModel let api: UrApiServiceProtocol - let clientId: SdkId? let handleWalletDeepLink: (URL) -> Void - let onSaveDeviceName: () async -> Void - let onHandleDeleteResult: (Result) -> Void - var body: some View { Color.clear .frame(width: 0, height: 0) - .task { - await viewModel.fetchDeviceInfo(clientId) - } - .alert("Device name", isPresented: $viewModel.isPresentedRenameDevice) { - TextField("Device name", text: $viewModel.editingDeviceName) - Button("Save") { - Task { - await onSaveDeviceName() - } - } - Button("Cancel", role: .cancel) {} - } - .onChange(of: accountPreferencesViewModel.saveErrorMessage) { newValue in - if let newValue { - snackbarManager.showSnackbar(message: newValue) - accountPreferencesViewModel.clearSaveErrorMessage() - } - } - .confirmationDialog( - "Are you sure you want to delete your account?", - isPresented: $viewModel.isPresentedDeleteAccountConfirmation, - titleVisibility: .visible - ) { - Button("Delete account", role: .destructive) { - Task { - let result = await viewModel.deleteAccount() - onHandleDeleteResult(result) - } - } - } - .sheet(isPresented: $viewModel.presentSigninWithSolanaSheet) { - SolanaSignMessageSheet( - isSigningMessage: viewModel.isSigningMessage, - setIsSigningMessage: viewModel.setIsSigningMessage, - signButtonText: "Confirm Seeker Token", - signButtonLabelText: "Claim multiplier", - message: connectWalletProviderViewModel.claimSeekerTokenMessage, - dismiss: { - viewModel.presentSigninWithSolanaSheet = false - } - ) - .environmentObject(themeManager) - .environmentObject(connectWalletProviderViewModel) - .presentationDetents([.height(148)]) - } - .sheet(isPresented: $viewModel.presentUpdateReferralNetworkSheet) { - UpdateReferralNetworkSheet( - api: api, - onSuccess: { - Task { - await viewModel.fetchReferralNetwork() - } - viewModel.presentUpdateReferralNetworkSheet = false - }, - dismiss: { - viewModel.presentUpdateReferralNetworkSheet = false - }, - referralNetwork: viewModel.referralNetwork - ) - .environmentObject(themeManager) - .presentationDetents([.height(268)]) - .presentationDragIndicator(.visible) - } .sheet(isPresented: $viewModel.presentSeedphraseSheet) { SeedphraseDisplayView( seedphrase: viewModel.generatedSeedphrase, diff --git a/app/network/Main/Account/Settings/SettingsCoreModifiersView.swift b/app/network/Main/Account/Settings/SettingsCoreModifiersView.swift new file mode 100644 index 00000000..926c1aae --- /dev/null +++ b/app/network/Main/Account/Settings/SettingsCoreModifiersView.swift @@ -0,0 +1,98 @@ +// +// SettingsCoreModifiersView.swift +// URnetwork +// +// Created by Hermes on 2026/07/17. +// + +import SwiftUI +import URnetworkSdk + +/** + Holds the pre-existing SettingsView modifiers (task, alert, onChange, + delete confirmation dialog, Solana sign-in sheet, referral update sheet). + + Split from SettingsAuthModifiersView to keep each chain short enough + for Swift's type-checker. + */ +struct SettingsCoreModifiersView: View { + + @EnvironmentObject var themeManager: ThemeManager + @EnvironmentObject var snackbarManager: UrSnackbarManager + @EnvironmentObject var connectWalletProviderViewModel: ConnectWalletProviderViewModel + + @ObservedObject var viewModel: SettingsView.ViewModel + @ObservedObject var accountPreferencesViewModel: AccountPreferencesViewModel + let api: UrApiServiceProtocol + let clientId: SdkId? + let onSaveDeviceName: () async -> Void + let onHandleDeleteResult: (Result) -> Void + + var body: some View { + Color.clear + .frame(width: 0, height: 0) + .task { + await viewModel.fetchDeviceInfo(clientId) + } + .alert("Device name", isPresented: $viewModel.isPresentedRenameDevice) { + TextField("Device name", text: $viewModel.editingDeviceName) + Button("Save") { + Task { + await onSaveDeviceName() + } + } + Button("Cancel", role: .cancel) {} + } + .onChange(of: accountPreferencesViewModel.saveErrorMessage) { newValue in + if let newValue { + snackbarManager.showSnackbar(message: newValue) + accountPreferencesViewModel.clearSaveErrorMessage() + } + } + .confirmationDialog( + "Are you sure you want to delete your account?", + isPresented: $viewModel.isPresentedDeleteAccountConfirmation, + titleVisibility: .visible + ) { + Button("Delete account", role: .destructive) { + Task { + let result = await viewModel.deleteAccount() + onHandleDeleteResult(result) + } + } + } + .sheet(isPresented: $viewModel.presentSigninWithSolanaSheet) { + SolanaSignMessageSheet( + isSigningMessage: viewModel.isSigningMessage, + setIsSigningMessage: viewModel.setIsSigningMessage, + signButtonText: "Confirm Seeker Token", + signButtonLabelText: "Claim multiplier", + message: connectWalletProviderViewModel.claimSeekerTokenMessage, + dismiss: { + viewModel.presentSigninWithSolanaSheet = false + } + ) + .environmentObject(themeManager) + .environmentObject(connectWalletProviderViewModel) + .presentationDetents([.height(148)]) + } + .sheet(isPresented: $viewModel.presentUpdateReferralNetworkSheet) { + UpdateReferralNetworkSheet( + api: api, + onSuccess: { + Task { + await viewModel.fetchReferralNetwork() + } + viewModel.presentUpdateReferralNetworkSheet = false + }, + dismiss: { + viewModel.presentUpdateReferralNetworkSheet = false + }, + referralNetwork: viewModel.referralNetwork + ) + .environmentObject(themeManager) + .presentationDetents([.height(268)]) + .presentationDragIndicator(.visible) + } + } +} diff --git a/app/network/Main/Account/Settings/SettingsView.swift b/app/network/Main/Account/Settings/SettingsView.swift index 2cb05699..bfbc26c4 100644 --- a/app/network/Main/Account/Settings/SettingsView.swift +++ b/app/network/Main/Account/Settings/SettingsView.swift @@ -82,18 +82,26 @@ struct SettingsView: View { ) .background(themeManager.currentTheme.backgroundColor) - SettingsAuthModifiersView( + SettingsCoreModifiersView( viewModel: viewModel, - api: api, accountPreferencesViewModel: accountPreferencesViewModel, + api: api, clientId: clientId, - handleWalletDeepLink: handleWalletDeepLink, onSaveDeviceName: { await self.saveDeviceName() }, onHandleDeleteResult: { self.handleResult($0) } ) .environmentObject(themeManager) .environmentObject(snackbarManager) .environmentObject(connectWalletProviderViewModel) + + SettingsAuthModifiersView( + viewModel: viewModel, + api: api, + handleWalletDeepLink: handleWalletDeepLink + ) + .environmentObject(themeManager) + .environmentObject(snackbarManager) + .environmentObject(connectWalletProviderViewModel) } #elseif os(macOS) @@ -126,18 +134,26 @@ struct SettingsView: View { viewModel: viewModel ) - SettingsAuthModifiersView( + SettingsCoreModifiersView( viewModel: viewModel, - api: api, accountPreferencesViewModel: accountPreferencesViewModel, + api: api, clientId: clientId, - handleWalletDeepLink: handleWalletDeepLink, onSaveDeviceName: { await self.saveDeviceName() }, onHandleDeleteResult: { self.handleResult($0) } ) .environmentObject(themeManager) .environmentObject(snackbarManager) .environmentObject(connectWalletProviderViewModel) + + SettingsAuthModifiersView( + viewModel: viewModel, + api: api, + handleWalletDeepLink: handleWalletDeepLink + ) + .environmentObject(themeManager) + .environmentObject(snackbarManager) + .environmentObject(connectWalletProviderViewModel) } #endif From 674ccec032b28ffb212859cff5c19b6ebb8eda02 Mon Sep 17 00:00:00 2001 From: ryanmello07 Date: Sat, 18 Jul 2026 00:34:02 +0100 Subject: [PATCH 085/109] fix: use inline let bindings in body to give type-checker concrete type anchors at each step --- .../Main/Account/Settings/SettingsView.swift | 376 +++++++++++++----- 1 file changed, 278 insertions(+), 98 deletions(-) diff --git a/app/network/Main/Account/Settings/SettingsView.swift b/app/network/Main/Account/Settings/SettingsView.swift index bfbc26c4..8a237fce 100644 --- a/app/network/Main/Account/Settings/SettingsView.swift +++ b/app/network/Main/Account/Settings/SettingsView.swift @@ -53,107 +53,287 @@ struct SettingsView: View { var body: some View { #if os(iOS) - ZStack { - SettingsForm_iOS( - urApiService: api, - clientId: clientId, - referralCode: referralLinkViewModel.referralCode, - totalReferrals: referralLinkViewModel.totalReferrals, - referralNetworkName: viewModel.referralNetwork?.name, - version: viewModel.version, - isUpdatingAccountPreferences: accountPreferencesViewModel.isUpdatingAccountPreferences, - copyToPasteboard: copyToPasteboard, - presentUpdateReferralNetworkSheet: { - viewModel.presentUpdateReferralNetworkSheet = true - }, - presentDeleteAccountConfirmation: { - viewModel.isPresentedDeleteAccountConfirmation = true - }, - navigate: navigate, - provideEnabled: deviceManager.provideEnabled, - providePaused: deviceManager.providePaused, - deviceName: viewModel.deviceName, - deviceSpec: viewModel.deviceSpec, - presentRenameDevice: viewModel.presentRenameDevice, - canReceiveNotifications: $viewModel.canReceiveNotifications, - canReceiveProductUpdates: $accountPreferencesViewModel.canReceiveProductUpdates, - networkUserViewModel: networkUserViewModel, - viewModel: viewModel, - ) - .background(themeManager.currentTheme.backgroundColor) - - SettingsCoreModifiersView( - viewModel: viewModel, - accountPreferencesViewModel: accountPreferencesViewModel, - api: api, - clientId: clientId, - onSaveDeviceName: { await self.saveDeviceName() }, - onHandleDeleteResult: { self.handleResult($0) } - ) - .environmentObject(themeManager) - .environmentObject(snackbarManager) - .environmentObject(connectWalletProviderViewModel) - - SettingsAuthModifiersView( - viewModel: viewModel, - api: api, - handleWalletDeepLink: handleWalletDeepLink - ) - .environmentObject(themeManager) - .environmentObject(snackbarManager) - .environmentObject(connectWalletProviderViewModel) + let form = SettingsForm_iOS( + urApiService: api, + clientId: clientId, + referralCode: referralLinkViewModel.referralCode, + totalReferrals: referralLinkViewModel.totalReferrals, + referralNetworkName: viewModel.referralNetwork?.name, + version: viewModel.version, + isUpdatingAccountPreferences: accountPreferencesViewModel.isUpdatingAccountPreferences, + copyToPasteboard: copyToPasteboard, + presentUpdateReferralNetworkSheet: { + viewModel.presentUpdateReferralNetworkSheet = true + }, + presentDeleteAccountConfirmation: { + viewModel.isPresentedDeleteAccountConfirmation = true + }, + navigate: navigate, + provideEnabled: deviceManager.provideEnabled, + providePaused: deviceManager.providePaused, + deviceName: viewModel.deviceName, + deviceSpec: viewModel.deviceSpec, + presentRenameDevice: viewModel.presentRenameDevice, + canReceiveNotifications: $viewModel.canReceiveNotifications, + canReceiveProductUpdates: $accountPreferencesViewModel.canReceiveProductUpdates, + networkUserViewModel: networkUserViewModel, + viewModel: viewModel, + ) + .background(themeManager.currentTheme.backgroundColor) + + let coreMods = Color.clear + .frame(width: 0, height: 0) + .task { + await viewModel.fetchDeviceInfo(clientId) + } + .alert("Device name", isPresented: $viewModel.isPresentedRenameDevice) { + TextField("Device name", text: $viewModel.editingDeviceName) + Button("Save") { + Task { + await saveDeviceName() + } + } + Button("Cancel", role: .cancel) {} + } + .onChange(of: accountPreferencesViewModel.saveErrorMessage) { newValue in + if let newValue { + snackbarManager.showSnackbar(message: newValue) + accountPreferencesViewModel.clearSaveErrorMessage() + } + } + .confirmationDialog( + "Are you sure you want to delete your account?", + isPresented: $viewModel.isPresentedDeleteAccountConfirmation, + titleVisibility: .visible + ) { + Button("Delete account", role: .destructive) { + Task { + let result = await viewModel.deleteAccount() + self.handleResult(result) + } + } + } + .sheet(isPresented: $viewModel.presentSigninWithSolanaSheet) { + SolanaSignMessageSheet( + isSigningMessage: viewModel.isSigningMessage, + setIsSigningMessage: viewModel.setIsSigningMessage, + signButtonText: "Confirm Seeker Token", + signButtonLabelText: "Claim multiplier", + message: connectWalletProviderViewModel.claimSeekerTokenMessage, + dismiss: { + viewModel.presentSigninWithSolanaSheet = false + } + ) + .environmentObject(themeManager) + .environmentObject(connectWalletProviderViewModel) + .presentationDetents([.height(148)]) + } + .sheet(isPresented: $viewModel.presentUpdateReferralNetworkSheet) { + UpdateReferralNetworkSheet( + api: api, + onSuccess: { + Task { + await viewModel.fetchReferralNetwork() + } + viewModel.presentUpdateReferralNetworkSheet = false + }, + dismiss: { + viewModel.presentUpdateReferralNetworkSheet = false + }, + referralNetwork: viewModel.referralNetwork + ) + .environmentObject(themeManager) + .presentationDetents([.height(268)]) + .presentationDragIndicator(.visible) + } + + let authMods = Color.clear + .frame(width: 0, height: 0) + .sheet(isPresented: $viewModel.presentSeedphraseSheet) { + SeedphraseDisplayView( + seedphrase: viewModel.generatedSeedphrase, + onConfirmed: { _ in + viewModel.dismissSeedphraseSheet() + } + ) + .environmentObject(themeManager) + } + .confirmationDialog( + "Generate a recovery seedphrase?", + isPresented: $viewModel.presentSeedphraseConfirmation, + titleVisibility: .visible + ) { + Button("Generate") { + Task { + await viewModel.executePendingSeedphraseAction() + } + } + Button("Cancel", role: .cancel) {} + } message: { + Text("A seedphrase lets you recover your account if you lose access. Store it safely.") + } + .confirmationDialog( + "Remove this sign-in method?", + isPresented: $viewModel.presentRemoveAuthConfirmation, + titleVisibility: .visible + ) { + Button("Remove", role: .destructive) { + Task { + await viewModel.executeRemoveAuth() + } + } + Button("Cancel", role: .cancel) {} + } message: { + if let authType = viewModel.authTypeToRemove { + Text("Are you sure you want to remove \(authType) as a sign-in method?") + } + } + .sheet(isPresented: $viewModel.presentAddAuthSheet) { + AddAuthSheet(api: api) + .environmentObject(themeManager) + .environmentObject(snackbarManager) + .environmentObject(connectWalletProviderViewModel) + } + + return ZStack { + form + coreMods + authMods + } + .onOpenURL { url in + handleWalletDeepLink(url) } #elseif os(macOS) - ZStack { - SettingsForm_macOS( - urApiService: api, - clientId: clientId, - referralCode: referralLinkViewModel.referralCode, - totalReferrals: referralLinkViewModel.totalReferrals, - referralNetworkName: viewModel.referralNetwork?.name, - version: viewModel.version, - isUpdatingAccountPreferences: accountPreferencesViewModel.isUpdatingAccountPreferences, - copyToPasteboard: copyToPasteboard, - presentUpdateReferralNetworkSheet: { - viewModel.presentUpdateReferralNetworkSheet = true - }, - presentDeleteAccountConfirmation: { - viewModel.isPresentedDeleteAccountConfirmation = true - }, - navigate: navigate, - provideEnabled: deviceManager.provideEnabled, - providePaused: deviceManager.providePaused, - deviceName: viewModel.deviceName, - deviceSpec: viewModel.deviceSpec, - presentRenameDevice: viewModel.presentRenameDevice, - canReceiveNotifications: $viewModel.canReceiveNotifications, - canReceiveProductUpdates: $accountPreferencesViewModel.canReceiveProductUpdates, - launchAtStartupEnabled: $viewModel.launchAtStartupEnabled, - networkUserViewModel: networkUserViewModel, - viewModel: viewModel - ) - - SettingsCoreModifiersView( - viewModel: viewModel, - accountPreferencesViewModel: accountPreferencesViewModel, - api: api, - clientId: clientId, - onSaveDeviceName: { await self.saveDeviceName() }, - onHandleDeleteResult: { self.handleResult($0) } - ) - .environmentObject(themeManager) - .environmentObject(snackbarManager) - .environmentObject(connectWalletProviderViewModel) - - SettingsAuthModifiersView( - viewModel: viewModel, - api: api, - handleWalletDeepLink: handleWalletDeepLink - ) - .environmentObject(themeManager) - .environmentObject(snackbarManager) - .environmentObject(connectWalletProviderViewModel) + let macForm = SettingsForm_macOS( + urApiService: api, + clientId: clientId, + referralCode: referralLinkViewModel.referralCode, + totalReferrals: referralLinkViewModel.totalReferrals, + referralNetworkName: viewModel.referralNetwork?.name, + version: viewModel.version, + isUpdatingAccountPreferences: accountPreferencesViewModel.isUpdatingAccountPreferences, + copyToPasteboard: copyToPasteboard, + presentUpdateReferralNetworkSheet: { + viewModel.presentUpdateReferralNetworkSheet = true + }, + presentDeleteAccountConfirmation: { + viewModel.isPresentedDeleteAccountConfirmation = true + }, + navigate: navigate, + provideEnabled: deviceManager.provideEnabled, + providePaused: deviceManager.providePaused, + deviceName: viewModel.deviceName, + deviceSpec: viewModel.deviceSpec, + presentRenameDevice: viewModel.presentRenameDevice, + canReceiveNotifications: $viewModel.canReceiveNotifications, + canReceiveProductUpdates: $accountPreferencesViewModel.canReceiveProductUpdates, + launchAtStartupEnabled: $viewModel.launchAtStartupEnabled, + networkUserViewModel: networkUserViewModel, + viewModel: viewModel + ) + + let macCoreMods = Color.clear + .frame(width: 0, height: 0) + .task { + await viewModel.fetchDeviceInfo(clientId) + } + .alert("Device name", isPresented: $viewModel.isPresentedRenameDevice) { + TextField("Device name", text: $viewModel.editingDeviceName) + Button("Save") { + Task { + await saveDeviceName() + } + } + Button("Cancel", role: .cancel) {} + } + .onChange(of: accountPreferencesViewModel.saveErrorMessage) { newValue in + if let newValue { + snackbarManager.showSnackbar(message: newValue) + accountPreferencesViewModel.clearSaveErrorMessage() + } + } + .confirmationDialog( + "Are you sure you want to delete your account?", + isPresented: $viewModel.isPresentedDeleteAccountConfirmation, + titleVisibility: .visible + ) { + Button("Delete account", role: .destructive) { + Task { + let result = await viewModel.deleteAccount() + self.handleResult(result) + } + } + } + .sheet(isPresented: $viewModel.presentUpdateReferralNetworkSheet) { + UpdateReferralNetworkSheet( + api: api, + onSuccess: { + Task { + await viewModel.fetchReferralNetwork() + } + viewModel.presentUpdateReferralNetworkSheet = false + }, + dismiss: { + viewModel.presentUpdateReferralNetworkSheet = false + }, + referralNetwork: viewModel.referralNetwork + ) + .environmentObject(themeManager) + } + + let macAuthMods = Color.clear + .frame(width: 0, height: 0) + .sheet(isPresented: $viewModel.presentSeedphraseSheet) { + SeedphraseDisplayView( + seedphrase: viewModel.generatedSeedphrase, + onConfirmed: { _ in + viewModel.dismissSeedphraseSheet() + } + ) + .environmentObject(themeManager) + } + .confirmationDialog( + "Generate a recovery seedphrase?", + isPresented: $viewModel.presentSeedphraseConfirmation, + titleVisibility: .visible + ) { + Button("Generate") { + Task { + await viewModel.executePendingSeedphraseAction() + } + } + Button("Cancel", role: .cancel) {} + } message: { + Text("A seedphrase lets you recover your account if you lose access. Store it safely.") + } + .confirmationDialog( + "Remove this sign-in method?", + isPresented: $viewModel.presentRemoveAuthConfirmation, + titleVisibility: .visible + ) { + Button("Remove", role: .destructive) { + Task { + await viewModel.executeRemoveAuth() + } + } + Button("Cancel", role: .cancel) {} + } message: { + if let authType = viewModel.authTypeToRemove { + Text("Are you sure you want to remove \(authType) as a sign-in method?") + } + } + .sheet(isPresented: $viewModel.presentAddAuthSheet) { + AddAuthSheet(api: api) + .environmentObject(themeManager) + .environmentObject(snackbarManager) + .environmentObject(connectWalletProviderViewModel) + } + + return ZStack { + macForm + macCoreMods + macAuthMods } #endif From bdae449b06c60d73fbb997d3b90934142c3b5327 Mon Sep 17 00:00:00 2001 From: ryanmello07 Date: Sat, 18 Jul 2026 00:39:52 +0100 Subject: [PATCH 086/109] chore: remove unused SettingsAuthModifiersView and SettingsCoreModifiersView --- .../Settings/SettingsAuthModifiersView.swift | 79 --------------- .../Settings/SettingsCoreModifiersView.swift | 98 ------------------- 2 files changed, 177 deletions(-) delete mode 100644 app/network/Main/Account/Settings/SettingsAuthModifiersView.swift delete mode 100644 app/network/Main/Account/Settings/SettingsCoreModifiersView.swift diff --git a/app/network/Main/Account/Settings/SettingsAuthModifiersView.swift b/app/network/Main/Account/Settings/SettingsAuthModifiersView.swift deleted file mode 100644 index 515cd612..00000000 --- a/app/network/Main/Account/Settings/SettingsAuthModifiersView.swift +++ /dev/null @@ -1,79 +0,0 @@ -// -// SettingsAuthModifiersView.swift -// URnetwork -// -// Created by Hermes on 2026/07/17. -// - -import SwiftUI -import URnetworkSdk - -/** - Holds the seedphrase + sign-in method sheet/dialog/onOpenURL modifiers. - - Split from SettingsCoreModifiersView to keep each modifier chain short - enough for Swift's type-checker. - */ -struct SettingsAuthModifiersView: View { - - @EnvironmentObject var themeManager: ThemeManager - @EnvironmentObject var snackbarManager: UrSnackbarManager - @EnvironmentObject var connectWalletProviderViewModel: ConnectWalletProviderViewModel - - @ObservedObject var viewModel: SettingsView.ViewModel - let api: UrApiServiceProtocol - let handleWalletDeepLink: (URL) -> Void - - var body: some View { - Color.clear - .frame(width: 0, height: 0) - .sheet(isPresented: $viewModel.presentSeedphraseSheet) { - SeedphraseDisplayView( - seedphrase: viewModel.generatedSeedphrase, - onConfirmed: { _ in - viewModel.dismissSeedphraseSheet() - } - ) - .environmentObject(themeManager) - } - .confirmationDialog( - "Generate a recovery seedphrase?", - isPresented: $viewModel.presentSeedphraseConfirmation, - titleVisibility: .visible - ) { - Button("Generate") { - Task { - await viewModel.executePendingSeedphraseAction() - } - } - Button("Cancel", role: .cancel) {} - } message: { - Text("A seedphrase lets you recover your account if you lose access. Store it safely.") - } - .confirmationDialog( - "Remove this sign-in method?", - isPresented: $viewModel.presentRemoveAuthConfirmation, - titleVisibility: .visible - ) { - Button("Remove", role: .destructive) { - Task { - await viewModel.executeRemoveAuth() - } - } - Button("Cancel", role: .cancel) {} - } message: { - if let authType = viewModel.authTypeToRemove { - Text("Are you sure you want to remove \(authType) as a sign-in method?") - } - } - .sheet(isPresented: $viewModel.presentAddAuthSheet) { - AddAuthSheet(api: api) - .environmentObject(themeManager) - .environmentObject(snackbarManager) - .environmentObject(connectWalletProviderViewModel) - } - .onOpenURL { url in - handleWalletDeepLink(url) - } - } -} diff --git a/app/network/Main/Account/Settings/SettingsCoreModifiersView.swift b/app/network/Main/Account/Settings/SettingsCoreModifiersView.swift deleted file mode 100644 index 926c1aae..00000000 --- a/app/network/Main/Account/Settings/SettingsCoreModifiersView.swift +++ /dev/null @@ -1,98 +0,0 @@ -// -// SettingsCoreModifiersView.swift -// URnetwork -// -// Created by Hermes on 2026/07/17. -// - -import SwiftUI -import URnetworkSdk - -/** - Holds the pre-existing SettingsView modifiers (task, alert, onChange, - delete confirmation dialog, Solana sign-in sheet, referral update sheet). - - Split from SettingsAuthModifiersView to keep each chain short enough - for Swift's type-checker. - */ -struct SettingsCoreModifiersView: View { - - @EnvironmentObject var themeManager: ThemeManager - @EnvironmentObject var snackbarManager: UrSnackbarManager - @EnvironmentObject var connectWalletProviderViewModel: ConnectWalletProviderViewModel - - @ObservedObject var viewModel: SettingsView.ViewModel - @ObservedObject var accountPreferencesViewModel: AccountPreferencesViewModel - let api: UrApiServiceProtocol - let clientId: SdkId? - let onSaveDeviceName: () async -> Void - let onHandleDeleteResult: (Result) -> Void - - var body: some View { - Color.clear - .frame(width: 0, height: 0) - .task { - await viewModel.fetchDeviceInfo(clientId) - } - .alert("Device name", isPresented: $viewModel.isPresentedRenameDevice) { - TextField("Device name", text: $viewModel.editingDeviceName) - Button("Save") { - Task { - await onSaveDeviceName() - } - } - Button("Cancel", role: .cancel) {} - } - .onChange(of: accountPreferencesViewModel.saveErrorMessage) { newValue in - if let newValue { - snackbarManager.showSnackbar(message: newValue) - accountPreferencesViewModel.clearSaveErrorMessage() - } - } - .confirmationDialog( - "Are you sure you want to delete your account?", - isPresented: $viewModel.isPresentedDeleteAccountConfirmation, - titleVisibility: .visible - ) { - Button("Delete account", role: .destructive) { - Task { - let result = await viewModel.deleteAccount() - onHandleDeleteResult(result) - } - } - } - .sheet(isPresented: $viewModel.presentSigninWithSolanaSheet) { - SolanaSignMessageSheet( - isSigningMessage: viewModel.isSigningMessage, - setIsSigningMessage: viewModel.setIsSigningMessage, - signButtonText: "Confirm Seeker Token", - signButtonLabelText: "Claim multiplier", - message: connectWalletProviderViewModel.claimSeekerTokenMessage, - dismiss: { - viewModel.presentSigninWithSolanaSheet = false - } - ) - .environmentObject(themeManager) - .environmentObject(connectWalletProviderViewModel) - .presentationDetents([.height(148)]) - } - .sheet(isPresented: $viewModel.presentUpdateReferralNetworkSheet) { - UpdateReferralNetworkSheet( - api: api, - onSuccess: { - Task { - await viewModel.fetchReferralNetwork() - } - viewModel.presentUpdateReferralNetworkSheet = false - }, - dismiss: { - viewModel.presentUpdateReferralNetworkSheet = false - }, - referralNetwork: viewModel.referralNetwork - ) - .environmentObject(themeManager) - .presentationDetents([.height(268)]) - .presentationDragIndicator(.visible) - } - } -} From f7634205eabdabeb1e1b9b9b1f62a51b49b85473 Mon Sep 17 00:00:00 2001 From: ryanmello07 Date: Sat, 18 Jul 2026 00:50:41 +0100 Subject: [PATCH 087/109] fix: restore ViewModel properties dropped by upstream merge (presentSigninWithSolanaSheet, isSigningMessage, claimSeekerTokenMessage) --- .../Main/Account/Settings/SettingsView.swift | 142 ++++++++---------- .../Account/Settings/SettingsViewModel.swift | 8 + .../ConnectWalletProviderViewModel.swift | 1 + 3 files changed, 71 insertions(+), 80 deletions(-) diff --git a/app/network/Main/Account/Settings/SettingsView.swift b/app/network/Main/Account/Settings/SettingsView.swift index 8a237fce..81fabf83 100644 --- a/app/network/Main/Account/Settings/SettingsView.swift +++ b/app/network/Main/Account/Settings/SettingsView.swift @@ -53,36 +53,33 @@ struct SettingsView: View { var body: some View { #if os(iOS) - let form = SettingsForm_iOS( - urApiService: api, - clientId: clientId, - referralCode: referralLinkViewModel.referralCode, - totalReferrals: referralLinkViewModel.totalReferrals, - referralNetworkName: viewModel.referralNetwork?.name, - version: viewModel.version, - isUpdatingAccountPreferences: accountPreferencesViewModel.isUpdatingAccountPreferences, - copyToPasteboard: copyToPasteboard, - presentUpdateReferralNetworkSheet: { - viewModel.presentUpdateReferralNetworkSheet = true - }, - presentDeleteAccountConfirmation: { - viewModel.isPresentedDeleteAccountConfirmation = true - }, - navigate: navigate, - provideEnabled: deviceManager.provideEnabled, - providePaused: deviceManager.providePaused, - deviceName: viewModel.deviceName, - deviceSpec: viewModel.deviceSpec, - presentRenameDevice: viewModel.presentRenameDevice, - canReceiveNotifications: $viewModel.canReceiveNotifications, - canReceiveProductUpdates: $accountPreferencesViewModel.canReceiveProductUpdates, - networkUserViewModel: networkUserViewModel, - viewModel: viewModel, - ) - .background(themeManager.currentTheme.backgroundColor) - - let coreMods = Color.clear - .frame(width: 0, height: 0) + SettingsForm_iOS( + urApiService: api, + clientId: clientId, + referralCode: referralLinkViewModel.referralCode, + totalReferrals: referralLinkViewModel.totalReferrals, + referralNetworkName: viewModel.referralNetwork?.name, + version: viewModel.version, + isUpdatingAccountPreferences: accountPreferencesViewModel.isUpdatingAccountPreferences, + copyToPasteboard: copyToPasteboard, + presentUpdateReferralNetworkSheet: { + viewModel.presentUpdateReferralNetworkSheet = true + }, + presentDeleteAccountConfirmation: { + viewModel.isPresentedDeleteAccountConfirmation = true + }, + navigate: navigate, + provideEnabled: deviceManager.provideEnabled, + providePaused: deviceManager.providePaused, + deviceName: viewModel.deviceName, + deviceSpec: viewModel.deviceSpec, + presentRenameDevice: viewModel.presentRenameDevice, + canReceiveNotifications: $viewModel.canReceiveNotifications, + canReceiveProductUpdates: $accountPreferencesViewModel.canReceiveProductUpdates, + networkUserViewModel: networkUserViewModel, + viewModel: viewModel, + ) + .background(themeManager.currentTheme.backgroundColor) .task { await viewModel.fetchDeviceInfo(clientId) } @@ -107,13 +104,16 @@ struct SettingsView: View { titleVisibility: .visible ) { Button("Delete account", role: .destructive) { + Task { let result = await viewModel.deleteAccount() self.handleResult(result) } + } } .sheet(isPresented: $viewModel.presentSigninWithSolanaSheet) { + SolanaSignMessageSheet( isSigningMessage: viewModel.isSigningMessage, setIsSigningMessage: viewModel.setIsSigningMessage, @@ -128,6 +128,9 @@ struct SettingsView: View { .environmentObject(connectWalletProviderViewModel) .presentationDetents([.height(148)]) } + .onOpenURL { url in + handleWalletDeepLink(url) + } .sheet(isPresented: $viewModel.presentUpdateReferralNetworkSheet) { UpdateReferralNetworkSheet( api: api, @@ -146,9 +149,6 @@ struct SettingsView: View { .presentationDetents([.height(268)]) .presentationDragIndicator(.visible) } - - let authMods = Color.clear - .frame(width: 0, height: 0) .sheet(isPresented: $viewModel.presentSeedphraseSheet) { SeedphraseDisplayView( seedphrase: viewModel.generatedSeedphrase, @@ -195,46 +195,34 @@ struct SettingsView: View { .environmentObject(connectWalletProviderViewModel) } - return ZStack { - form - coreMods - authMods - } - .onOpenURL { url in - handleWalletDeepLink(url) - } - #elseif os(macOS) - let macForm = SettingsForm_macOS( - urApiService: api, - clientId: clientId, - referralCode: referralLinkViewModel.referralCode, - totalReferrals: referralLinkViewModel.totalReferrals, - referralNetworkName: viewModel.referralNetwork?.name, - version: viewModel.version, - isUpdatingAccountPreferences: accountPreferencesViewModel.isUpdatingAccountPreferences, - copyToPasteboard: copyToPasteboard, - presentUpdateReferralNetworkSheet: { - viewModel.presentUpdateReferralNetworkSheet = true - }, - presentDeleteAccountConfirmation: { - viewModel.isPresentedDeleteAccountConfirmation = true - }, - navigate: navigate, - provideEnabled: deviceManager.provideEnabled, - providePaused: deviceManager.providePaused, - deviceName: viewModel.deviceName, - deviceSpec: viewModel.deviceSpec, - presentRenameDevice: viewModel.presentRenameDevice, - canReceiveNotifications: $viewModel.canReceiveNotifications, - canReceiveProductUpdates: $accountPreferencesViewModel.canReceiveProductUpdates, - launchAtStartupEnabled: $viewModel.launchAtStartupEnabled, - networkUserViewModel: networkUserViewModel, - viewModel: viewModel - ) - - let macCoreMods = Color.clear - .frame(width: 0, height: 0) + SettingsForm_macOS( + urApiService: api, + clientId: clientId, + referralCode: referralLinkViewModel.referralCode, + totalReferrals: referralLinkViewModel.totalReferrals, + referralNetworkName: viewModel.referralNetwork?.name, + version: viewModel.version, + isUpdatingAccountPreferences: accountPreferencesViewModel.isUpdatingAccountPreferences, + copyToPasteboard: copyToPasteboard, + presentUpdateReferralNetworkSheet: { + viewModel.presentUpdateReferralNetworkSheet = true + }, + presentDeleteAccountConfirmation: { + viewModel.isPresentedDeleteAccountConfirmation = true + }, + navigate: navigate, + provideEnabled: deviceManager.provideEnabled, + providePaused: deviceManager.providePaused, + deviceName: viewModel.deviceName, + deviceSpec: viewModel.deviceSpec, + presentRenameDevice: viewModel.presentRenameDevice, + canReceiveNotifications: $viewModel.canReceiveNotifications, + canReceiveProductUpdates: $accountPreferencesViewModel.canReceiveProductUpdates, + launchAtStartupEnabled: $viewModel.launchAtStartupEnabled, + networkUserViewModel: networkUserViewModel, + viewModel: viewModel + ) .task { await viewModel.fetchDeviceInfo(clientId) } @@ -259,10 +247,12 @@ struct SettingsView: View { titleVisibility: .visible ) { Button("Delete account", role: .destructive) { + Task { let result = await viewModel.deleteAccount() self.handleResult(result) } + } } .sheet(isPresented: $viewModel.presentUpdateReferralNetworkSheet) { @@ -281,9 +271,6 @@ struct SettingsView: View { ) .environmentObject(themeManager) } - - let macAuthMods = Color.clear - .frame(width: 0, height: 0) .sheet(isPresented: $viewModel.presentSeedphraseSheet) { SeedphraseDisplayView( seedphrase: viewModel.generatedSeedphrase, @@ -330,11 +317,6 @@ struct SettingsView: View { .environmentObject(connectWalletProviderViewModel) } - return ZStack { - macForm - macCoreMods - macAuthMods - } #endif } diff --git a/app/network/Main/Account/Settings/SettingsViewModel.swift b/app/network/Main/Account/Settings/SettingsViewModel.swift index 41d14208..ea945e92 100644 --- a/app/network/Main/Account/Settings/SettingsViewModel.swift +++ b/app/network/Main/Account/Settings/SettingsViewModel.swift @@ -21,6 +21,14 @@ extension SettingsView { @Published var presentUpdateReferralNetworkSheet: Bool = false + // MARK: - Solana/Wallet Sign-In (restored from upstream merge) + @Published var presentSigninWithSolanaSheet: Bool = false + @Published private(set) var isSigningMessage: Bool = false + + func setIsSigningMessage(_ isSigning: Bool) -> Void { + isSigningMessage = isSigning + } + @Published var version: String = "" init(api: UrApiServiceProtocol) { diff --git a/app/network/Shared/ViewModels/ConnectWalletProviderViewModel.swift b/app/network/Shared/ViewModels/ConnectWalletProviderViewModel.swift index af751c7d..987ebbb4 100644 --- a/app/network/Shared/ViewModels/ConnectWalletProviderViewModel.swift +++ b/app/network/Shared/ViewModels/ConnectWalletProviderViewModel.swift @@ -28,6 +28,7 @@ class ConnectWalletProviderViewModel: ObservableObject { var connectedWalletProvider: ConnectedWalletProvider? = nil let welcomeMessage = "Welcome to URnetwork" + let claimSeekerTokenMessage = "Claim point multiplier by holding Seeker Pre-order or Saga Genesis token" /** * Solflare From 637163cc91c146d9d7c85f5326f0606d1609a202 Mon Sep 17 00:00:00 2001 From: ryanmello07 Date: Sat, 18 Jul 2026 05:43:15 +0100 Subject: [PATCH 088/109] refactor: use backend auth_types array instead of local hasSeedphraseLocally hack - Replace parseAuthMethods to read from networkUser.authTypes array - Remove @Published var hasSeedphraseLocally from SettingsViewModel - Remove error-substring detection for 'already exists' - Remove hasSeedphrase() helper, replace with authTypesContains() - Seedphrase section reads from authTypes to show generate vs regenerate - macOS form updated with same authTypes closure pattern --- .../Account/Settings/SettingsForm-iOS.swift | 97 ++++++---- .../Account/Settings/SettingsForm-macOS.swift | 19 +- .../Account/Settings/SettingsViewModel.swift | 16 +- migrate-auth-types-plan.md | 179 ++++++++++++++++++ 4 files changed, 256 insertions(+), 55 deletions(-) create mode 100644 migrate-auth-types-plan.md diff --git a/app/network/Main/Account/Settings/SettingsForm-iOS.swift b/app/network/Main/Account/Settings/SettingsForm-iOS.swift index cfc84c4c..f1a72a04 100644 --- a/app/network/Main/Account/Settings/SettingsForm-iOS.swift +++ b/app/network/Main/Account/Settings/SettingsForm-iOS.swift @@ -142,20 +142,37 @@ struct SettingsForm_iOS: View { Section("Seedphrase") { VStack(alignment: .leading, spacing: 8) { - if viewModel.hasSeedphraseLocally || hasSeedphrase(networkUserViewModel?.networkUser) { - Button(action: { - viewModel.confirmRegenerateSeedphrase() - }) { - HStack { - Text("Regenerate Seedphrase") - .font(themeManager.currentTheme.bodyFont) - Spacer() - if viewModel.isRegeneratingSeedphrase { - ProgressView() + if let networkUser = networkUserViewModel?.networkUser { + let hasSeedphrase = authTypesContains(networkUser.authTypes, "seedphrase") + if hasSeedphrase { + Button(action: { + viewModel.confirmRegenerateSeedphrase() + }) { + HStack { + Text("Regenerate Seedphrase") + .font(themeManager.currentTheme.bodyFont) + Spacer() + if viewModel.isRegeneratingSeedphrase { + ProgressView() + } + } + } + .disabled(viewModel.isRegeneratingSeedphrase) + } else { + Button(action: { + viewModel.confirmGenerateSeedphrase() + }) { + HStack { + Text("Generate Seedphrase") + .font(themeManager.currentTheme.bodyFont) + Spacer() + if viewModel.isGeneratingSeedphrase { + ProgressView() + } } } + .disabled(viewModel.isGeneratingSeedphrase) } - .disabled(viewModel.isRegeneratingSeedphrase) } else { Button(action: { viewModel.confirmGenerateSeedphrase() @@ -182,19 +199,17 @@ struct SettingsForm_iOS: View { Section("Sign-In Methods") { if let networkUser = networkUserViewModel?.networkUser { - let authMethods = parseAuthMethods(networkUser, hasSeedphraseLocally: viewModel.hasSeedphraseLocally) + let authMethods = parseAuthMethods(networkUser) ForEach(authMethods, id: \.self) { method in HStack { Text(methodDisplayName(method)) .font(themeManager.currentTheme.bodyFont) Spacer() - if method != "seedphrase" || viewModel.hasSeedphraseLocally { - Button(role: .destructive) { - viewModel.presentRemoveAuth(method) - } label: { - Text("Remove") - } + Button(role: .destructive) { + viewModel.presentRemoveAuth(method) + } label: { + Text("Remove") } } } @@ -432,29 +447,37 @@ struct SettingsForm_iOS: View { // MARK: - Helpers - private func hasSeedphrase(_ networkUser: SdkNetworkUser?) -> Bool { - guard let networkUser = networkUser else { return false } - return networkUser.authType == "seedphrase" + private func authTypesContains(_ authTypes: SdkStringList, _ method: String) -> Bool { + for i in 0.. [String] { + private func parseAuthMethods(_ networkUser: SdkNetworkUser) -> [String] { var methods: [String] = [] - let authType = networkUser.authType - if !authType.isEmpty { methods.append(authType) } - // The SDK SdkNetworkUser currently only exposes a single authType. - // Future expansion could read from an array returned by the endpoint. - - // If the user has a seedphrase (detected locally via generate/regenerate), include it - if hasSeedphraseLocally && !methods.contains("seedphrase") { - methods.append("seedphrase") - } - // If there's a userAuth, include it as a method label - let userAuth = networkUser.userAuth - if !userAuth.isEmpty { - let methodLabel = userAuth.contains("@") ? "email" : userAuth - if !methods.contains(methodLabel) { - methods.append(methodLabel) + // Read from the new auth_types array returned by GET /network/user + let authTypes = networkUser.authTypes + if authTypes.len() > 0 { + for i in 0.. [String] { + var methods: [String] = [] + + // Use the new auth_types array from the backend + let authTypes = networkUser.authTypes + if authTypes.len() > 0 { + for i in 0.. Date: Sat, 18 Jul 2026 05:52:16 +0100 Subject: [PATCH 089/109] feat: refresh auth_types from server after add/remove/seedphrase operations - Added networkUserViewModel reference to ViewModel - Refresh NetworkUser data after generate/regenerate/removeAuth - Pass networkUserViewModel through to ViewModel init --- .../Main/Account/Settings/SettingsView.swift | 2 +- .../Account/Settings/SettingsViewModel.swift | 18 ++++++++++++++++-- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/app/network/Main/Account/Settings/SettingsView.swift b/app/network/Main/Account/Settings/SettingsView.swift index 81fabf83..17a5cc27 100644 --- a/app/network/Main/Account/Settings/SettingsView.swift +++ b/app/network/Main/Account/Settings/SettingsView.swift @@ -39,7 +39,7 @@ struct SettingsView: View { providerCountries: [SdkConnectLocation], networkUserViewModel: NetworkUserViewModel? = nil ) { - _viewModel = StateObject(wrappedValue: ViewModel(api: api)) + _viewModel = StateObject(wrappedValue: ViewModel(api: api, networkUserViewModel: networkUserViewModel)) self.clientId = clientId self.accountPreferencesViewModel = accountPreferencesViewModel self.referralLinkViewModel = referralLinkViewModel diff --git a/app/network/Main/Account/Settings/SettingsViewModel.swift b/app/network/Main/Account/Settings/SettingsViewModel.swift index 99503047..15601fe4 100644 --- a/app/network/Main/Account/Settings/SettingsViewModel.swift +++ b/app/network/Main/Account/Settings/SettingsViewModel.swift @@ -31,8 +31,11 @@ extension SettingsView { @Published var version: String = "" - init(api: UrApiServiceProtocol) { + var networkUserViewModel: NetworkUserViewModel? + + init(api: UrApiServiceProtocol, networkUserViewModel: NetworkUserViewModel? = nil) { self.api = api + self.networkUserViewModel = networkUserViewModel #if os(macOS) self.launchAtStartupEnabled = SMAppService.mainApp.status == .enabled @@ -321,6 +324,10 @@ extension SettingsView { self.presentSeedphraseConfirmation = false self.seedphraseError = error.localizedDescription } + // Refresh user data to get updated auth_types + Task { [weak self] in + _ = await self?.networkUserViewModel?.refreshNetworkUser() + } } func executeRegenerateSeedphrase() async { @@ -333,12 +340,15 @@ extension SettingsView { self.isRegeneratingSeedphrase = false self.presentSeedphraseConfirmation = false self.presentSeedphraseSheet = true - self.hasSeedphraseLocally = true } catch(let error) { self.isRegeneratingSeedphrase = false self.presentSeedphraseConfirmation = false self.seedphraseError = error.localizedDescription } + // Refresh user data to get updated auth_types + Task { [weak self] in + _ = await self?.networkUserViewModel?.refreshNetworkUser() + } } func dismissSeedphraseSheet() { @@ -375,6 +385,10 @@ extension SettingsView { self.isRemovingAuth = false self.removeAuthError = error.localizedDescription } + // Refresh user data to get updated auth_types + Task { [weak self] in + _ = await self?.networkUserViewModel?.refreshNetworkUser() + } } } From 9d231236c2aef9680ffac9acb28b59e503b7b9a9 Mon Sep 17 00:00:00 2001 From: ryanmello07 Date: Sat, 18 Jul 2026 06:09:32 +0100 Subject: [PATCH 090/109] ci: trigger rebuild with updated SDK AuthTypes support From 852e9d63536739d4304e03a34420a9edd70e086c Mon Sep 17 00:00:00 2001 From: ryanmello07 Date: Sat, 18 Jul 2026 06:23:48 +0100 Subject: [PATCH 091/109] fix: unwrap optional SdkStringList for networkUser.authTypes The SDK's new AuthTypes field is a Go pointer (*StringList), which gomobile bridges to Swift as SdkStringList?. Update the seedphrase detection and auth-methods parsing helpers to safely unwrap it instead of assuming a non-optional value, matching the pattern already used in DnsSettingsStore/BlockActionsStore. --- .../Main/Account/Settings/SettingsForm-iOS.swift | 10 +++++----- .../Main/Account/Settings/SettingsForm-macOS.swift | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/network/Main/Account/Settings/SettingsForm-iOS.swift b/app/network/Main/Account/Settings/SettingsForm-iOS.swift index f1a72a04..86c74b80 100644 --- a/app/network/Main/Account/Settings/SettingsForm-iOS.swift +++ b/app/network/Main/Account/Settings/SettingsForm-iOS.swift @@ -447,7 +447,8 @@ struct SettingsForm_iOS: View { // MARK: - Helpers - private func authTypesContains(_ authTypes: SdkStringList, _ method: String) -> Bool { + private func authTypesContains(_ authTypes: SdkStringList?, _ method: String) -> Bool { + guard let authTypes else { return false } for i in 0.. [String] { var methods: [String] = [] - + // Read from the new auth_types array returned by GET /network/user - let authTypes = networkUser.authTypes - if authTypes.len() > 0 { + if let authTypes = networkUser.authTypes, authTypes.len() > 0 { for i in 0.. Date: Sat, 18 Jul 2026 07:27:37 +0100 Subject: [PATCH 092/109] chore: remove scratch planning doc from repo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Not meant to ship — was accidentally committed alongside the auth_types refactor. --- migrate-auth-types-plan.md | 179 ------------------------------------- 1 file changed, 179 deletions(-) delete mode 100644 migrate-auth-types-plan.md diff --git a/migrate-auth-types-plan.md b/migrate-auth-types-plan.md deleted file mode 100644 index 7d77f716..00000000 --- a/migrate-auth-types-plan.md +++ /dev/null @@ -1,179 +0,0 @@ -# Plan: Migrate Auth Method Display to Backend `auth_types` Array - -## Overview - -Replace the fragile local `hasSeedphraseLocally` heuristic with the new -`auth_types` array returned by `GET /network/user`. This eliminates the -server-error-substring parsing, the local state hack, and incorrect -method listing on accounts with wallet + seedphrase. - -## SDK Changes (`beta/custom-server` on `Ryanmello07/urnetwork-sdk`) - -### 1. `network_user_view_controller.go` — Extend `NetworkUser` struct - -```go -type NetworkUser struct { - UserId *Id `json:"userId"` - UserName string `json:"user_name"` - UserAuth string `json:"user_auth,omitempty"` - Verified bool `json:"verified"` - AuthType string `json:"auth_type"` - NetworkName string `json:"network_name"` - WalletAddress string `json:"wallet_address,omitempty"` - - // NEW: multi-auth fields - AuthTypes []string `json:"auth_types,omitempty"` - WalletAuths []WalletAuth `json:"wallet_auths,omitempty"` - SeedphraseAuths []SeedphraseAuth `json:"seedphrase_auths,omitempty"` -} - -type WalletAuth struct { - WalletAddress string `json:"wallet_address"` - Blockchain string `json:"blockchain"` -} - -type SeedphraseAuth struct { - CreateTime string `json:"create_time"` -} -``` - -**gomobile note:** Go slices (`[]string`,`[]WalletAuth`,`[]SeedphraseAuth`) -export to Swift as, respectively, `func (self SdkNetworkUser) AuthTypes() -*SdkStringList`, `func (self SdkNetworkUser) WalletAuths() -*SdkWalletAuthList`, etc. These already exist in the generated bindings. - -`WalletAuth` / `SeedphraseAuth` must be exported types. If gomobile -refuses to export them, fall back to a single `AuthTypes []string` only -and keep the existing `authType` for single-auth read. - -### 2. `api.go` — No change needed - -`GetNetworkUserResult` already deserialises the full JSON response. -New `auth_types` etc. will be deserialised into `NetworkUser` as long as -JSON tags match. - -## iOS Changes (`feat/seedphrase-auth-ios` on `Ryanmello07/urnetwork-ios`) - -### 1. `SettingsForm-iOS.swift` — Replace `parseAuthMethods` - -**Current:** reads `networkUser.authType` (single string), appends -seedphrase via `hasSeedphraseLocally` local state hack. - -**New:** - -```swift -private func parseAuthMethods(_ networkUser: SdkNetworkUser) -> [String] { - var methods: [String] = [] - - // Use the new auth_types array from the backend - let authTypes = networkUser.authTypes - if authTypes.len() > 0 { - for i in 0.. Date: Sat, 18 Jul 2026 19:52:54 +0100 Subject: [PATCH 093/109] fix: show seedphrase after instant account creation on macOS The seedphrase-display/success-handling flow was wrapped in #if os(iOS) only, with no macOS branch. macOS users creating an instant account got no seedphrase display and no success handling at all -- the account was created server-side but the app just sat on the create screen. Use .sheet(item:) + .interactiveDismissDisabled(true) on macOS, since .fullScreenCover isn't available there. --- .../CreateNetworkInstantView.swift | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/app/network/Authenticate/CreateNetworkInstant/CreateNetworkInstantView.swift b/app/network/Authenticate/CreateNetworkInstant/CreateNetworkInstantView.swift index cd10d73a..840ceeb2 100644 --- a/app/network/Authenticate/CreateNetworkInstant/CreateNetworkInstantView.swift +++ b/app/network/Authenticate/CreateNetworkInstant/CreateNetworkInstantView.swift @@ -113,6 +113,20 @@ struct CreateNetworkInstantView: View { ) .environmentObject(themeManager) } + #elseif os(macOS) + .sheet(item: $accountResult) { result in + SeedphraseDisplayView( + seedphrase: result.seedphrase, + onConfirmed: { _ in + accountResult = nil + Task { + await handleSuccess(result.jwt) + } + } + ) + .environmentObject(themeManager) + .interactiveDismissDisabled(true) + } #endif } From 144eb7ba4f9a62fe53ff5d817118590ee0917e92 Mon Sep 17 00:00:00 2001 From: ryanmello07 Date: Sat, 18 Jul 2026 19:53:13 +0100 Subject: [PATCH 094/109] fix: AddAuthSheet stale UI, uncancelled wallet poll, dead code Three issues in the same file: - None of AddAuthSheet's four success paths (wallet, Apple, Google, generic addAuth) called refreshNetworkUser() after linking a new auth method, so Settings kept showing the old method list until an unrelated refresh happened. Now takes a NetworkUserViewModel and refreshes after each successful add. - pollForWalletConnection() polled for up to 60s with no cancellation wired to sheet dismissal, and the try? around Task.sleep silently swallowed cancellation errors even if something did cancel it. Now stores the connect Task, cancels it (and clears the pending wallet signature handler) in onDisappear, and the poll loop checks Task.isCancelled explicitly instead of relying on the swallowed throw. - Removed handleWalletAuth(), a function with zero call sites anywhere in the repo -- superseded by the connectWallet/signWalletChallenge flow. --- .../Main/Account/Settings/AddAuthSheet.swift | 35 +++++++++---------- .../Main/Account/Settings/SettingsView.swift | 4 +-- 2 files changed, 19 insertions(+), 20 deletions(-) diff --git a/app/network/Main/Account/Settings/AddAuthSheet.swift b/app/network/Main/Account/Settings/AddAuthSheet.swift index 5cadb5ae..b7225b40 100644 --- a/app/network/Main/Account/Settings/AddAuthSheet.swift +++ b/app/network/Main/Account/Settings/AddAuthSheet.swift @@ -15,12 +15,14 @@ struct AddAuthSheet: View { @EnvironmentObject var connectWalletProviderViewModel: ConnectWalletProviderViewModel let api: UrApiServiceProtocol - + let networkUserViewModel: NetworkUserViewModel? + @State private var email: String = "" @State private var password: String = "" @State private var isAdding: Bool = false @State private var selectedMethod: String = "email" @State private var addError: String? + @State private var walletConnectionTask: Task? var body: some View { NavigationStack { @@ -91,6 +93,12 @@ struct AddAuthSheet: View { } } } + .onDisappear { + walletConnectionTask?.cancel() + walletConnectionTask = nil + connectWalletProviderViewModel.pendingAddAuthSignatureHandler = nil + connectWalletProviderViewModel.pendingWalletAuthMessage = nil + } } } @@ -176,7 +184,7 @@ struct AddAuthSheet: View { case .disconnected: HStack(spacing: 12) { Button(action: { - Task { + walletConnectionTask = Task { await connectWallet(.phantom) } }) { @@ -197,7 +205,7 @@ struct AddAuthSheet: View { .disabled(isAdding || walletStep == .connecting) Button(action: { - Task { + walletConnectionTask = Task { await connectWallet(.solflare) } }) { @@ -319,12 +327,14 @@ struct AddAuthSheet: View { private func pollForWalletConnection() async { // Poll for up to 60 seconds for the wallet to connect back for _ in 0..<60 { + if Task.isCancelled { return } if let pk = connectWalletProviderViewModel.connectedPublicKey { walletStep = .connected(pk) isAdding = false return } try? await Task.sleep(nanoseconds: 1_000_000_000) // 1 second + if Task.isCancelled { return } } addError = "Wallet connection timed out. Please try again." isAdding = false @@ -379,6 +389,7 @@ struct AddAuthSheet: View { walletStep = .disconnected connectWalletProviderViewModel.pendingAddAuthSignatureHandler = nil connectWalletProviderViewModel.pendingWalletAuthMessage = nil + _ = await networkUserViewModel?.refreshNetworkUser() snackbarManager.showSnackbar(message: String(localized: "Wallet sign-in method added")) dismiss() } catch(let error) { @@ -436,6 +447,7 @@ struct AddAuthSheet: View { let _ = try await api.addAuth(args) isAdding = false + _ = await networkUserViewModel?.refreshNetworkUser() snackbarManager.showSnackbar(message: String(localized: "Apple sign-in method added")) dismiss() } catch(let error) { @@ -480,6 +492,7 @@ struct AddAuthSheet: View { let _ = try await api.addAuth(args) isAdding = false + _ = await networkUserViewModel?.refreshNetworkUser() snackbarManager.showSnackbar(message: String(localized: "Google sign-in method added")) dismiss() } catch(let error) { @@ -488,21 +501,6 @@ struct AddAuthSheet: View { } } - private func handleWalletAuth() async { - isAdding = true - addError = nil - - #if os(iOS) - // For wallet auth linking, the user needs to sign a challenge message - // The existing flow in LoginInitialView uses deep links with phantom/solflare - // For now, guide users to use the main wallet connection flow - addError = "Please connect your wallet from the main authentication flow, then add it as a sign-in method here." - #else - addError = "Wallet sign-in is available on iOS." - #endif - isAdding = false - } - private func addAuth() async { isAdding = true addError = nil @@ -524,6 +522,7 @@ struct AddAuthSheet: View { let _ = try await api.addAuth(args) isAdding = false + _ = await networkUserViewModel?.refreshNetworkUser() snackbarManager.showSnackbar(message: String(localized: "Sign-in method added successfully")) dismiss() } catch(let error) { diff --git a/app/network/Main/Account/Settings/SettingsView.swift b/app/network/Main/Account/Settings/SettingsView.swift index 17a5cc27..b7c5a2d5 100644 --- a/app/network/Main/Account/Settings/SettingsView.swift +++ b/app/network/Main/Account/Settings/SettingsView.swift @@ -189,7 +189,7 @@ struct SettingsView: View { } } .sheet(isPresented: $viewModel.presentAddAuthSheet) { - AddAuthSheet(api: api) + AddAuthSheet(api: api, networkUserViewModel: networkUserViewModel) .environmentObject(themeManager) .environmentObject(snackbarManager) .environmentObject(connectWalletProviderViewModel) @@ -311,7 +311,7 @@ struct SettingsView: View { } } .sheet(isPresented: $viewModel.presentAddAuthSheet) { - AddAuthSheet(api: api) + AddAuthSheet(api: api, networkUserViewModel: networkUserViewModel) .environmentObject(themeManager) .environmentObject(snackbarManager) .environmentObject(connectWalletProviderViewModel) From 567ae48f7000cec58ef39a5420df707edd263e36 Mon Sep 17 00:00:00 2001 From: ryanmello07 Date: Sat, 18 Jul 2026 19:53:30 +0100 Subject: [PATCH 095/109] fix: macOS Sign-In Methods can't list or remove multiple auth methods macOS's Sign-In Methods section (net-new in this PR) only ever rendered the single legacy authType/userAuth fields and had no per-method Remove button -- the confirmation dialog for it was already wired in SettingsView.swift but nothing on macOS ever triggered it. A user with Google+Apple+Seedphrase all linked saw only one method on macOS and couldn't remove any of them there, while iOS correctly listed and could remove each one. Extracted authTypesContains()/parseAuthMethods()/methodDisplayName() (previously private to SettingsForm-iOS, and partly reimplemented inline on macOS) into a shared AuthMethods.swift, and rebuilt macOS's Sign-In Methods section to ForEach over authTypes with a Remove button, matching iOS. --- .../Main/Account/Settings/AuthMethods.swift | 57 +++++++++++++++++++ .../Account/Settings/SettingsForm-iOS.swift | 49 ---------------- .../Account/Settings/SettingsForm-macOS.swift | 48 +++++----------- 3 files changed, 70 insertions(+), 84 deletions(-) create mode 100644 app/network/Main/Account/Settings/AuthMethods.swift diff --git a/app/network/Main/Account/Settings/AuthMethods.swift b/app/network/Main/Account/Settings/AuthMethods.swift new file mode 100644 index 00000000..18a7f0e0 --- /dev/null +++ b/app/network/Main/Account/Settings/AuthMethods.swift @@ -0,0 +1,57 @@ +// +// AuthMethods.swift +// URnetwork +// + +import URnetworkSdk + +// Shared by SettingsForm-iOS.swift and SettingsForm-macOS.swift so both +// platforms read auth-method state identically. + +func authTypesContains(_ authTypes: SdkStringList?, _ method: String) -> Bool { + guard let authTypes else { return false } + for i in 0.. [String] { + var methods: [String] = [] + + // Read from the new auth_types array returned by GET /network/user + if let authTypes = networkUser.authTypes, authTypes.len() > 0 { + for i in 0.. String { + switch method { + case "email": return "Email" + case "google": return "Google" + case "apple": return "Apple" + case "solana": return "Solana Wallet" + case "seedphrase": return "Seedphrase" + default: return method.capitalized + } +} diff --git a/app/network/Main/Account/Settings/SettingsForm-iOS.swift b/app/network/Main/Account/Settings/SettingsForm-iOS.swift index 86c74b80..f09f1c4e 100644 --- a/app/network/Main/Account/Settings/SettingsForm-iOS.swift +++ b/app/network/Main/Account/Settings/SettingsForm-iOS.swift @@ -445,55 +445,6 @@ struct SettingsForm_iOS: View { .background(themeManager.currentTheme.backgroundColor) } - // MARK: - Helpers - - private func authTypesContains(_ authTypes: SdkStringList?, _ method: String) -> Bool { - guard let authTypes else { return false } - for i in 0.. [String] { - var methods: [String] = [] - - // Read from the new auth_types array returned by GET /network/user - if let authTypes = networkUser.authTypes, authTypes.len() > 0 { - for i in 0.. String { - switch method { - case "email": return "Email" - case "google": return "Google" - case "apple": return "Apple" - case "solana": return "Solana Wallet" - case "seedphrase": return "Seedphrase" - default: return method.capitalized - } - } } #endif diff --git a/app/network/Main/Account/Settings/SettingsForm-macOS.swift b/app/network/Main/Account/Settings/SettingsForm-macOS.swift index fc4e7073..efd77f85 100644 --- a/app/network/Main/Account/Settings/SettingsForm-macOS.swift +++ b/app/network/Main/Account/Settings/SettingsForm-macOS.swift @@ -183,13 +183,7 @@ struct SettingsForm_macOS: View { VStack(alignment: .leading) { if let networkUser = networkUserViewModel?.networkUser { - let hasSeedphrase: Bool = { - guard let authTypes = networkUser.authTypes else { return false } - for i in 0.. String { - switch method { - case "email": return "Email" - case "google": return "Google" - case "apple": return "Apple" - case "solana": return "Solana Wallet" - case "seedphrase": return "Seedphrase" - default: return method.capitalized - } - } } #endif From 8cb76da8707d520813fbae1ae42bc8eddaacc833 Mon Sep 17 00:00:00 2001 From: ryanmello07 Date: Sat, 18 Jul 2026 19:53:50 +0100 Subject: [PATCH 096/109] fix: Profile Save uses live auth_types, not the frozen legacy authType ProfileView branched on the legacy singular authType field to choose claimNetworkName() vs saveNetworkName(). AddAuth never updates that field server-side (only RemoveAuth's COALESCE does), so an account created as seedphrase-only keeps authType == "seedphrase" forever, even after the user binds a real email/Google/Apple identity -- Profile would keep calling the first-time claim flow indefinitely instead of switching to the normal rename flow once a verified identity exists. Replaced the authType: String? param with needsNameClaim: Bool, computed from the live authTypes array as "no email/google/apple bound yet", matching the server's own requireEmailOrSsoBound gate that both claim-name and change-name are subject to. --- .../Main/Account/AccountNavStackView.swift | 8 +++++++- .../Main/Account/Profile/ProfileView.swift | 16 ++++++++++------ 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/app/network/Main/Account/AccountNavStackView.swift b/app/network/Main/Account/AccountNavStackView.swift index 7c24e142..4f5a4c41 100644 --- a/app/network/Main/Account/AccountNavStackView.swift +++ b/app/network/Main/Account/AccountNavStackView.swift @@ -106,7 +106,13 @@ struct AccountNavStackView: View { back: viewModel.back, networkName: networkName, userAuth: networkUserViewModel.networkUser?.userAuth, - authType: networkUserViewModel.networkUser?.authType + needsNameClaim: { + guard let networkUser = networkUserViewModel.networkUser else { return false } + let hasIdentityMethod = authTypesContains(networkUser.authTypes, "email") + || authTypesContains(networkUser.authTypes, "google") + || authTypesContains(networkUser.authTypes, "apple") + return !hasIdentityMethod + }() ) .background(themeManager.currentTheme.backgroundColor) .navigationTitle("Profile") diff --git a/app/network/Main/Account/Profile/ProfileView.swift b/app/network/Main/Account/Profile/ProfileView.swift index 87246798..538ed7e3 100644 --- a/app/network/Main/Account/Profile/ProfileView.swift +++ b/app/network/Main/Account/Profile/ProfileView.swift @@ -18,16 +18,20 @@ struct ProfileView: View { var back: () -> Void var networkName: String? var userAuth: String? - var authType: String? - - init(api: SdkApi, back: @escaping () -> Void, networkName: String?, userAuth: String?, authType: String? = nil) { + /// True when the account has no verified identity method (email/Google/Apple) + /// bound yet, so it's still on its auto-generated name and needs the + /// claim-name flow (no reclaim cooldown on the old name) rather than + /// change-name (which applies a 24h cooldown to protect the old name). + var needsNameClaim: Bool + + init(api: SdkApi, back: @escaping () -> Void, networkName: String?, userAuth: String?, needsNameClaim: Bool = false) { _viewModel = StateObject.init(wrappedValue: ViewModel( api: api )) self.back = back self.userAuth = userAuth self.networkName = networkName - self.authType = authType + self.needsNameClaim = needsNameClaim } var body: some View { @@ -67,7 +71,7 @@ struct ProfileView: View { text: "Save", action: { Task { - if authType == "seedphrase" { + if needsNameClaim { let result = await viewModel.claimNetworkName() handleNetworkNameResult(result) } else { @@ -104,7 +108,7 @@ struct ProfileView: View { } HStack { - if authType == "seedphrase" { + if needsNameClaim { Text("Claim a custom network name to replace your auto-generated one") .font(themeManager.currentTheme.secondaryBodyFont) .foregroundColor(themeManager.currentTheme.textMutedColor) From 287a124a766f901080a90af45698e3de27aae093 Mon Sep 17 00:00:00 2001 From: ryanmello07 Date: Sat, 18 Jul 2026 19:54:38 +0100 Subject: [PATCH 097/109] fix: gate seedphrase Sign In on word count, not just non-empty isSeedphraseValid only checked the raw string was non-empty after trimming, so a single stray character enabled the Sign In button and hit the server with obviously-invalid input. validateWordCount() already computed the real 12/24-word check but only drove a warning label, never the actual submit gate. Now isSeedphraseValid uses the same word-count check. --- .../LoginSeedphrase/LoginSeedphraseViewModel.swift | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/app/network/Authenticate/LoginSeedphrase/LoginSeedphraseViewModel.swift b/app/network/Authenticate/LoginSeedphrase/LoginSeedphraseViewModel.swift index 3039b33d..a5ac278d 100644 --- a/app/network/Authenticate/LoginSeedphrase/LoginSeedphraseViewModel.swift +++ b/app/network/Authenticate/LoginSeedphrase/LoginSeedphraseViewModel.swift @@ -36,7 +36,7 @@ extension LoginSeedphraseView { } var isSeedphraseValid: Bool { - !seedphrase.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty + wordCount == 12 || wordCount == 24 } var normalizedSeedphrase: String { @@ -73,10 +73,9 @@ extension LoginSeedphraseView { } guard isSeedphraseValid else { - return .failure(NSError(domain: domain, code: 0, userInfo: [NSLocalizedDescriptionKey: "Seedphrase is empty"])) + return .failure(NSError(domain: domain, code: 0, userInfo: [NSLocalizedDescriptionKey: "Seedphrase should be 12 or 24 words"])) } - // Warn but don't block on word count — server handles validation validateWordCount() isLoggingIn = true From 6ed6f8f13868ebc8ede39a961cc02a8756c6e69b Mon Sep 17 00:00:00 2001 From: ryanmello07 Date: Sat, 18 Jul 2026 19:56:39 +0100 Subject: [PATCH 098/109] fix: createInstantAccount ignores verificationRequired unlike createNetwork createInstantAccount's callback never checked result.verificationRequired, unlike sibling createNetwork on the same shared NetworkCreateCallback/ result type. If the server ever returns verificationRequired for an instant-account response, the code previously fell through to a generic "No seedphrase in result" error instead of a clear message. --- app/network/Shared/Services/UrApiService/UrApiService.swift | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/network/Shared/Services/UrApiService/UrApiService.swift b/app/network/Shared/Services/UrApiService/UrApiService.swift index 3464c883..0eac82fa 100644 --- a/app/network/Shared/Services/UrApiService/UrApiService.swift +++ b/app/network/Shared/Services/UrApiService/UrApiService.swift @@ -465,6 +465,11 @@ extension UrApiService { return } + if result.verificationRequired != nil { + continuation.resume(throwing: NSError(domain: "UrApiService", code: -1, userInfo: [NSLocalizedDescriptionKey: "Verification required for instant account creation"])) + return + } + guard !result.seedphrase.isEmpty else { continuation.resume(throwing: NSError(domain: "UrApiService", code: -1, userInfo: [NSLocalizedDescriptionKey: "No seedphrase in result"])) return From 7cf9297c5efaa91fb3a32364d196f7b3f8fab023 Mon Sep 17 00:00:00 2001 From: ryanmello07 Date: Sat, 18 Jul 2026 19:57:23 +0100 Subject: [PATCH 099/109] fix: dim macOS seedphrase/instant-account buttons when disabled These two new macOS buttons applied .disabled(isLoginActionInFlight) but, unlike sibling Apple/Auth-Code buttons in the same view, never dimmed on that state -- they went non-interactive while staying at full opacity, giving no visual cue. --- app/network/Authenticate/LoginInitial/LoginInitialView.swift | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/network/Authenticate/LoginInitial/LoginInitialView.swift b/app/network/Authenticate/LoginInitial/LoginInitialView.swift index 1f1ff120..87a277cf 100644 --- a/app/network/Authenticate/LoginInitial/LoginInitialView.swift +++ b/app/network/Authenticate/LoginInitial/LoginInitialView.swift @@ -1063,9 +1063,10 @@ private struct SSOButtons: View { .cornerRadius(6) .buttonStyle(.plain) .disabled(isLoginActionInFlight) + .opacity(isLoginActionInFlight ? 0.3 : 1) Spacer().frame(width: 8) - + // Instant create account button (macOS) Button(action: presentCreateInstant) { HStack { @@ -1086,6 +1087,7 @@ private struct SSOButtons: View { .cornerRadius(6) .buttonStyle(.plain) .disabled(isLoginActionInFlight) + .opacity(isLoginActionInFlight ? 0.3 : 1) } } From 0ac09e1a5176dad35fb08bad81b7292615c5f032 Mon Sep 17 00:00:00 2001 From: ryanmello07 Date: Sat, 18 Jul 2026 19:57:50 +0100 Subject: [PATCH 100/109] fix: don't refresh network user after a failed seedphrase/auth operation executeGenerateSeedphrase, executeRegenerateSeedphrase, and executeRemoveAuth each fired a refreshNetworkUser() network round-trip unconditionally after their do/catch block, so a caught failure still triggered a redundant fetch of the user record that couldn't have changed. Scoped the refresh to the success path in each. --- .../Account/Settings/SettingsViewModel.swift | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/app/network/Main/Account/Settings/SettingsViewModel.swift b/app/network/Main/Account/Settings/SettingsViewModel.swift index 15601fe4..76a53812 100644 --- a/app/network/Main/Account/Settings/SettingsViewModel.swift +++ b/app/network/Main/Account/Settings/SettingsViewModel.swift @@ -319,15 +319,15 @@ extension SettingsView { self.isGeneratingSeedphrase = false self.presentSeedphraseConfirmation = false self.presentSeedphraseSheet = true + // Refresh user data to get updated auth_types + Task { [weak self] in + _ = await self?.networkUserViewModel?.refreshNetworkUser() + } } catch(let error) { self.isGeneratingSeedphrase = false self.presentSeedphraseConfirmation = false self.seedphraseError = error.localizedDescription } - // Refresh user data to get updated auth_types - Task { [weak self] in - _ = await self?.networkUserViewModel?.refreshNetworkUser() - } } func executeRegenerateSeedphrase() async { @@ -340,15 +340,15 @@ extension SettingsView { self.isRegeneratingSeedphrase = false self.presentSeedphraseConfirmation = false self.presentSeedphraseSheet = true + // Refresh user data to get updated auth_types + Task { [weak self] in + _ = await self?.networkUserViewModel?.refreshNetworkUser() + } } catch(let error) { self.isRegeneratingSeedphrase = false self.presentSeedphraseConfirmation = false self.seedphraseError = error.localizedDescription } - // Refresh user data to get updated auth_types - Task { [weak self] in - _ = await self?.networkUserViewModel?.refreshNetworkUser() - } } func dismissSeedphraseSheet() { @@ -381,14 +381,14 @@ extension SettingsView { self.isRemovingAuth = false self.presentRemoveAuthConfirmation = false self.authTypeToRemove = nil + // Refresh user data to get updated auth_types + Task { [weak self] in + _ = await self?.networkUserViewModel?.refreshNetworkUser() + } } catch(let error) { self.isRemovingAuth = false self.removeAuthError = error.localizedDescription } - // Refresh user data to get updated auth_types - Task { [weak self] in - _ = await self?.networkUserViewModel?.refreshNetworkUser() - } } } From d67c2769924cd6d4372d0f809062de9870cfc254 Mon Sep 17 00:00:00 2001 From: ryanmello07 Date: Sat, 18 Jul 2026 20:51:52 +0100 Subject: [PATCH 101/109] fix: include phone auth in ProfileView's needsNameClaim identity check The needsNameClaim computation (added in 8cb76da) checked authTypes for email/google/apple but missed "phone" -- a distinct auth_type value from "email" server-side, and a reachable sign-up/login path in this app (LoginInitialView/CreateNetworkView both offer "Email or phone number"). A phone-authenticated account was incorrectly treated as having no identity method bound, routing it to the claim-name flow instead of the normal change-name flow. Found in re-review before this landed anywhere. --- app/network/Main/Account/AccountNavStackView.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/app/network/Main/Account/AccountNavStackView.swift b/app/network/Main/Account/AccountNavStackView.swift index 4f5a4c41..ec23b9e3 100644 --- a/app/network/Main/Account/AccountNavStackView.swift +++ b/app/network/Main/Account/AccountNavStackView.swift @@ -109,6 +109,7 @@ struct AccountNavStackView: View { needsNameClaim: { guard let networkUser = networkUserViewModel.networkUser else { return false } let hasIdentityMethod = authTypesContains(networkUser.authTypes, "email") + || authTypesContains(networkUser.authTypes, "phone") || authTypesContains(networkUser.authTypes, "google") || authTypesContains(networkUser.authTypes, "apple") return !hasIdentityMethod From da386049c0e92652ad137ea834ef7f911922cb2a Mon Sep 17 00:00:00 2001 From: ryanmello07 Date: Sun, 19 Jul 2026 01:06:28 +0100 Subject: [PATCH 102/109] fix: stale UI after network name change, and recognize wallet as identity for name claim ProfileView reads the displayed name from the cached JWT, which the change/claim-name calls don't reissue, so the screen (and anywhere else the JWT-derived name is read) kept showing the old name until the SDK's next background refresh. Force a refresh on success, the same way SubscriptionBalanceViewModel does after a balance change. Also recognize a bound wallet ("solana") as a valid identity method for needsNameClaim, matching the backend's requireVerifiedIdentityBound change that now accepts wallet-bound users for claim/change-name. --- app/network/Main/Account/AccountNavStackView.swift | 1 + app/network/Main/Account/Profile/ProfileView.swift | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/app/network/Main/Account/AccountNavStackView.swift b/app/network/Main/Account/AccountNavStackView.swift index ec23b9e3..d32c2bce 100644 --- a/app/network/Main/Account/AccountNavStackView.swift +++ b/app/network/Main/Account/AccountNavStackView.swift @@ -112,6 +112,7 @@ struct AccountNavStackView: View { || authTypesContains(networkUser.authTypes, "phone") || authTypesContains(networkUser.authTypes, "google") || authTypesContains(networkUser.authTypes, "apple") + || authTypesContains(networkUser.authTypes, "solana") return !hasIdentityMethod }() ) diff --git a/app/network/Main/Account/Profile/ProfileView.swift b/app/network/Main/Account/Profile/ProfileView.swift index 538ed7e3..62bb3685 100644 --- a/app/network/Main/Account/Profile/ProfileView.swift +++ b/app/network/Main/Account/Profile/ProfileView.swift @@ -167,6 +167,15 @@ struct ProfileView: View { switch result { case .success(let newName): snackbarManager.showSnackbar(message: String(localized: "Network name changed to \(newName)")) + // The displayed network name comes from the cached JWT + // (deviceManager.parsedJwt?.networkName), which isn't reissued by + // this call — force a refresh so the new name propagates to this + // screen and anywhere else the JWT-derived name is shown. + do { + try deviceManager.device?.refreshToken(0) + } catch { + print("Error refreshing JWT after network name change: \(error)") + } case .failure(let error): print("Error changing network name: \(error)") snackbarManager.showSnackbar(message: String(localized: "Error changing network name")) From 02d4b030c0b451a060883f69e74073e6e9434fc1 Mon Sep 17 00:00:00 2001 From: ryanmello07 Date: Sun, 19 Jul 2026 04:45:34 +0100 Subject: [PATCH 103/109] fix: refresh JWT when the app returns to foreground, not just on rename Cold launch already gets a fresh JWT (the SDK's token manager refreshes once immediately on start), but resuming from the background doesn't -- the token manager only refreshes on its own schedule (~14 days out from expiration) or when explicitly asked. Add a scenePhase hook to force a refresh on foreground so a network name (or anything else JWT-derived) changed from another device or the web dashboard shows up without needing a full logout/login. --- app/network/NetworkApp.swift | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/app/network/NetworkApp.swift b/app/network/NetworkApp.swift index a2f6fe48..b16d34af 100644 --- a/app/network/NetworkApp.swift +++ b/app/network/NetworkApp.swift @@ -20,9 +20,10 @@ struct NetworkApp: App { #endif @AppStorage("showMenuBarExtra") private var showMenuBarExtra = true - + @State private var isWindowVisible = true @State private var keyEventMonitor: Any? + @Environment(\.scenePhase) private var scenePhase let themeManager = ThemeManager.shared @@ -90,6 +91,21 @@ struct NetworkApp: App { dnsSettingsStore.reset() networkPeersStore.reset() } + + // Cold launch already gets a fresh JWT (the SDK's token manager refreshes + // once immediately on start), but returning from the background does not + // -- the token manager only refreshes on a schedule (~14 days out from + // expiration) or when explicitly asked. Force one here so anything that + // could have changed out-of-band (e.g. a network name changed from + // another device or the web dashboard) shows up when the user comes + // back to the app, not just after a local rename or a full logout/login. + private func refreshJwtOnForeground() { + do { + try deviceManager.device?.refreshToken(0) + } catch { + print("Error refreshing JWT on foreground: \(error)") + } + } private var connectEnabled: Bool { @@ -157,6 +173,11 @@ struct NetworkApp: App { .onReceive(deviceManager.$device) { device in updateConnectViewModel(device) } + .onChange(of: scenePhase) { newPhase in + if newPhase == .active { + refreshJwtOnForeground() + } + } #elseif os(macOS) ContentView() .environmentObject(themeManager) @@ -185,6 +206,11 @@ struct NetworkApp: App { .onReceive(deviceManager.$device) { device in updateConnectViewModel(device) } + .onChange(of: scenePhase) { newPhase in + if newPhase == .active { + refreshJwtOnForeground() + } + } .onAppear { if mainWindow == nil { mainWindow = NSApplication.shared.windows.first { window in From 58307d97e0f7ad701550084cc8f27240d158c694 Mon Sep 17 00:00:00 2001 From: ryanmello07 Date: Sun, 19 Jul 2026 05:22:21 +0100 Subject: [PATCH 104/109] feat(ios,macos): add seedphrase sign-in, instant account creation, and multi-method auth settings Adds a full seedphrase-based auth flow alongside the existing email/SSO/wallet paths: - Seedphrase login and instant (no-verification) account creation, with a seedphrase display/backup screen on both iOS and macOS. - A rebuilt Settings > Sign-In Methods section (shared between iOS and macOS) that lists and manages every bound auth method live from the server's auth_types array, with an AddAuthSheet supporting email/password, Apple, Google, and Solana wallet binding. - Profile's claim-vs-change network name flow now checks live auth_types instead of a frozen legacy field, so it correctly detects when a user still needs to claim a name. - Guest mode removed (GuestModeSheet, GuestUpgradeViewModel, and the associated create-network guest path deleted). This is the exact diff behind Ryanmello07/urnetwork-ios#4, squashed into one commit for this upstream PR (that branch's own history is ~70 commits, mostly iterative CI/build-error/type-checker-timeout fixes with no standalone review value). Depends on urnetwork/sdk#132 (AuthTypes/WalletAddress fields and the seedphrase/wallet-auth endpoints this UI calls) -- opening as a draft until that lands, since this won't build against a Package.swift pointed at stock upstream sdk/main until then. --- .../CreateNetwork/CreateNetworkView.swift | 11 +- .../CreateNetworkViewModel.swift | 52 -- .../CreateNetworkInstantView.swift | 133 +++++ .../CreateNetworkInstantViewModel.swift | 73 +++ .../SeedphraseDisplayView.swift | 162 ++++++ .../LoginInitial/GuestModeSheet.swift | 67 --- .../LoginInitial/LoginInitialView.swift | 380 ++++++------- .../LoginInitial/LoginInitialViewModel.swift | 46 -- .../Authenticate/LoginNavigationView.swift | 21 +- .../LoginNavigationViewModel.swift | 2 + .../LoginPassword/LoginPasswordView.swift | 49 +- .../LoginSeedphrase/LoginSeedphraseView.swift | 125 ++++ .../LoginSeedphraseViewModel.swift | 99 ++++ .../Main/Account/AccountNavStackView.swift | 12 +- .../Main/Account/Profile/ProfileView.swift | 120 +++- .../Account/Profile/ProfileViewModel.swift | 132 +++++ .../Main/Account/Settings/AddAuthSheet.swift | 533 ++++++++++++++++++ .../Main/Account/Settings/AuthMethods.swift | 57 ++ .../Account/Settings/SettingsForm-iOS.swift | 96 ++++ .../Account/Settings/SettingsForm-macOS.swift | 114 +++- .../Main/Account/Settings/SettingsView.swift | 158 +++++- .../Account/Settings/SettingsViewModel.swift | 131 ++++- app/network/NetworkApp.swift | 28 +- .../UrApiService/MockUrApiService.swift | 36 +- .../Services/UrApiService/UrApiService.swift | 329 ++++++++++- .../UrApiService/UrApiServiceProtocol.swift | 21 +- .../ConnectWalletProviderViewModel.swift | 7 + .../ViewModels/GuestUpgradeViewModel.swift | 111 ---- 28 files changed, 2496 insertions(+), 609 deletions(-) create mode 100644 app/network/Authenticate/CreateNetworkInstant/CreateNetworkInstantView.swift create mode 100644 app/network/Authenticate/CreateNetworkInstant/CreateNetworkInstantViewModel.swift create mode 100644 app/network/Authenticate/CreateNetworkInstant/SeedphraseDisplayView.swift delete mode 100644 app/network/Authenticate/LoginInitial/GuestModeSheet.swift create mode 100644 app/network/Authenticate/LoginSeedphrase/LoginSeedphraseView.swift create mode 100644 app/network/Authenticate/LoginSeedphrase/LoginSeedphraseViewModel.swift create mode 100644 app/network/Main/Account/Settings/AddAuthSheet.swift create mode 100644 app/network/Main/Account/Settings/AuthMethods.swift delete mode 100644 app/network/Shared/ViewModels/GuestUpgradeViewModel.swift diff --git a/app/network/Authenticate/CreateNetwork/CreateNetworkView.swift b/app/network/Authenticate/CreateNetwork/CreateNetworkView.swift index f3091c10..3ecc5ad4 100644 --- a/app/network/Authenticate/CreateNetwork/CreateNetworkView.swift +++ b/app/network/Authenticate/CreateNetwork/CreateNetworkView.swift @@ -188,16 +188,7 @@ struct CreateNetworkView: View { #endif Task { - let result = deviceManager.device != nil - // device exists - upgrade guest network - ? await viewModel.upgradeGuestNetwork( - userAuth: userAuth, - authJwt: authLoginArgs.authJwt, - authType: authLoginArgs.authJwtType, - walletAuth: authLoginArgs.walletAuth - ) - // no device exists - create a new network - : await viewModel.createNetwork( + let result = await viewModel.createNetwork( userAuth: userAuth, authJwt: authLoginArgs.authJwt, authType: authLoginArgs.authJwtType, diff --git a/app/network/Authenticate/CreateNetwork/CreateNetworkViewModel.swift b/app/network/Authenticate/CreateNetwork/CreateNetworkViewModel.swift index d133425e..a77bd0ea 100644 --- a/app/network/Authenticate/CreateNetwork/CreateNetworkViewModel.swift +++ b/app/network/Authenticate/CreateNetwork/CreateNetworkViewModel.swift @@ -247,58 +247,6 @@ extension CreateNetworkView { } - func upgradeGuestNetwork( - userAuth: String?, - authJwt: String?, - authType: String?, - walletAuth: SdkWalletAuthArgs? - ) async -> LoginNetworkResult { - - if !formIsValid { - return .failure(NSError(domain: domain, code: 0, userInfo: [NSLocalizedDescriptionKey: "Create network form is invalid"])) - } - - if isCreatingNetwork { - return .failure(NSError(domain: domain, code: 0, userInfo: [NSLocalizedDescriptionKey: "Network creation already in progress"])) - } - - - self.createNetworkErrorMessage = nil - self.isCreatingNetwork = true - - do { - - let args = SdkUpgradeGuestArgs() - args.networkName = networkName.trimmingCharacters(in: .whitespacesAndNewlines) - - if let userAuth = userAuth { - args.userAuth = userAuth - args.password = password - } - - if let authJwt, let authType { - args.authJwt = authJwt - args.authJwtType = authType - } - - if let walletAuth { - args.walletAuth = walletAuth - } - - let result = try await urApiService.upgradeGuest(args) - - self.isCreatingNetwork = false - - return result - - } catch { - self.isCreatingNetwork = false - return .failure(error) - } - - - } - func createNetwork( userAuth: String?, authJwt: String?, diff --git a/app/network/Authenticate/CreateNetworkInstant/CreateNetworkInstantView.swift b/app/network/Authenticate/CreateNetworkInstant/CreateNetworkInstantView.swift new file mode 100644 index 00000000..840ceeb2 --- /dev/null +++ b/app/network/Authenticate/CreateNetworkInstant/CreateNetworkInstantView.swift @@ -0,0 +1,133 @@ +// +// CreateNetworkInstantView.swift +// URnetwork +// + +import SwiftUI +import URnetworkSdk + +private struct InstantAccountResult: Identifiable { + let id = UUID() + let jwt: String + let seedphrase: String +} + +struct CreateNetworkInstantView: View { + + @EnvironmentObject var themeManager: ThemeManager + + @StateObject private var viewModel: ViewModel + + let handleSuccess: (_ jwt: String) async -> Void + let back: () -> Void + + @State private var accountResult: InstantAccountResult? = nil + + init( + urApiService: UrApiServiceProtocol, + handleSuccess: @escaping (_ jwt: String) async -> Void, + back: @escaping () -> Void + ) { + _viewModel = StateObject(wrappedValue: ViewModel(urApiService: urApiService)) + self.handleSuccess = handleSuccess + self.back = back + } + + var body: some View { + ScrollView { + VStack(alignment: .center) { + + Text("Create Instant Account") + .foregroundColor(.urWhite) + .font(themeManager.currentTheme.titleFont) + + Spacer().frame(height: 16) + + Text("No email, phone, or password needed. Your account is secured by a seedphrase.") + .foregroundColor(themeManager.currentTheme.textMutedColor) + .font(themeManager.currentTheme.bodyFont) + .multilineTextAlignment(.center) + + Spacer().frame(height: 48) + + UrSwitchToggle(isOn: $viewModel.termsAgreed, isEnabled: !viewModel.isCreatingAccount) { + Text("I agree to URnetwork's [Terms and Services](https://ur.io/terms) and [Privacy Policy](https://ur.io/privacy)") + .foregroundColor(themeManager.currentTheme.textMutedColor) + .font(themeManager.currentTheme.secondaryBodyFont) + } + + Spacer().frame(height: 32) + + UrButton( + text: "Create Account", + action: { + Task { + let result = await viewModel.createInstantAccount() + if let (jwt, seedphrase) = result { + accountResult = InstantAccountResult(jwt: jwt, seedphrase: seedphrase) + } + } + }, + enabled: viewModel.formIsValid, + isProcessing: viewModel.isCreatingAccount + ) + + Spacer().frame(height: 8) + + UrInlineErrorText(message: viewModel.errorMessage) + + } + .padding() + .frame(maxWidth: .infinity) + .frame(maxWidth: 400) + } + .background(themeManager.currentTheme.backgroundColor.ignoresSafeArea()) + .toolbar { + #if os(iOS) + ToolbarItem(placement: .navigationBarLeading) { + Button(action: { back() }) { + Image(systemName: "chevron.left") + .foregroundColor(themeManager.currentTheme.textColor) + } + } + #elseif os(macOS) + ToolbarItem { + Button(action: { back() }) { + Image(systemName: "chevron.left") + .foregroundColor(themeManager.currentTheme.textColor) + } + } + #endif + } + #if os(iOS) + .navigationBarBackButtonHidden(true) + .fullScreenCover(item: $accountResult) { result in + SeedphraseDisplayView( + seedphrase: result.seedphrase, + onConfirmed: { _ in + accountResult = nil + Task { + await handleSuccess(result.jwt) + } + } + ) + .environmentObject(themeManager) + } + #elseif os(macOS) + .sheet(item: $accountResult) { result in + SeedphraseDisplayView( + seedphrase: result.seedphrase, + onConfirmed: { _ in + accountResult = nil + Task { + await handleSuccess(result.jwt) + } + } + ) + .environmentObject(themeManager) + .interactiveDismissDisabled(true) + } + #endif + } + +} diff --git a/app/network/Authenticate/CreateNetworkInstant/CreateNetworkInstantViewModel.swift b/app/network/Authenticate/CreateNetworkInstant/CreateNetworkInstantViewModel.swift new file mode 100644 index 00000000..7243fdf6 --- /dev/null +++ b/app/network/Authenticate/CreateNetworkInstant/CreateNetworkInstantViewModel.swift @@ -0,0 +1,73 @@ +// +// CreateNetworkInstantViewModel.swift +// URnetwork +// + +import Foundation +import URnetworkSdk + +extension CreateNetworkInstantView { + + @MainActor + class ViewModel: ObservableObject { + + private let urApiService: UrApiServiceProtocol + + @Published var termsAgreed: Bool = false { + didSet { + errorMessage = nil + validateForm() + } + } + + @Published private(set) var isCreatingAccount: Bool = false + + @Published private(set) var formIsValid: Bool = false + + @Published private(set) var errorMessage: String? + + let domain = "CreateNetworkInstantViewModel" + + init(urApiService: UrApiServiceProtocol) { + self.urApiService = urApiService + } + + func setErrorMessage(_ message: String?) { + errorMessage = message + } + + private func validateForm() { + formIsValid = termsAgreed && !isCreatingAccount + } + + func createInstantAccount() async -> (jwt: String, seedphrase: String)? { + + if isCreatingAccount { + return nil + } + + guard termsAgreed else { + errorMessage = "You must agree to the Terms and Privacy Policy" + return nil + } + + isCreatingAccount = true + errorMessage = nil + + defer { + isCreatingAccount = false + } + + do { + let result = try await urApiService.createInstantAccount() + return result + } catch { + errorMessage = "There was an error creating your account. Please try again." + return nil + } + + } + + } + +} diff --git a/app/network/Authenticate/CreateNetworkInstant/SeedphraseDisplayView.swift b/app/network/Authenticate/CreateNetworkInstant/SeedphraseDisplayView.swift new file mode 100644 index 00000000..90e503d3 --- /dev/null +++ b/app/network/Authenticate/CreateNetworkInstant/SeedphraseDisplayView.swift @@ -0,0 +1,162 @@ +// +// SeedphraseDisplayView.swift +// URnetwork +// + +import SwiftUI + +private struct SeedWord: Identifiable { + let id: Int + let word: String +} + +struct SeedphraseDisplayView: View { + + @EnvironmentObject var themeManager: ThemeManager + + let seedphrase: String + let onConfirmed: (_ jwt: String) -> Void + + @State private var hasCopied = false + + private let words: [SeedWord] + + init(seedphrase: String, onConfirmed: @escaping (String) -> Void) { + // Normalize: trim whitespace and collapse multiple spaces + let normalized = seedphrase + .trimmingCharacters(in: .whitespacesAndNewlines) + .components(separatedBy: .whitespacesAndNewlines) + .filter { !$0.isEmpty } + .joined(separator: " ") + self.seedphrase = normalized + self.onConfirmed = onConfirmed + let tokens = normalized.components(separatedBy: " ") + self.words = tokens.enumerated().map { SeedWord(id: $0.offset, word: $0.element) } + } + + var body: some View { + NavigationStack { + ScrollView { + VStack(alignment: .center) { + + titleView + warningView + instructionsView + + Spacer().frame(height: 32) + + wordGridView + + Spacer().frame(height: 24) + + copyButton + confirmationButton + + } + .padding() + .frame(maxWidth: .infinity) + .frame(maxWidth: 400) + } + .background(themeManager.currentTheme.backgroundColor.ignoresSafeArea()) + .interactiveDismissDisabled(true) + .toolbar { + ToolbarItem(placement: .principal) { + Text("Secure Your Account") + .font(themeManager.currentTheme.toolbarTitleFont).fontWeight(.bold) + } + } + } + } + + private var titleView: some View { + Text("Your Seedphrase") + .foregroundColor(.urWhite) + .font(themeManager.currentTheme.titleFont) + } + + private var warningView: some View { + Text("⚠️ This is the ONLY time you'll see this.") + .foregroundColor(.urYellow) + .font(themeManager.currentTheme.bodyFont.bold()) + .multilineTextAlignment(.center) + } + + private var instructionsView: some View { + Text("Write it down and store it somewhere safe. If you lose it, you'll lose access to your account.") + .foregroundColor(themeManager.currentTheme.textMutedColor) + .font(themeManager.currentTheme.secondaryBodyFont) + .multilineTextAlignment(.center) + } + + private var wordGridView: some View { + let columns = [GridItem(.flexible(), spacing: 8), GridItem(.flexible(), spacing: 8)] + return LazyVGrid(columns: columns, spacing: 8) { + ForEach(words) { seedWord in + wordRowView(index: seedWord.id, word: seedWord.word) + } + } + .padding(12) + .overlay( + RoundedRectangle(cornerRadius: 8) + .stroke(themeManager.currentTheme.textMutedColor.opacity(0.3), lineWidth: 1) + ) + } + + private func wordRowView(index: Int, word: String) -> some View { + HStack(spacing: 4) { + Text(String(index + 1) + ".") + .foregroundColor(themeManager.currentTheme.textMutedColor) + .font(.system(.caption, design: .monospaced)) + .frame(width: 24, alignment: .trailing) + Text(word) + .foregroundColor(.urWhite) + .font(.system(.body, design: .monospaced)) + .lineLimit(1) + Spacer() + } + .padding(.vertical, 6) + .padding(.horizontal, 8) + .background(themeManager.currentTheme.tintedBackgroundBase) + .cornerRadius(6) + } + + private var copyButton: some View { + UrButton( + text: hasCopied ? "Copied!" : "Copy to Clipboard", + action: { + #if os(iOS) + UIPasteboard.general.string = seedphrase + #elseif os(macOS) + NSPasteboard.general.clearContents() + NSPasteboard.general.setString(seedphrase, forType: .string) + #endif + hasCopied = true + DispatchQueue.main.asyncAfter(deadline: .now() + 2) { + hasCopied = false + } + }, + enabled: !hasCopied + ) + } + + private var confirmationButton: some View { + UrButton( + text: "I've Saved My Seedphrase", + action: { + onConfirmed(seedphrase) + }, + enabled: true, + isProcessing: false + ) + .tint(.urGreen) + } + +} + +#Preview { + SeedphraseDisplayView( + seedphrase: "abandon ability able about above absent absorb abstract absurd abuse access accident account accuse achieve acid acoustic acquire across act action actor active activity", + onConfirmed: { _ in } + ) + .environmentObject(ThemeManager.shared) +} diff --git a/app/network/Authenticate/LoginInitial/GuestModeSheet.swift b/app/network/Authenticate/LoginInitial/GuestModeSheet.swift deleted file mode 100644 index e6179bfe..00000000 --- a/app/network/Authenticate/LoginInitial/GuestModeSheet.swift +++ /dev/null @@ -1,67 +0,0 @@ -// -// GuestModeSheet.swift -// URnetwork -// -// Created by Stuart Kuentzel on 2025/01/10. -// - -import SwiftUI - -struct GuestModeSheet: View { - - @Binding var termsAgreed: Bool - let isCreatingGuestNetwork: Bool - let errorMessage: String? - let onCreateGuestNetwork: () -> Void - - - @EnvironmentObject var themeManager: ThemeManager - - var body: some View { - VStack { - - HStack { - - Text("Try guest mode") - .font(themeManager.currentTheme.secondaryTitleFont) - - Spacer() - - } - - Spacer().frame(height: 24) - - UrSwitchToggle(isOn: $termsAgreed, isEnabled: !isCreatingGuestNetwork) { - Text("I agree to URnetwork's [Terms and Services](https://ur.io/terms) and [Privacy Policy](https://ur.io/privacy)") - .foregroundColor(themeManager.currentTheme.textMutedColor) - .font(themeManager.currentTheme.secondaryBodyFont) - } - - Spacer().frame(height: 24) - - UrButton( - text: "Enter URnetwork", - action: { - onCreateGuestNetwork() - }, - enabled: termsAgreed && !isCreatingGuestNetwork, - isProcessing: isCreatingGuestNetwork - ) - - Spacer().frame(height: 8) - - UrInlineErrorText(message: errorMessage) - - } - .padding() - } -} - -#Preview { - GuestModeSheet( - termsAgreed: .constant(false), - isCreatingGuestNetwork: false, - errorMessage: nil, - onCreateGuestNetwork: {} - ) -} diff --git a/app/network/Authenticate/LoginInitial/LoginInitialView.swift b/app/network/Authenticate/LoginInitial/LoginInitialView.swift index 2280c749..db08fb92 100644 --- a/app/network/Authenticate/LoginInitial/LoginInitialView.swift +++ b/app/network/Authenticate/LoginInitial/LoginInitialView.swift @@ -20,8 +20,6 @@ struct LoginInitialView: View { @StateObject private var viewModel: ViewModel @State private var initialIsLandscape: Bool = false - @ObservedObject var guestUpgradeViewModel: GuestUpgradeViewModel - let navigate: (LoginInitialNavigationPath) -> Void let cancel: (() -> Void)? let handleSuccess: (_ jwt: String) async -> Void @@ -31,14 +29,12 @@ struct LoginInitialView: View { urApiService: UrApiServiceProtocol, navigate: @escaping (LoginInitialNavigationPath) -> Void, cancel: (() -> Void)? = nil, - handleSuccess: @escaping (_ jwt: String) async -> Void, - guestUpgradeViewModel: GuestUpgradeViewModel + handleSuccess: @escaping (_ jwt: String) async -> Void ) { _viewModel = StateObject(wrappedValue: ViewModel(urApiService: urApiService)) self.navigate = navigate self.cancel = cancel self.handleSuccess = handleSuccess - self.guestUpgradeViewModel = guestUpgradeViewModel self.urApiService = urApiService } @@ -70,7 +66,7 @@ struct LoginInitialView: View { handleGoogleSignInButton: handleGoogleSignInButton, isValidUserAuth: viewModel.isValidUserAuth, activeLoginAction: viewModel.activeLoginAction, - isLoginActionInFlight: viewModel.isLoginActionInFlight || guestUpgradeViewModel.isUpgrading, + isLoginActionInFlight: viewModel.isLoginActionInFlight, loginErrorMessage: viewModel.loginErrorMessage, deviceExists: deviceExists, presentSignInWithSolanaSheet: { @@ -87,7 +83,12 @@ struct LoginInitialView: View { presentAuthCodeLoginSheet: { viewModel.setPresentAuthCodeLoginSheet(true) }, - presentGuestNetworkSheet: $viewModel.presentGuestNetworkSheet, + presentSeedphraseLogin: { + navigate(.seedphrase) + }, + presentCreateInstant: { + navigate(.createInstant) + } ) .frame(width: geometry.size.width / 2, alignment: .leading) @@ -109,7 +110,7 @@ struct LoginInitialView: View { handleGoogleSignInButton: handleGoogleSignInButton, isValidUserAuth: viewModel.isValidUserAuth, activeLoginAction: viewModel.activeLoginAction, - isLoginActionInFlight: viewModel.isLoginActionInFlight || guestUpgradeViewModel.isUpgrading, + isLoginActionInFlight: viewModel.isLoginActionInFlight, loginErrorMessage: viewModel.loginErrorMessage, deviceExists: deviceExists, presentSignInWithSolanaSheet: { @@ -126,7 +127,12 @@ struct LoginInitialView: View { presentAuthCodeLoginSheet: { viewModel.setPresentAuthCodeLoginSheet(true) }, - presentGuestNetworkSheet: $viewModel.presentGuestNetworkSheet + presentSeedphraseLogin: { + navigate(.seedphrase) + }, + presentCreateInstant: { + navigate(.createInstant) + } ) } @@ -156,23 +162,6 @@ struct LoginInitialView: View { .presentationDetents([.height(216)]) #endif - } - .sheet(isPresented: $viewModel.presentGuestNetworkSheet) { - - GuestModeSheet( - termsAgreed: $viewModel.termsAgreed, - isCreatingGuestNetwork: viewModel.isCreatingGuestNetwork, - errorMessage: viewModel.guestNetworkErrorMessage, - onCreateGuestNetwork: { - Task { - let result = await viewModel.createGuestNetwork() - await self.handleCreateGuestNetworkResult(result) - } - } - ) - .environmentObject(themeManager) - .presentationDetents([.height(264)]) - } .sheet(isPresented: $viewModel.presentAuthCodeLoginSheet) { @@ -305,23 +294,10 @@ struct LoginInitialView: View { switch createArgsResult { case .success(let args): - if deviceManager.device != nil { - - let upgradeArgs = self.createUpgradeSolanaWalletArgs(args) - - let result = await guestUpgradeViewModel.linkGuestToExistingLogin(args: upgradeArgs) - - await self.handleAuthLoginResult(result) - viewModel.presentSigninWithSolanaSheet = false - viewModel.setIsSigningMessage(false) - - - } else { - let result = await viewModel.authLogin(args: args) - await self.handleAuthLoginResult(result) - viewModel.presentSigninWithSolanaSheet = false - viewModel.setIsSigningMessage(false) - } + let result = await viewModel.authLogin(args: args) + await self.handleAuthLoginResult(result) + viewModel.presentSigninWithSolanaSheet = false + viewModel.setIsSigningMessage(false) case .failure(let error): print("error create args result: \(error.localizedDescription)") @@ -330,25 +306,6 @@ struct LoginInitialView: View { } } - private func handleCreateGuestNetworkResult(_ result: LoginNetworkResult) async { - switch result { - - case .successWithJwt(let jwt): - viewModel.presentGuestNetworkSheet = false - await handleSuccess(jwt) - break - case .failure(let error): - print("CreateNetworkView: handleResult: \(error.localizedDescription)") - viewModel.setGuestNetworkErrorMessage("There was an error creating your guest network. Please try again.") - break - default: - print("neither success with jwt or failure") - viewModel.setGuestNetworkErrorMessage("There was an error creating your guest network. Please try again.") - break - - } - } - private func handleAppleLoginResult(_ result: Result) async { guard viewModel.beginLoginAction(.apple) else { @@ -362,25 +319,8 @@ struct LoginInitialView: View { let createArgsResult = viewModel.createAppleAuthLoginArgs(result) switch createArgsResult { case .success(let args): - - if deviceManager.device != nil { - - // device exists, meaning we're in the guest flow - // link guest account to google account - - let upgradeArgs = self.createUpgradeExistingSocialArgs(args) - - let result = await guestUpgradeViewModel.linkGuestToExistingLogin(args: upgradeArgs) - await self.handleAuthLoginResult(result) - - } else { - - // login with apple - // let result = await viewModel.authLogin(args: args) - let result = await viewModel.authLogin(args: args) - await self.handleAuthLoginResult(result) - - } + let result = await viewModel.authLogin(args: args) + await self.handleAuthLoginResult(result) case .failure(let error): print("error create args result: \(error.localizedDescription)") @@ -414,6 +354,56 @@ struct LoginInitialView: View { } + private func handleGoogleSignInButton() async { + + guard viewModel.beginLoginAction(.google) else { + return + } + + defer { + viewModel.endLoginAction(.google) + } + + do { + #if os(iOS) + + guard let rootViewController = getRootViewController() else { + print("no root view controller found") + viewModel.setLoginErrorMessage(String(localized: "There was an error logging in")) + return + } + + let signInResult = try await GIDSignIn.sharedInstance.signIn(withPresenting: rootViewController) + #elseif os(macOS) + + guard let presentingWindow = NSApplication.shared.windows.first else { + print("There is no presenting window!") + viewModel.setLoginErrorMessage(String(localized: "There was an error logging in")) + return + } + + let signInResult = try await GIDSignIn.sharedInstance.signIn(withPresenting: presentingWindow) + #endif + + let createArgsResult = viewModel.createGoogleAuthLoginArgs(signInResult) + switch createArgsResult { + case .success(let args): + let result = await viewModel.authLogin(args: args) + await self.handleAuthLoginResult(result) + + case .failure(let error): + print("error create args result: \(error.localizedDescription)") + viewModel.setLoginErrorMessage(String(localized: "There was an error logging in")) + } + + } catch { + print("Error signing in: \(error.localizedDescription)") + viewModel.setLoginErrorMessage(String(localized: "There was an error logging in")) + } + + + } + private func handleAuthLoginResult(_ authLoginResult: AuthLoginResult) async { switch authLoginResult { @@ -489,18 +479,6 @@ struct LoginInitialView: View { } } - private func createUpgradeSolanaWalletArgs(_ args: SdkAuthLoginArgs) -> SdkUpgradeGuestExistingArgs { - let updateArgs = SdkUpgradeGuestExistingArgs() - updateArgs.walletAuth = args.walletAuth - return updateArgs - } - - /** - * Bittensor sign in: fetches a fresh server-issued challenge (same - * flow as Solana), then opens the ur.io/wallet-connect bridge; the - * wallet returns through onOpenURL with the ss58 address and sr25519 - * signature over that challenge. - */ private func handleBittensorSignIn() { Task { let ok = await viewModel.prepareBittensorChallenge() @@ -541,20 +519,9 @@ struct LoginInitialView: View { switch createArgsResult { case .success(let args): - if deviceManager.device != nil { - - let upgradeArgs = self.createUpgradeSolanaWalletArgs(args) - - let result = await guestUpgradeViewModel.linkGuestToExistingLogin(args: upgradeArgs) - - await self.handleAuthLoginResult(result) - viewModel.setIsSigningMessage(false) - - } else { - let result = await viewModel.authLogin(args: args) - await self.handleAuthLoginResult(result) - viewModel.setIsSigningMessage(false) - } + let result = await viewModel.authLogin(args: args) + await self.handleAuthLoginResult(result) + viewModel.setIsSigningMessage(false) case .failure(let error): print("error create args result: \(error.localizedDescription)") @@ -563,82 +530,6 @@ struct LoginInitialView: View { } } - private func createUpgradeExistingSocialArgs(_ args: SdkAuthLoginArgs) -> SdkUpgradeGuestExistingArgs { - let updateArgs = SdkUpgradeGuestExistingArgs() - updateArgs.authJwt = args.authJwt - updateArgs.authJwtType = args.authJwtType - return updateArgs - } - - private func handleGoogleSignInButton() async { - - guard viewModel.beginLoginAction(.google) else { - return - } - - defer { - viewModel.endLoginAction(.google) - } - - do { - #if os(iOS) - - guard let rootViewController = getRootViewController() else { - print("no root view controller found") - viewModel.setLoginErrorMessage(String(localized: "There was an error logging in")) - return - } - - let signInResult = try await GIDSignIn.sharedInstance.signIn(withPresenting: rootViewController) - #elseif os(macOS) - - guard let presentingWindow = NSApplication.shared.windows.first else { - print("There is no presenting window!") - viewModel.setLoginErrorMessage(String(localized: "There was an error logging in")) - return - } - - let signInResult = try await GIDSignIn.sharedInstance.signIn(withPresenting: presentingWindow) - #endif - - let createArgsResult = viewModel.createGoogleAuthLoginArgs(signInResult) - switch createArgsResult { - case .success(let args): - - if deviceManager.device != nil { - - // device exists, meaning we're in the guest flow - // link guest account to google account - // let result = await viewModel.linkGuestToExistingSocialLogin(args: args) - - let upgradeArgs = self.createUpgradeExistingSocialArgs(args) - - let result = await guestUpgradeViewModel.linkGuestToExistingLogin(args: upgradeArgs) - - await self.handleAuthLoginResult(result) - - } else { - - // login with google - let result = await viewModel.authLogin(args: args) - await self.handleAuthLoginResult(result) - - } - - case .failure(let error): - print("error create args result: \(error.localizedDescription)") - viewModel.setLoginErrorMessage(String(localized: "There was an error logging in")) - } - - } catch { - print("Error signing in: \(error.localizedDescription)") - viewModel.setLoginErrorMessage(String(localized: "There was an error logging in")) - } - - - - } - } private struct LoginInitialFormView: View { @@ -659,7 +550,9 @@ private struct LoginInitialFormView: View { let signInWithBittensor: () -> Void let presentAuthCodeLoginSheet: () -> Void - @Binding var presentGuestNetworkSheet: Bool + let presentSeedphraseLogin: () -> Void + let presentCreateInstant: () -> Void + @State private var presentNetworkServerSheet = false var body: some View { @@ -742,6 +635,8 @@ private struct LoginInitialFormView: View { presentSignInWithSolanaSheet: presentSignInWithSolanaSheet, signInWithBittensor: signInWithBittensor, presentAuthCodeLoginSheet: presentAuthCodeLoginSheet, + presentSeedphraseLogin: presentSeedphraseLogin, + presentCreateInstant: presentCreateInstant, activeLoginAction: activeLoginAction, isLoginActionInFlight: isLoginActionInFlight ) @@ -754,28 +649,54 @@ private struct LoginInitialFormView: View { Spacer() .frame(height: 24) - if !deviceExists { - // if a device exists, it means they are already in guest mode and trying to upgrade their account - // restrict access to create guest network from within authed guest network - + // Seedphrase login button + Button(action: presentSeedphraseLogin) { HStack { - Text("Commitment issues?") - .font(themeManager.currentTheme.bodyFont) - .foregroundColor(themeManager.currentTheme.textMutedColor) - - Button(action: { - presentGuestNetworkSheet = true - }) { - Text("Try Guest Mode") - .font(themeManager.currentTheme.bodyFont) - .foregroundColor(themeManager.currentTheme.textColor) - } - .buttonStyle(.plain) - .disabled(isLoginActionInFlight) - + Image(systemName: "key.fill") + .resizable() + .scaledToFit() + .frame(width: 16) + Spacer().frame(width: 8) + Text("Sign in with Seedphrase") + .foregroundColor(themeManager.currentTheme.inverseTextColor) + .font( + Font.system(size: 19, weight: .medium) + ) } - + .frame(maxWidth: .infinity) + } + .frame(height: 48) + .background(.white) + .clipShape(Capsule()) + .buttonStyle(.plain) + .opacity(isLoginActionInFlight ? 0.3 : 1) + .disabled(isLoginActionInFlight) + + Spacer() + .frame(height: 12) + + // Instant create account button + Button(action: presentCreateInstant) { + HStack { + Image(systemName: "bolt.fill") + .resizable() + .scaledToFit() + .frame(width: 16) + Spacer().frame(width: 8) + Text("Create Instant Account") + .foregroundColor(themeManager.currentTheme.inverseTextColor) + .font( + Font.system(size: 19, weight: .medium) + ) + } + .frame(maxWidth: .infinity) } + .frame(height: 48) + .background(.white) + .clipShape(Capsule()) + .buttonStyle(.plain) + .opacity(isLoginActionInFlight ? 0.3 : 1) + .disabled(isLoginActionInFlight) Spacer() .frame(height: 8) @@ -830,6 +751,8 @@ private struct SSOButtons: View { let presentSignInWithSolanaSheet: () -> Void let signInWithBittensor: () -> Void let presentAuthCodeLoginSheet: () -> Void + let presentSeedphraseLogin: () -> Void + let presentCreateInstant: () -> Void let activeLoginAction: LoginInitialView.LoginAction? let isLoginActionInFlight: Bool @@ -997,6 +920,8 @@ private struct SSOButtons: View { var presentSignInWithSolanaSheet: () -> Void var signInWithBittensor: () -> Void let presentAuthCodeLoginSheet: () -> Void + let presentSeedphraseLogin: () -> Void + let presentCreateInstant: () -> Void let activeLoginAction: LoginInitialView.LoginAction? let isLoginActionInFlight: Bool @@ -1114,10 +1039,57 @@ private struct SSOButtons: View { Spacer().frame(maxWidth: .infinity) } .frame(maxWidth: .infinity) + + Spacer() + .frame(height: 8) + // Seedphrase login button (macOS) + Button(action: presentSeedphraseLogin) { + HStack { + Image(systemName: "key.fill") + .resizable() + .scaledToFit() + .frame(width: 12) + Spacer().frame(width: 8) + Text("Sign in with Seedphrase") + .foregroundColor(themeManager.currentTheme.inverseTextColor) + .font(Font.system(size: 12, weight: .medium)) + } + .frame(maxWidth: .infinity) + } + .frame(height: 30) + .frame(maxWidth: .infinity) + .background(.white) + .cornerRadius(6) + .buttonStyle(.plain) + .disabled(isLoginActionInFlight) + .opacity(isLoginActionInFlight ? 0.3 : 1) + + Spacer().frame(width: 8) + // Instant create account button (macOS) + Button(action: presentCreateInstant) { + HStack { + Image(systemName: "bolt.fill") + .resizable() + .scaledToFit() + .frame(width: 12) + Spacer().frame(width: 8) + Text("Create Instant Account") + .foregroundColor(themeManager.currentTheme.inverseTextColor) + .font(Font.system(size: 12, weight: .medium)) + } + .frame(maxWidth: .infinity) + } + .frame(height: 30) + .frame(maxWidth: .infinity) + .background(.white) + .cornerRadius(6) + .buttonStyle(.plain) + .disabled(isLoginActionInFlight) + .opacity(isLoginActionInFlight ? 0.3 : 1) } - + } } #endif diff --git a/app/network/Authenticate/LoginInitial/LoginInitialViewModel.swift b/app/network/Authenticate/LoginInitial/LoginInitialViewModel.swift index c707061d..3e434fd2 100644 --- a/app/network/Authenticate/LoginInitial/LoginInitialViewModel.swift +++ b/app/network/Authenticate/LoginInitial/LoginInitialViewModel.swift @@ -72,18 +72,6 @@ extension LoginInitialView { loginErrorMessage = message } - /** - * Guest mode - */ - @Published private(set) var isCreatingGuestNetwork: Bool = false - @Published var presentGuestNetworkSheet: Bool = false - @Published var termsAgreed: Bool = false - @Published private(set) var guestNetworkErrorMessage: String? - - func setGuestNetworkErrorMessage(_ message: String?) -> Void { - guestNetworkErrorMessage = message - } - /** * Auth code login */ @@ -287,40 +275,6 @@ extension LoginInitialView.ViewModel { } -// MARK: create guest network -extension LoginInitialView.ViewModel { - - func createGuestNetwork() async -> LoginNetworkResult { - - if self.isCreatingGuestNetwork { - return .failure(LoginError.inProgress) - } - - self.isCreatingGuestNetwork = true - self.guestNetworkErrorMessage = nil - - do { - - let args = SdkNetworkCreateArgs() - args.terms = true - args.guestMode = true - - let result = try await urApiService.createNetwork(args) - - self.isCreatingGuestNetwork = false - - - return result - - } catch(let error) { - self.isCreatingGuestNetwork = false - return .failure(error) - } - - } - -} - // MARK: Solana Sign in extension LoginInitialView.ViewModel { func createSolanaAuthLoginArgs(message: String, signature: String, publicKey: String) -> Result { diff --git a/app/network/Authenticate/LoginNavigationView.swift b/app/network/Authenticate/LoginNavigationView.swift index d603197e..2d6fd4a6 100644 --- a/app/network/Authenticate/LoginNavigationView.swift +++ b/app/network/Authenticate/LoginNavigationView.swift @@ -12,8 +12,6 @@ struct LoginNavigationView: View { @StateObject private var viewModel = ViewModel() - @StateObject private var guestUpgradeViewModel: GuestUpgradeViewModel - @EnvironmentObject var themeManager: ThemeManager @EnvironmentObject var deviceManager: DeviceManager @@ -38,7 +36,6 @@ struct LoginNavigationView: View { self.api = api self.cancel = cancel self.handleSuccess = handleSuccess - _guestUpgradeViewModel = StateObject(wrappedValue: GuestUpgradeViewModel(api: api)) } var body: some View { @@ -49,8 +46,7 @@ struct LoginNavigationView: View { urApiService: urApiService, navigate: viewModel.navigate, cancel: cancel, - handleSuccess: handleSuccess, - guestUpgradeViewModel: guestUpgradeViewModel + handleSuccess: handleSuccess ) .id(deviceManager.activeHostName) .background(themeManager.currentTheme.backgroundColor.ignoresSafeArea()) @@ -61,7 +57,6 @@ struct LoginNavigationView: View { userAuth: userAuth, navigate: viewModel.navigate, handleSuccess: handleSuccess, - guestUpgradeViewModel: guestUpgradeViewModel, api: api ) .background(themeManager.currentTheme.backgroundColor.ignoresSafeArea()) @@ -89,6 +84,20 @@ struct LoginNavigationView: View { api: api ) .background(themeManager.currentTheme.backgroundColor.ignoresSafeArea()) + case .seedphrase: + LoginSeedphraseView( + urApiService: urApiService, + handleSuccess: handleSuccess, + back: viewModel.back + ) + .background(themeManager.currentTheme.backgroundColor.ignoresSafeArea()) + case .createInstant: + CreateNetworkInstantView( + urApiService: urApiService, + handleSuccess: handleSuccess, + back: viewModel.back + ) + .background(themeManager.currentTheme.backgroundColor.ignoresSafeArea()) } } } diff --git a/app/network/Authenticate/LoginNavigationViewModel.swift b/app/network/Authenticate/LoginNavigationViewModel.swift index 29260e74..a1f53261 100644 --- a/app/network/Authenticate/LoginNavigationViewModel.swift +++ b/app/network/Authenticate/LoginNavigationViewModel.swift @@ -14,6 +14,8 @@ enum LoginInitialNavigationPath: Hashable { case createNetwork(_ authLoginArgs: SdkAuthLoginArgs) case verify(_ userAuth: String) case resetPassword(_ userAuth: String) + case seedphrase + case createInstant } extension LoginNavigationView { diff --git a/app/network/Authenticate/LoginPassword/LoginPasswordView.swift b/app/network/Authenticate/LoginPassword/LoginPasswordView.swift index ca47c8fd..1c92fe2e 100644 --- a/app/network/Authenticate/LoginPassword/LoginPasswordView.swift +++ b/app/network/Authenticate/LoginPassword/LoginPasswordView.swift @@ -11,9 +11,7 @@ import URnetworkSdk struct LoginPasswordView: View { @EnvironmentObject var themeManager: ThemeManager - @EnvironmentObject var deviceManager: DeviceManager @StateObject private var viewModel: ViewModel - @ObservedObject var guestUpgradeViewModel: GuestUpgradeViewModel var userAuth: String var navigate: (LoginInitialNavigationPath) -> Void @@ -25,14 +23,12 @@ struct LoginPasswordView: View { userAuth: String, navigate: @escaping (LoginInitialNavigationPath) -> Void, handleSuccess: @escaping (_ jwt: String) async -> Void, - guestUpgradeViewModel: GuestUpgradeViewModel, api: SdkApi? ) { _viewModel = StateObject(wrappedValue: ViewModel(api: api)) self.userAuth = userAuth self.navigate = navigate self.handleSuccess = handleSuccess - self.guestUpgradeViewModel = guestUpgradeViewModel } var body: some View { @@ -88,7 +84,6 @@ struct LoginPasswordView: View { }, enabled: !isLoginInProgress && viewModel.isValid, isProcessing: isLoginInProgress - // todo add icon ) Spacer().frame(height: 8) @@ -123,7 +118,7 @@ struct LoginPasswordView: View { } private var isLoginInProgress: Bool { - viewModel.isLoggingIn || guestUpgradeViewModel.isUpgrading + viewModel.isLoggingIn } private func submitPasswordLogin() async { @@ -133,45 +128,8 @@ struct LoginPasswordView: View { viewModel.setErrorMessage(nil) - if deviceManager.device != nil { - // in guest mode; merge user auth account and login - let args = SdkUpgradeGuestExistingArgs() - args.userAuth = self.userAuth - args.password = self.viewModel.password - - let result = await guestUpgradeViewModel.linkGuestToExistingLogin(args: args) - await handleUpgradeLoginResult(result) - } else { - let result = await viewModel.loginWithPassword(userAuth: self.userAuth) - await handleLoginResult(result) - } - } - - private func handleUpgradeLoginResult(_ result: AuthLoginResult) async { - switch result { - - case .login(let authJwt): - await handleSuccess(authJwt) - break - - case .failure(let error): - print("auth login error: \(error.localizedDescription)") - viewModel.setIsLoggingIn(false) - viewModel.setErrorMessage(snackbarErrorMessage) - break - - case .verificationRequired(let userAuth): - navigate(.verify(userAuth)) - viewModel.setIsLoggingIn(false) - break - - default: - viewModel.setIsLoggingIn(false) - viewModel.setErrorMessage(snackbarErrorMessage) - print("upgrade login result does not match any case") - return - - } + let result = await viewModel.loginWithPassword(userAuth: self.userAuth) + await handleLoginResult(result) } private func handleLoginResult(_ result: LoginNetworkResult) async { @@ -179,7 +137,6 @@ struct LoginPasswordView: View { case .successWithJwt(let jwt): await handleSuccess(jwt) - // viewModel.setIsLoggingIn(false) break case .successWithVerificationRequired: diff --git a/app/network/Authenticate/LoginSeedphrase/LoginSeedphraseView.swift b/app/network/Authenticate/LoginSeedphrase/LoginSeedphraseView.swift new file mode 100644 index 00000000..44923b83 --- /dev/null +++ b/app/network/Authenticate/LoginSeedphrase/LoginSeedphraseView.swift @@ -0,0 +1,125 @@ +// +// LoginSeedphraseView.swift +// URnetwork +// + +import SwiftUI +import URnetworkSdk + +struct LoginSeedphraseView: View { + + @EnvironmentObject var themeManager: ThemeManager + + @StateObject private var viewModel: ViewModel + + let handleSuccess: (_ jwt: String) async -> Void + let back: () -> Void + + init( + urApiService: UrApiServiceProtocol, + handleSuccess: @escaping (_ jwt: String) async -> Void, + back: @escaping () -> Void + ) { + _viewModel = StateObject(wrappedValue: ViewModel(urApiService: urApiService)) + self.handleSuccess = handleSuccess + self.back = back + } + + var body: some View { + ScrollView { + VStack(alignment: .center) { + + Text("Sign in with Seedphrase") + .foregroundColor(.urWhite) + .font(themeManager.currentTheme.titleFont) + + Spacer().frame(height: 48) + + TextEditor(text: $viewModel.seedphrase) + .font(.system(.body, design: .monospaced)) + .frame(minHeight: 160) + .padding(12) + .overlay( + RoundedRectangle(cornerRadius: 8) + .stroke(themeManager.currentTheme.textMutedColor.opacity(0.3), lineWidth: 1) + ) + .overlay(alignment: .topLeading) { + if viewModel.seedphrase.isEmpty { + Text("Paste your 24-word seedphrase here") + .foregroundColor(themeManager.currentTheme.textMutedColor) + .font(themeManager.currentTheme.bodyFont) + .padding(.horizontal, 16) + .padding(.vertical, 20) + .allowsHitTesting(false) + } + } + .cornerRadius(8) + + Spacer().frame(height: 32) + + UrButton( + text: "Sign In", + action: { + Task { + let result = await viewModel.login() + await handleResult(result) + } + }, + enabled: viewModel.isSeedphraseValid && !viewModel.isLoggingIn, + isProcessing: viewModel.isLoggingIn + ) + + Spacer().frame(height: 8) + + if let wordCountWarning = viewModel.wordCountWarning { + Text(wordCountWarning) + .foregroundColor(.urYellow) + .font(themeManager.currentTheme.secondaryBodyFont) + .multilineTextAlignment(.center) + .padding(.horizontal) + + Spacer().frame(height: 8) + } + + UrInlineErrorText(message: viewModel.errorMessage) + + } + .padding() + .frame(maxWidth: .infinity) + .frame(maxWidth: 400) + } + .background(themeManager.currentTheme.backgroundColor.ignoresSafeArea()) + .toolbar { + #if os(iOS) + ToolbarItem(placement: .navigationBarLeading) { + Button(action: { back() }) { + Image(systemName: "chevron.left") + .foregroundColor(themeManager.currentTheme.textColor) + } + } + #elseif os(macOS) + ToolbarItem { + Button(action: { back() }) { + Image(systemName: "chevron.left") + .foregroundColor(themeManager.currentTheme.textColor) + } + } + #endif + } + #if os(iOS) + .navigationBarBackButtonHidden(true) + #endif + } + + private func handleResult(_ result: AuthLoginResult) async { + switch result { + case .login(let jwt): + await handleSuccess(jwt) + case .failure(let error): + viewModel.errorMessage = error.localizedDescription + default: + viewModel.errorMessage = "There was an error signing in with your seedphrase. Please try again." + } + } + +} diff --git a/app/network/Authenticate/LoginSeedphrase/LoginSeedphraseViewModel.swift b/app/network/Authenticate/LoginSeedphrase/LoginSeedphraseViewModel.swift new file mode 100644 index 00000000..a5ac278d --- /dev/null +++ b/app/network/Authenticate/LoginSeedphrase/LoginSeedphraseViewModel.swift @@ -0,0 +1,99 @@ +// +// LoginSeedphraseViewModel.swift +// URnetwork +// + +import Foundation +import URnetworkSdk + +extension LoginSeedphraseView { + + @MainActor + class ViewModel: ObservableObject { + + private let urApiService: UrApiServiceProtocol + + @Published var seedphrase: String = "" { + didSet { + errorMessage = nil + } + } + + @Published private(set) var wordCountWarning: String? + + @Published private(set) var isLoggingIn: Bool = false + + @Published var errorMessage: String? + + let domain = "LoginSeedphraseViewModel" + + init(urApiService: UrApiServiceProtocol) { + self.urApiService = urApiService + } + + func setErrorMessage(_ message: String?) { + errorMessage = message + } + + var isSeedphraseValid: Bool { + wordCount == 12 || wordCount == 24 + } + + var normalizedSeedphrase: String { + seedphrase + .lowercased() + .trimmingCharacters(in: .whitespacesAndNewlines) + .components(separatedBy: .whitespacesAndNewlines) + .filter { !$0.isEmpty } + .joined(separator: " ") + } + + var wordCount: Int { + normalizedSeedphrase.isEmpty ? 0 : normalizedSeedphrase.components(separatedBy: " ").count + } + + func validateWordCount() -> Bool { + let count = wordCount + if count == 12 || count == 24 { + wordCountWarning = nil + return true + } + if count > 0 { + wordCountWarning = "Seedphrase should be 12 or 24 words (you entered \(count))" + } else { + wordCountWarning = nil + } + return false + } + + func login() async -> AuthLoginResult { + + if isLoggingIn { + return .failure(NSError(domain: domain, code: 0, userInfo: [NSLocalizedDescriptionKey: "Login already in progress"])) + } + + guard isSeedphraseValid else { + return .failure(NSError(domain: domain, code: 0, userInfo: [NSLocalizedDescriptionKey: "Seedphrase should be 12 or 24 words"])) + } + + validateWordCount() + + isLoggingIn = true + errorMessage = nil + + defer { + isLoggingIn = false + } + + do { + let result = try await urApiService.loginWithSeedphrase(seedphrase: normalizedSeedphrase) + return result + } catch { + return .failure(error) + } + + } + + } + +} diff --git a/app/network/Main/Account/AccountNavStackView.swift b/app/network/Main/Account/AccountNavStackView.swift index 1aa85852..d32c2bce 100644 --- a/app/network/Main/Account/AccountNavStackView.swift +++ b/app/network/Main/Account/AccountNavStackView.swift @@ -105,7 +105,16 @@ struct AccountNavStackView: View { api: api, back: viewModel.back, networkName: networkName, - userAuth: networkUserViewModel.networkUser?.userAuth + userAuth: networkUserViewModel.networkUser?.userAuth, + needsNameClaim: { + guard let networkUser = networkUserViewModel.networkUser else { return false } + let hasIdentityMethod = authTypesContains(networkUser.authTypes, "email") + || authTypesContains(networkUser.authTypes, "phone") + || authTypesContains(networkUser.authTypes, "google") + || authTypesContains(networkUser.authTypes, "apple") + || authTypesContains(networkUser.authTypes, "solana") + return !hasIdentityMethod + }() ) .background(themeManager.currentTheme.backgroundColor) .navigationTitle("Profile") @@ -119,6 +128,7 @@ struct AccountNavStackView: View { accountWalletsViewModel: accountWalletsViewModel, navigate: viewModel.navigate, providerCountries: providerCountries, + networkUserViewModel: networkUserViewModel ) .background(themeManager.currentTheme.backgroundColor.ignoresSafeArea()) .navigationTitle("Settings") diff --git a/app/network/Main/Account/Profile/ProfileView.swift b/app/network/Main/Account/Profile/ProfileView.swift index 77fdeae4..62bb3685 100644 --- a/app/network/Main/Account/Profile/ProfileView.swift +++ b/app/network/Main/Account/Profile/ProfileView.swift @@ -18,42 +18,107 @@ struct ProfileView: View { var back: () -> Void var networkName: String? var userAuth: String? - - init(api: SdkApi, back: @escaping () -> Void, networkName: String?, userAuth: String?) { + /// True when the account has no verified identity method (email/Google/Apple) + /// bound yet, so it's still on its auto-generated name and needs the + /// claim-name flow (no reclaim cooldown on the old name) rather than + /// change-name (which applies a 24h cooldown to protect the old name). + var needsNameClaim: Bool + + init(api: SdkApi, back: @escaping () -> Void, networkName: String?, userAuth: String?, needsNameClaim: Bool = false) { _viewModel = StateObject.init(wrappedValue: ViewModel( api: api )) self.back = back self.userAuth = userAuth self.networkName = networkName + self.needsNameClaim = needsNameClaim } var body: some View { VStack { - -// HStack { -// Text("Profile") -// .font(themeManager.currentTheme.titleFont) -// .foregroundColor(themeManager.currentTheme.textColor) -// -// Spacer() -// } -// -// Spacer().frame(height: 64) - + HStack { UrLabel(text: "Network name") Spacer() } - HStack { - Text(networkName ?? "") - .font(themeManager.currentTheme.bodyFont) - .foregroundColor(themeManager.currentTheme.textColor) + if viewModel.isEditingNetworkName { + // Editable view + HStack { + TextField("Enter network name", text: $viewModel.editedNetworkName) + .textFieldStyle(.plain) + .font(themeManager.currentTheme.bodyFont) + .foregroundColor(themeManager.currentTheme.textColor) + .autocorrectionDisabled() + #if os(iOS) + .autocapitalization(.none) + #endif + } + .padding() + .background(themeManager.currentTheme.tintedBackgroundBase) + .cornerRadius(8) - Spacer() + if let error = viewModel.networkNameError { + Text(error) + .font(themeManager.currentTheme.secondaryBodyFont) + .foregroundColor(.red) + } + + HStack(spacing: 12) { + UrButton( + text: "Save", + action: { + Task { + if needsNameClaim { + let result = await viewModel.claimNetworkName() + handleNetworkNameResult(result) + } else { + let result = await viewModel.saveNetworkName() + handleNetworkNameResult(result) + } + } + }, + enabled: !viewModel.isSavingNetworkName && !viewModel.editedNetworkName.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty, + isProcessing: viewModel.isSavingNetworkName + ) + + Button(action: { + viewModel.cancelEditingNetworkName() + }) { + Text("Cancel") + } + } + } else { + // Read-only view + HStack { + Text(networkName ?? "") + .font(themeManager.currentTheme.bodyFont) + .foregroundColor(themeManager.currentTheme.textColor) + + Spacer() + + Button(action: { + viewModel.startEditingNetworkName(currentName: networkName ?? "") + }) { + Image(systemName: "pencil") + .foregroundColor(themeManager.currentTheme.textMutedColor) + } + } + + HStack { + if needsNameClaim { + Text("Claim a custom network name to replace your auto-generated one") + .font(themeManager.currentTheme.secondaryBodyFont) + .foregroundColor(themeManager.currentTheme.textMutedColor) + } else { + Text("Tap the edit icon to change your network name") + .font(themeManager.currentTheme.secondaryBodyFont) + .foregroundColor(themeManager.currentTheme.textMutedColor) + } + Spacer() + } } Spacer().frame(height: 32) @@ -97,6 +162,25 @@ struct ProfileView: View { snackbarManager.showSnackbar(message: String(localized: "Error sending password reset link")) } } + + private func handleNetworkNameResult(_ result: Result) { + switch result { + case .success(let newName): + snackbarManager.showSnackbar(message: String(localized: "Network name changed to \(newName)")) + // The displayed network name comes from the cached JWT + // (deviceManager.parsedJwt?.networkName), which isn't reissued by + // this call — force a refresh so the new name propagates to this + // screen and anywhere else the JWT-derived name is shown. + do { + try deviceManager.device?.refreshToken(0) + } catch { + print("Error refreshing JWT after network name change: \(error)") + } + case .failure(let error): + print("Error changing network name: \(error)") + snackbarManager.showSnackbar(message: String(localized: "Error changing network name")) + } + } } #Preview { diff --git a/app/network/Main/Account/Profile/ProfileViewModel.swift b/app/network/Main/Account/Profile/ProfileViewModel.swift index 3eb0567c..b1432155 100644 --- a/app/network/Main/Account/Profile/ProfileViewModel.swift +++ b/app/network/Main/Account/Profile/ProfileViewModel.swift @@ -18,6 +18,11 @@ extension ProfileView { @Published private(set) var isSendingPasswordResetLink: Bool = false @Published private(set) var sendPasswordResetLinkError: String? + @Published var isEditingNetworkName: Bool = false + @Published var editedNetworkName: String = "" + @Published private(set) var isSavingNetworkName: Bool = false + @Published private(set) var networkNameError: String? + init(api: SdkApi) { self.api = api } @@ -70,6 +75,116 @@ extension ProfileView { } + func startEditingNetworkName(currentName: String) { + editedNetworkName = currentName + isEditingNetworkName = true + networkNameError = nil + } + + func saveNetworkName() async -> Result { + + let name = editedNetworkName.trimmingCharacters(in: .whitespacesAndNewlines) + guard !name.isEmpty else { + return .failure(NetworkNameError.empty) + } + + isSavingNetworkName = true + networkNameError = nil + + do { + let result: SdkChangeNetworkNameResult = try await withCheckedThrowingContinuation { continuation in + + let callback = ChangeNetworkNameCallback { result, err in + + if let err = err { + continuation.resume(throwing: err) + return + } + + guard let result = result else { + continuation.resume(throwing: NetworkNameError.resultNil) + return + } + + if let errMsg = result.error?.message { + continuation.resume(throwing: NSError(domain: "ProfileViewModel", code: -1, userInfo: [NSLocalizedDescriptionKey: errMsg])) + return + } + + continuation.resume(returning: result) + } + + let args = SdkChangeNetworkNameArgs() + args.newName = name + api.changeNetworkName(args, callback: callback) + } + + self.isSavingNetworkName = false + self.isEditingNetworkName = false + return .success(result.networkName) + + } catch(let error) { + self.isSavingNetworkName = false + self.networkNameError = error.localizedDescription + return .failure(error) + } + } + + func claimNetworkName() async -> Result { + + let name = editedNetworkName.trimmingCharacters(in: .whitespacesAndNewlines) + guard !name.isEmpty else { + return .failure(NetworkNameError.empty) + } + + isSavingNetworkName = true + networkNameError = nil + + do { + let result: SdkClaimNetworkNameResult = try await withCheckedThrowingContinuation { continuation in + + let callback = ClaimNetworkNameCallback { result, err in + + if let err = err { + continuation.resume(throwing: err) + return + } + + guard let result = result else { + continuation.resume(throwing: NetworkNameError.resultNil) + return + } + + if let errMsg = result.error?.message { + continuation.resume(throwing: NSError(domain: "ProfileViewModel", code: -1, userInfo: [NSLocalizedDescriptionKey: errMsg])) + return + } + + continuation.resume(returning: result) + } + + let args = SdkClaimNetworkNameArgs() + args.newName = name + api.claimNetworkName(args, callback: callback) + } + + self.isSavingNetworkName = false + self.isEditingNetworkName = false + return .success(result.networkName) + + } catch(let error) { + self.isSavingNetworkName = false + self.networkNameError = error.localizedDescription + return .failure(error) + } + } + + func cancelEditingNetworkName() { + isEditingNetworkName = false + editedNetworkName = "" + networkNameError = nil + } + } } @@ -79,8 +194,25 @@ enum SendPasswordResetLinkError: Error { case resultInvalid } +enum NetworkNameError: Error { + case empty + case resultNil +} + private class AuthPasswordResetCallback: SdkCallback, SdkAuthPasswordResetCallbackProtocol { func result(_ result: SdkAuthPasswordResetResult?, err: Error?) { handleResult(result, err: err) } } + +private class ChangeNetworkNameCallback: SdkCallback, SdkChangeNetworkNameCallbackProtocol { + func result(_ result: SdkChangeNetworkNameResult?, err: Error?) { + handleResult(result, err: err) + } +} + +private class ClaimNetworkNameCallback: SdkCallback, SdkClaimNetworkNameCallbackProtocol { + func result(_ result: SdkClaimNetworkNameResult?, err: Error?) { + handleResult(result, err: err) + } +} diff --git a/app/network/Main/Account/Settings/AddAuthSheet.swift b/app/network/Main/Account/Settings/AddAuthSheet.swift new file mode 100644 index 00000000..b7225b40 --- /dev/null +++ b/app/network/Main/Account/Settings/AddAuthSheet.swift @@ -0,0 +1,533 @@ +// +// AddAuthSheet.swift +// URnetwork +// + +import SwiftUI +import URnetworkSdk +import AuthenticationServices +import GoogleSignIn + +struct AddAuthSheet: View { + + @EnvironmentObject var themeManager: ThemeManager + @EnvironmentObject var snackbarManager: UrSnackbarManager + @EnvironmentObject var connectWalletProviderViewModel: ConnectWalletProviderViewModel + + let api: UrApiServiceProtocol + let networkUserViewModel: NetworkUserViewModel? + + @State private var email: String = "" + @State private var password: String = "" + @State private var isAdding: Bool = false + @State private var selectedMethod: String = "email" + @State private var addError: String? + @State private var walletConnectionTask: Task? + + var body: some View { + NavigationStack { + ScrollView { + VStack(alignment: .leading, spacing: 16) { + + Text("Add a sign-in method") + .font(themeManager.currentTheme.titleFont) + .foregroundColor(themeManager.currentTheme.textColor) + + Text("Link another way to sign in to your account.") + .font(themeManager.currentTheme.secondaryBodyFont) + .foregroundColor(themeManager.currentTheme.textMutedColor) + + Spacer().frame(height: 16) + + Picker("Method", selection: $selectedMethod) { + Text("Apple").tag("apple") + Text("Google").tag("google") + Text("Wallet").tag("wallet") + Text("Email").tag("email") + Text("Seedphrase").tag("seedphrase") + } + .pickerStyle(.menu) + + if selectedMethod == "apple" { + appleSignInView + } else if selectedMethod == "google" { + googleSignInView + } else if selectedMethod == "wallet" { + walletSignInView + } else if selectedMethod == "email" { + emailFields + } else if selectedMethod == "seedphrase" { + Text("A new seedphrase will be generated and linked to your account.") + .font(themeManager.currentTheme.secondaryBodyFont) + .foregroundColor(themeManager.currentTheme.textMutedColor) + } + + if let error = addError { + Text(error) + .font(themeManager.currentTheme.secondaryBodyFont) + .foregroundColor(.red) + } + + if selectedMethod == "email" || selectedMethod == "seedphrase" { + Spacer().frame(height: 16) + + UrButton( + text: "Add Sign-In Method", + action: { + Task { + await addAuth() + } + }, + enabled: !isAdding && formValid, + isProcessing: isAdding + ) + } + } + .padding() + } + .background(themeManager.currentTheme.backgroundColor.ignoresSafeArea()) + .toolbar { + ToolbarItem(placement: .confirmationAction) { + Button("Done") { + dismiss() + } + } + } + .onDisappear { + walletConnectionTask?.cancel() + walletConnectionTask = nil + connectWalletProviderViewModel.pendingAddAuthSignatureHandler = nil + connectWalletProviderViewModel.pendingWalletAuthMessage = nil + } + } + } + + @Environment(\.dismiss) private var dismiss + + private var formValid: Bool { + switch selectedMethod { + case "email": + return !email.isEmpty && password.count >= 12 + default: + return true + } + } + + // MARK: - Apple Sign-In + + private var appleSignInView: some View { + VStack(alignment: .leading, spacing: 12) { + Text("Sign in with your Apple ID to add it as a sign-in method.") + .font(themeManager.currentTheme.secondaryBodyFont) + .foregroundColor(themeManager.currentTheme.textMutedColor) + + #if os(iOS) + SignInWithAppleButton(.signIn) { request in + request.requestedScopes = [.email] + } onCompletion: { result in + Task { + await handleAppleResult(result) + } + } + .signInWithAppleButtonStyle(.white) + .frame(height: 50) + .cornerRadius(8) + #else + Text("Apple Sign-In is available on iOS.") + .font(themeManager.currentTheme.secondaryBodyFont) + .foregroundColor(themeManager.currentTheme.textMutedColor) + #endif + } + } + + // MARK: - Google Sign-In + + private var googleSignInView: some View { + VStack(alignment: .leading, spacing: 12) { + Text("Sign in with Google to add it as a sign-in method.") + .font(themeManager.currentTheme.secondaryBodyFont) + .foregroundColor(themeManager.currentTheme.textMutedColor) + + UrGoogleSignInButton( + action: { + await handleGoogleSignIn() + } + ) + } + } + + // MARK: - Wallet Sign-In + + @State private var walletStep: WalletStep = .disconnected + + enum WalletStep: Equatable { + case disconnected + case connecting + case connected(String) // publicKey + case signing + + var isConnected: Bool { + if case .connected = self { return true } + return false + } + } + + @State private var walletChallengeMessage: String = "" + + private var walletSignInView: some View { + VStack(alignment: .leading, spacing: 12) { + Text("Connect a Solana wallet (Phantom or Solflare) to add it as a sign-in method.") + .font(themeManager.currentTheme.secondaryBodyFont) + .foregroundColor(themeManager.currentTheme.textMutedColor) + + switch walletStep { + case .disconnected: + HStack(spacing: 12) { + Button(action: { + walletConnectionTask = Task { + await connectWallet(.phantom) + } + }) { + VStack { + Image("phantom.white.logo") + .resizable() + .aspectRatio(contentMode: .fit) + .frame(width: 36, height: 36) + .padding() + .background(Color(hex: "#ab9ff2")) + .cornerRadius(12) + Text("Phantom") + .font(themeManager.currentTheme.secondaryBodyFont) + .foregroundColor(themeManager.currentTheme.textColor) + } + } + .buttonStyle(.plain) + .disabled(isAdding || walletStep == .connecting) + + Button(action: { + walletConnectionTask = Task { + await connectWallet(.solflare) + } + }) { + VStack { + Image("solflare.logo") + .resizable() + .aspectRatio(contentMode: .fit) + .frame(width: 36, height: 36) + .padding() + .background(.urWhite) + .cornerRadius(12) + Text("Solflare") + .font(themeManager.currentTheme.secondaryBodyFont) + .foregroundColor(themeManager.currentTheme.textColor) + } + } + .buttonStyle(.plain) + .disabled(isAdding || walletStep == .connecting) + } + + case .connecting: + HStack { + ProgressView() + Text("Connecting to wallet...") + .font(themeManager.currentTheme.secondaryBodyFont) + .foregroundColor(themeManager.currentTheme.textMutedColor) + } + + case .connected(let publicKey): + HStack { + Image(systemName: "wallet.pass.fill") + .foregroundColor(.green) + Text("Connected: \(publicKey.prefix(8))...") + .font(themeManager.currentTheme.secondaryBodyFont) + } + + if !walletChallengeMessage.isEmpty { + UrButton( + text: "Sign with Wallet", + action: { + Task { + await signWalletChallenge() + } + }, + enabled: !isAdding && walletStep.isConnected, + isProcessing: isAdding + ) + } + + case .signing: + HStack { + ProgressView() + Text("Waiting for wallet signature...") + .font(themeManager.currentTheme.secondaryBodyFont) + .foregroundColor(themeManager.currentTheme.textMutedColor) + } + } + } + } + + private func connectWallet(_ provider: ConnectedWalletProvider) async { + isAdding = true + addError = nil + walletStep = .connecting + + // Set up the pending handler for when wallet signs back + connectWalletProviderViewModel.pendingAddAuthSignatureHandler = { [self] publicKey, signature in + Task { @MainActor in + await completeWalletAuth(publicKey: publicKey, signature: signature) + } + } + + // Fetch the challenge first before connecting + do { + let challengeArgs = SdkAuthWalletChallengeArgs() + challengeArgs.blockchain = "solana" + let challengeResult = try await api.authWalletChallenge(challengeArgs) + self.walletChallengeMessage = challengeResult.messageTemplate + connectWalletProviderViewModel.pendingWalletAuthMessage = challengeResult.messageTemplate + } catch { + addError = "Failed to get wallet challenge: \(error.localizedDescription)" + isAdding = false + walletStep = .disconnected + return + } + + // Check if already connected + if connectWalletProviderViewModel.connectedPublicKey != nil { + walletStep = .connected(connectWalletProviderViewModel.connectedPublicKey!) + isAdding = false + return + } + + // Open wallet connection + let opened: Bool + switch provider { + case .phantom: + opened = connectWalletProviderViewModel.connectPhantomWallet() + case .solflare: + opened = connectWalletProviderViewModel.connectSolflareWallet() + case .bittensor: + opened = false + @unknown default: + opened = false + } + + if !opened { + addError = "Could not open wallet. Please install it and try again." + isAdding = false + walletStep = .disconnected + return + } + + // We'll wait for the connect deep link to fire + // The Sheet's onAppear sets up a polling timer + await pollForWalletConnection() + } + + private func pollForWalletConnection() async { + // Poll for up to 60 seconds for the wallet to connect back + for _ in 0..<60 { + if Task.isCancelled { return } + if let pk = connectWalletProviderViewModel.connectedPublicKey { + walletStep = .connected(pk) + isAdding = false + return + } + try? await Task.sleep(nanoseconds: 1_000_000_000) // 1 second + if Task.isCancelled { return } + } + addError = "Wallet connection timed out. Please try again." + isAdding = false + walletStep = .disconnected + } + + private func signWalletChallenge() async { + isAdding = true + addError = nil + walletStep = .signing + + guard let provider = connectWalletProviderViewModel.connectedWalletProvider else { + addError = "Wallet not connected" + isAdding = false + walletStep = .disconnected + return + } + + let message = walletChallengeMessage + let didStartSigning: Bool + switch provider { + case .phantom: + didStartSigning = connectWalletProviderViewModel.signMessagePhantom(message: message) + case .solflare: + didStartSigning = connectWalletProviderViewModel.signMessageSolflare(message: message) + case .bittensor: + didStartSigning = false + @unknown default: + didStartSigning = false + } + + if !didStartSigning { + addError = "Failed to start wallet signing" + isAdding = false + walletStep = .connected(connectWalletProviderViewModel.connectedPublicKey ?? "") + } + // The pendingAddAuthSignatureHandler will complete the flow when the signature comes back + } + + private func completeWalletAuth(publicKey: String, signature: String) async { + do { + let args = SdkAddAuthArgs() + let walletAuth = SdkWalletAuthArgs() + walletAuth.blockchain = SdkSOL + walletAuth.publicKey = publicKey + walletAuth.message = walletChallengeMessage + walletAuth.signature = signature + args.walletAuth = walletAuth + + let _ = try await api.addAuth(args) + isAdding = false + walletStep = .disconnected + connectWalletProviderViewModel.pendingAddAuthSignatureHandler = nil + connectWalletProviderViewModel.pendingWalletAuthMessage = nil + _ = await networkUserViewModel?.refreshNetworkUser() + snackbarManager.showSnackbar(message: String(localized: "Wallet sign-in method added")) + dismiss() + } catch(let error) { + isAdding = false + addError = error.localizedDescription + walletStep = .disconnected + connectWalletProviderViewModel.pendingAddAuthSignatureHandler = nil + connectWalletProviderViewModel.pendingWalletAuthMessage = nil + } + } + + // MARK: - Email Fields + + private var emailFields: some View { + VStack(alignment: .leading, spacing: 12) { + UrTextField( + text: $email, + label: "Email", + placeholder: "your@email.com", + disableCapitalization: true + ) + + UrTextField( + text: $password, + label: "Password", + placeholder: "Enter a password", + isSecure: true + ) + + Text("Password must be at least 12 characters") + .font(themeManager.currentTheme.secondaryBodyFont) + .foregroundColor(themeManager.currentTheme.textMutedColor) + } + } + + // MARK: - Actions + + private func handleAppleResult(_ result: Result) async { + isAdding = true + addError = nil + + do { + let authResult = try result.get() + guard let credential = authResult.credential as? ASAuthorizationAppleIDCredential, + let idToken = credential.identityToken, + let idTokenString = String(data: idToken, encoding: .utf8) else { + addError = "Could not read Apple ID token" + isAdding = false + return + } + + let args = SdkAddAuthArgs() + args.authJwt = idTokenString + args.authJwtType = "apple" + + let _ = try await api.addAuth(args) + isAdding = false + _ = await networkUserViewModel?.refreshNetworkUser() + snackbarManager.showSnackbar(message: String(localized: "Apple sign-in method added")) + dismiss() + } catch(let error) { + isAdding = false + addError = error.localizedDescription + } + } + + private func handleGoogleSignIn() async { + isAdding = true + addError = nil + + do { + #if os(iOS) + guard let rootViewController = UIApplication.shared.connectedScenes + .compactMap({ $0 as? UIWindowScene }) + .first?.keyWindow? + .rootViewController else { + addError = "Could not get root view controller" + isAdding = false + return + } + let signInResult = try await GIDSignIn.sharedInstance.signIn(withPresenting: rootViewController) + #elseif os(macOS) + guard let presentingWindow = NSApplication.shared.windows.first else { + addError = "Could not get presenting window" + isAdding = false + return + } + let signInResult = try await GIDSignIn.sharedInstance.signIn(withPresenting: presentingWindow) + #endif + + guard let idTokenString = signInResult.user.idToken?.tokenString else { + addError = "Could not get Google ID token" + isAdding = false + return + } + + let args = SdkAddAuthArgs() + args.authJwt = idTokenString + args.authJwtType = "google" + + let _ = try await api.addAuth(args) + isAdding = false + _ = await networkUserViewModel?.refreshNetworkUser() + snackbarManager.showSnackbar(message: String(localized: "Google sign-in method added")) + dismiss() + } catch(let error) { + isAdding = false + addError = error.localizedDescription + } + } + + private func addAuth() async { + isAdding = true + addError = nil + + do { + let args = SdkAddAuthArgs() + + switch selectedMethod { + case "email": + args.userAuth = email + args.password = password + case "seedphrase": + // Seedphrase is generated server-side when no auth fields are set + // Just call addAuth with empty args to trigger seedphrase linking + break + default: + break + } + + let _ = try await api.addAuth(args) + isAdding = false + _ = await networkUserViewModel?.refreshNetworkUser() + snackbarManager.showSnackbar(message: String(localized: "Sign-in method added successfully")) + dismiss() + } catch(let error) { + isAdding = false + addError = error.localizedDescription + } + } +} diff --git a/app/network/Main/Account/Settings/AuthMethods.swift b/app/network/Main/Account/Settings/AuthMethods.swift new file mode 100644 index 00000000..18a7f0e0 --- /dev/null +++ b/app/network/Main/Account/Settings/AuthMethods.swift @@ -0,0 +1,57 @@ +// +// AuthMethods.swift +// URnetwork +// + +import URnetworkSdk + +// Shared by SettingsForm-iOS.swift and SettingsForm-macOS.swift so both +// platforms read auth-method state identically. + +func authTypesContains(_ authTypes: SdkStringList?, _ method: String) -> Bool { + guard let authTypes else { return false } + for i in 0.. [String] { + var methods: [String] = [] + + // Read from the new auth_types array returned by GET /network/user + if let authTypes = networkUser.authTypes, authTypes.len() > 0 { + for i in 0.. String { + switch method { + case "email": return "Email" + case "google": return "Google" + case "apple": return "Apple" + case "solana": return "Solana Wallet" + case "seedphrase": return "Seedphrase" + default: return method.capitalized + } +} diff --git a/app/network/Main/Account/Settings/SettingsForm-iOS.swift b/app/network/Main/Account/Settings/SettingsForm-iOS.swift index 69f95e3b..f09f1c4e 100644 --- a/app/network/Main/Account/Settings/SettingsForm-iOS.swift +++ b/app/network/Main/Account/Settings/SettingsForm-iOS.swift @@ -35,6 +35,9 @@ struct SettingsForm_iOS: View { @Binding var canReceiveNotifications: Bool @Binding var canReceiveProductUpdates: Bool + let networkUserViewModel: NetworkUserViewModel? + let viewModel: SettingsView.ViewModel + var body: some View { Form { @@ -135,6 +138,98 @@ struct SettingsForm_iOS: View { } } + // MARK: - Seedphrase Management + + Section("Seedphrase") { + VStack(alignment: .leading, spacing: 8) { + if let networkUser = networkUserViewModel?.networkUser { + let hasSeedphrase = authTypesContains(networkUser.authTypes, "seedphrase") + if hasSeedphrase { + Button(action: { + viewModel.confirmRegenerateSeedphrase() + }) { + HStack { + Text("Regenerate Seedphrase") + .font(themeManager.currentTheme.bodyFont) + Spacer() + if viewModel.isRegeneratingSeedphrase { + ProgressView() + } + } + } + .disabled(viewModel.isRegeneratingSeedphrase) + } else { + Button(action: { + viewModel.confirmGenerateSeedphrase() + }) { + HStack { + Text("Generate Seedphrase") + .font(themeManager.currentTheme.bodyFont) + Spacer() + if viewModel.isGeneratingSeedphrase { + ProgressView() + } + } + } + .disabled(viewModel.isGeneratingSeedphrase) + } + } else { + Button(action: { + viewModel.confirmGenerateSeedphrase() + }) { + HStack { + Text("Generate Seedphrase") + .font(themeManager.currentTheme.bodyFont) + Spacer() + if viewModel.isGeneratingSeedphrase { + ProgressView() + } + } + } + .disabled(viewModel.isGeneratingSeedphrase) + } + + Text("A seedphrase lets you recover your account if you lose access.") + .font(themeManager.currentTheme.secondaryBodyFont) + .foregroundColor(themeManager.currentTheme.textMutedColor) + } + } + + // MARK: - Sign-In Methods + + Section("Sign-In Methods") { + if let networkUser = networkUserViewModel?.networkUser { + let authMethods = parseAuthMethods(networkUser) + + ForEach(authMethods, id: \.self) { method in + HStack { + Text(methodDisplayName(method)) + .font(themeManager.currentTheme.bodyFont) + Spacer() + Button(role: .destructive) { + viewModel.presentRemoveAuth(method) + } label: { + Text("Remove") + } + } + } + + Button(action: { + viewModel.presentAddAuthSheet = true + }) { + Text("Add sign-in method") + } + } else { + HStack { + Text("Loading sign-in methods...") + .font(themeManager.currentTheme.secondaryBodyFont) + .foregroundColor(themeManager.currentTheme.textMutedColor) + Spacer() + ProgressView() + } + } + } + Section("Account") { /** * Update referral code @@ -349,6 +444,7 @@ struct SettingsForm_iOS: View { .scrollContentBackground(.hidden) .background(themeManager.currentTheme.backgroundColor) } + } #endif diff --git a/app/network/Main/Account/Settings/SettingsForm-macOS.swift b/app/network/Main/Account/Settings/SettingsForm-macOS.swift index 4cf2b8e3..efd77f85 100644 --- a/app/network/Main/Account/Settings/SettingsForm-macOS.swift +++ b/app/network/Main/Account/Settings/SettingsForm-macOS.swift @@ -36,6 +36,9 @@ struct SettingsForm_macOS: View { @Binding var canReceiveProductUpdates: Bool @Binding var launchAtStartupEnabled: Bool + let networkUserViewModel: NetworkUserViewModel? + let viewModel: SettingsView.ViewModel + var provideIndicatorColor: Color { if !provideEnabled { return .urCoral @@ -170,6 +173,112 @@ struct SettingsForm_macOS: View { Spacer().frame(height: 32) + // MARK: - Seedphrase Management + + HStack { + UrLabel(text: "Seedphrase") + + Spacer() + } + + VStack(alignment: .leading) { + if let networkUser = networkUserViewModel?.networkUser { + let hasSeedphrase = authTypesContains(networkUser.authTypes, "seedphrase") + if hasSeedphrase { + HStack { + Button(action: { + viewModel.confirmRegenerateSeedphrase() + }) { + Text("Regenerate Seedphrase") + } + if viewModel.isRegeneratingSeedphrase { + ProgressView().scaleEffect(0.7) + } + Spacer() + } + .disabled(viewModel.isRegeneratingSeedphrase) + } else { + HStack { + Button(action: { + viewModel.confirmGenerateSeedphrase() + }) { + Text("Generate Seedphrase") + } + if viewModel.isGeneratingSeedphrase { + ProgressView().scaleEffect(0.7) + } + Spacer() + } + .disabled(viewModel.isGeneratingSeedphrase) + } + } else { + Text("Loading...") + .font(themeManager.currentTheme.secondaryBodyFont) + .foregroundColor(themeManager.currentTheme.textMutedColor) + } + + Text("A seedphrase lets you recover your account if you lose access.") + .font(themeManager.currentTheme.secondaryBodyFont) + .foregroundColor(themeManager.currentTheme.textMutedColor) + } + .padding() + .background(themeManager.currentTheme.tintedBackgroundBase) + .clipShape(RoundedRectangle(cornerRadius: 16, style: .continuous)) + + Spacer().frame(height: 32) + + // MARK: - Sign-In Methods + + HStack { + UrLabel(text: "Sign-In Methods") + + Spacer() + } + + VStack(alignment: .leading) { + if let networkUser = networkUserViewModel?.networkUser { + let authMethods = parseAuthMethods(networkUser) + + ForEach(authMethods, id: \.self) { method in + HStack { + Text(methodDisplayName(method)) + .font(themeManager.currentTheme.bodyFont) + Spacer() + Button(role: .destructive) { + viewModel.presentRemoveAuth(method) + } label: { + Text("Remove") + } + } + Spacer().frame(height: 8) + } + + Spacer().frame(height: 4) + + Button(action: { + viewModel.presentAddAuthSheet = true + }) { + Text("Add sign-in method") + } + } else { + HStack { + Text("Loading sign-in methods...") + .font(themeManager.currentTheme.secondaryBodyFont) + .foregroundColor(themeManager.currentTheme.textMutedColor) + Spacer() + ProgressView().scaleEffect(0.7) + } + } + } + .padding() + .background(themeManager.currentTheme.tintedBackgroundBase) + .clipShape(RoundedRectangle(cornerRadius: 16, style: .continuous)) + + Spacer().frame(height: 32) + + /** + * System + */ HStack { UrLabel(text: "System") @@ -235,7 +344,6 @@ struct SettingsForm_macOS: View { .contentShape(Rectangle()) .onTapGesture { navigate(.transferBalanceCodes) - // navigate to blocked } @@ -367,7 +475,6 @@ struct SettingsForm_macOS: View { .contentShape(Rectangle()) .onTapGesture { navigate(.blockedLocations) - // navigate to blocked } } @@ -496,7 +603,7 @@ struct SettingsForm_macOS: View { } Spacer().frame(height: 64) - + Button(role: .destructive, action: { presentDeleteAccountConfirmation() }) { @@ -514,6 +621,7 @@ struct SettingsForm_macOS: View { } } } + } #endif diff --git a/app/network/Main/Account/Settings/SettingsView.swift b/app/network/Main/Account/Settings/SettingsView.swift index 0239f7c1..b7c5a2d5 100644 --- a/app/network/Main/Account/Settings/SettingsView.swift +++ b/app/network/Main/Account/Settings/SettingsView.swift @@ -27,6 +27,7 @@ struct SettingsView: View { let api: UrApiServiceProtocol let navigate: (AccountNavigationPath) -> Void let providerCountries: [SdkConnectLocation] + let networkUserViewModel: NetworkUserViewModel? init( api: UrApiServiceProtocol, @@ -35,9 +36,10 @@ struct SettingsView: View { referralLinkViewModel: ReferralLinkViewModel, accountWalletsViewModel: AccountWalletsViewModel, navigate: @escaping (AccountNavigationPath) -> Void, - providerCountries: [SdkConnectLocation] + providerCountries: [SdkConnectLocation], + networkUserViewModel: NetworkUserViewModel? = nil ) { - _viewModel = StateObject(wrappedValue: ViewModel(api: api)) + _viewModel = StateObject(wrappedValue: ViewModel(api: api, networkUserViewModel: networkUserViewModel)) self.clientId = clientId self.accountPreferencesViewModel = accountPreferencesViewModel self.referralLinkViewModel = referralLinkViewModel @@ -45,10 +47,11 @@ struct SettingsView: View { self.navigate = navigate self.providerCountries = providerCountries self.api = api + self.networkUserViewModel = networkUserViewModel } var body: some View { - + #if os(iOS) SettingsForm_iOS( urApiService: api, @@ -73,6 +76,8 @@ struct SettingsView: View { presentRenameDevice: viewModel.presentRenameDevice, canReceiveNotifications: $viewModel.canReceiveNotifications, canReceiveProductUpdates: $accountPreferencesViewModel.canReceiveProductUpdates, + networkUserViewModel: networkUserViewModel, + viewModel: viewModel, ) .background(themeManager.currentTheme.backgroundColor) .task { @@ -107,6 +112,25 @@ struct SettingsView: View { } } + .sheet(isPresented: $viewModel.presentSigninWithSolanaSheet) { + + SolanaSignMessageSheet( + isSigningMessage: viewModel.isSigningMessage, + setIsSigningMessage: viewModel.setIsSigningMessage, + signButtonText: "Confirm Seeker Token", + signButtonLabelText: "Claim multiplier", + message: connectWalletProviderViewModel.claimSeekerTokenMessage, + dismiss: { + viewModel.presentSigninWithSolanaSheet = false + } + ) + .environmentObject(themeManager) + .environmentObject(connectWalletProviderViewModel) + .presentationDetents([.height(148)]) + } + .onOpenURL { url in + handleWalletDeepLink(url) + } .sheet(isPresented: $viewModel.presentUpdateReferralNetworkSheet) { UpdateReferralNetworkSheet( api: api, @@ -125,6 +149,51 @@ struct SettingsView: View { .presentationDetents([.height(268)]) .presentationDragIndicator(.visible) } + .sheet(isPresented: $viewModel.presentSeedphraseSheet) { + SeedphraseDisplayView( + seedphrase: viewModel.generatedSeedphrase, + onConfirmed: { _ in + viewModel.dismissSeedphraseSheet() + } + ) + .environmentObject(themeManager) + } + .confirmationDialog( + "Generate a recovery seedphrase?", + isPresented: $viewModel.presentSeedphraseConfirmation, + titleVisibility: .visible + ) { + Button("Generate") { + Task { + await viewModel.executePendingSeedphraseAction() + } + } + Button("Cancel", role: .cancel) {} + } message: { + Text("A seedphrase lets you recover your account if you lose access. Store it safely.") + } + .confirmationDialog( + "Remove this sign-in method?", + isPresented: $viewModel.presentRemoveAuthConfirmation, + titleVisibility: .visible + ) { + Button("Remove", role: .destructive) { + Task { + await viewModel.executeRemoveAuth() + } + } + Button("Cancel", role: .cancel) {} + } message: { + if let authType = viewModel.authTypeToRemove { + Text("Are you sure you want to remove \(authType) as a sign-in method?") + } + } + .sheet(isPresented: $viewModel.presentAddAuthSheet) { + AddAuthSheet(api: api, networkUserViewModel: networkUserViewModel) + .environmentObject(themeManager) + .environmentObject(snackbarManager) + .environmentObject(connectWalletProviderViewModel) + } #elseif os(macOS) SettingsForm_macOS( @@ -150,7 +219,9 @@ struct SettingsView: View { presentRenameDevice: viewModel.presentRenameDevice, canReceiveNotifications: $viewModel.canReceiveNotifications, canReceiveProductUpdates: $accountPreferencesViewModel.canReceiveProductUpdates, - launchAtStartupEnabled: $viewModel.launchAtStartupEnabled + launchAtStartupEnabled: $viewModel.launchAtStartupEnabled, + networkUserViewModel: networkUserViewModel, + viewModel: viewModel ) .task { await viewModel.fetchDeviceInfo(clientId) @@ -200,11 +271,76 @@ struct SettingsView: View { ) .environmentObject(themeManager) } + .sheet(isPresented: $viewModel.presentSeedphraseSheet) { + SeedphraseDisplayView( + seedphrase: viewModel.generatedSeedphrase, + onConfirmed: { _ in + viewModel.dismissSeedphraseSheet() + } + ) + .environmentObject(themeManager) + } + .confirmationDialog( + "Generate a recovery seedphrase?", + isPresented: $viewModel.presentSeedphraseConfirmation, + titleVisibility: .visible + ) { + Button("Generate") { + Task { + await viewModel.executePendingSeedphraseAction() + } + } + Button("Cancel", role: .cancel) {} + } message: { + Text("A seedphrase lets you recover your account if you lose access. Store it safely.") + } + .confirmationDialog( + "Remove this sign-in method?", + isPresented: $viewModel.presentRemoveAuthConfirmation, + titleVisibility: .visible + ) { + Button("Remove", role: .destructive) { + Task { + await viewModel.executeRemoveAuth() + } + } + Button("Cancel", role: .cancel) {} + } message: { + if let authType = viewModel.authTypeToRemove { + Text("Are you sure you want to remove \(authType) as a sign-in method?") + } + } + .sheet(isPresented: $viewModel.presentAddAuthSheet) { + AddAuthSheet(api: api, networkUserViewModel: networkUserViewModel) + .environmentObject(themeManager) + .environmentObject(snackbarManager) + .environmentObject(connectWalletProviderViewModel) + } #endif } + private func handleWalletDeepLink(_ url: URL) { + let vm = connectWalletProviderViewModel + if vm.pendingAddAuthSignatureHandler != nil, + let pk = vm.connectedPublicKey { + vm.handleDeepLink( + url, + onSignature: { signature in + if let handler = vm.pendingAddAuthSignatureHandler { + Task { + await handler(pk, signature) + } + } + }, + onError: { _ in } + ) + } else { + vm.handleDeepLink(url) + } + } + private func saveDeviceName() async { let result = await viewModel.updateDeviceName() switch result { @@ -237,17 +373,3 @@ struct SettingsView: View { #endif } } - -//#Preview { -// -// let themeManager = ThemeManager.shared -// let accountPreferenceViewModel = AccountPreferencesViewModel(api: SdkApi()) -// -// SettingsView( -// api: SdkApi(), -// clientId: nil, -// accountPreferencesViewModel: accountPreferenceViewModel -// ) -// .environmentObject(themeManager) -// .background(themeManager.currentTheme.backgroundColor.ignoresSafeArea()) -//} diff --git a/app/network/Main/Account/Settings/SettingsViewModel.swift b/app/network/Main/Account/Settings/SettingsViewModel.swift index 74acd8a8..76a53812 100644 --- a/app/network/Main/Account/Settings/SettingsViewModel.swift +++ b/app/network/Main/Account/Settings/SettingsViewModel.swift @@ -21,10 +21,21 @@ extension SettingsView { @Published var presentUpdateReferralNetworkSheet: Bool = false + // MARK: - Solana/Wallet Sign-In (restored from upstream merge) + @Published var presentSigninWithSolanaSheet: Bool = false + @Published private(set) var isSigningMessage: Bool = false + + func setIsSigningMessage(_ isSigning: Bool) -> Void { + isSigningMessage = isSigning + } + @Published var version: String = "" - init(api: UrApiServiceProtocol) { + var networkUserViewModel: NetworkUserViewModel? + + init(api: UrApiServiceProtocol, networkUserViewModel: NetworkUserViewModel? = nil) { self.api = api + self.networkUserViewModel = networkUserViewModel #if os(macOS) self.launchAtStartupEnabled = SMAppService.mainApp.status == .enabled @@ -233,7 +244,6 @@ extension SettingsView { } catch(let error) { print("\(domain) Error fetching transfer stats: \(error)") - // isLoadingTransferStats = false } } @@ -264,6 +274,123 @@ extension SettingsView { } #endif + // MARK: - Seedphrase Management + + @Published var presentSeedphraseSheet: Bool = false + @Published var generatedSeedphrase: String = "" + @Published var isGeneratingSeedphrase: Bool = false + @Published var isRegeneratingSeedphrase: Bool = false + @Published var presentSeedphraseConfirmation: Bool = false + @Published var seedphraseError: String? + + private var pendingSeedphraseAction: SeedphraseAction = .generate + + enum SeedphraseAction { + case generate + case regenerate + } + + func confirmGenerateSeedphrase() { + pendingSeedphraseAction = .generate + presentSeedphraseConfirmation = true + } + + func confirmRegenerateSeedphrase() { + pendingSeedphraseAction = .regenerate + presentSeedphraseConfirmation = true + } + + func executePendingSeedphraseAction() async { + switch pendingSeedphraseAction { + case .generate: + await executeGenerateSeedphrase() + case .regenerate: + await executeRegenerateSeedphrase() + } + } + + func executeGenerateSeedphrase() async { + isGeneratingSeedphrase = true + seedphraseError = nil + + do { + let result = try await api.generateSeedphrase() + self.generatedSeedphrase = result.seedphrase + self.isGeneratingSeedphrase = false + self.presentSeedphraseConfirmation = false + self.presentSeedphraseSheet = true + // Refresh user data to get updated auth_types + Task { [weak self] in + _ = await self?.networkUserViewModel?.refreshNetworkUser() + } + } catch(let error) { + self.isGeneratingSeedphrase = false + self.presentSeedphraseConfirmation = false + self.seedphraseError = error.localizedDescription + } + } + + func executeRegenerateSeedphrase() async { + isRegeneratingSeedphrase = true + seedphraseError = nil + + do { + let result = try await api.regenerateSeedphrase() + self.generatedSeedphrase = result.seedphrase + self.isRegeneratingSeedphrase = false + self.presentSeedphraseConfirmation = false + self.presentSeedphraseSheet = true + // Refresh user data to get updated auth_types + Task { [weak self] in + _ = await self?.networkUserViewModel?.refreshNetworkUser() + } + } catch(let error) { + self.isRegeneratingSeedphrase = false + self.presentSeedphraseConfirmation = false + self.seedphraseError = error.localizedDescription + } + } + + func dismissSeedphraseSheet() { + self.presentSeedphraseSheet = false + self.generatedSeedphrase = "" + } + + // MARK: - Auth Method Management + + @Published var presentAddAuthSheet: Bool = false + @Published var presentRemoveAuthConfirmation: Bool = false + @Published var authTypeToRemove: String? + @Published var isAddingAuth: Bool = false + @Published var isRemovingAuth: Bool = false + @Published var addAuthError: String? + @Published var removeAuthError: String? + + func presentRemoveAuth(_ authType: String) { + self.authTypeToRemove = authType + self.presentRemoveAuthConfirmation = true + } + + func executeRemoveAuth() async { + guard let authType = authTypeToRemove else { return } + isRemovingAuth = true + removeAuthError = nil + + do { + let _ = try await api.removeAuth(authType: authType) + self.isRemovingAuth = false + self.presentRemoveAuthConfirmation = false + self.authTypeToRemove = nil + // Refresh user data to get updated auth_types + Task { [weak self] in + _ = await self?.networkUserViewModel?.refreshNetworkUser() + } + } catch(let error) { + self.isRemovingAuth = false + self.removeAuthError = error.localizedDescription + } + } + } } diff --git a/app/network/NetworkApp.swift b/app/network/NetworkApp.swift index a2f6fe48..b16d34af 100644 --- a/app/network/NetworkApp.swift +++ b/app/network/NetworkApp.swift @@ -20,9 +20,10 @@ struct NetworkApp: App { #endif @AppStorage("showMenuBarExtra") private var showMenuBarExtra = true - + @State private var isWindowVisible = true @State private var keyEventMonitor: Any? + @Environment(\.scenePhase) private var scenePhase let themeManager = ThemeManager.shared @@ -90,6 +91,21 @@ struct NetworkApp: App { dnsSettingsStore.reset() networkPeersStore.reset() } + + // Cold launch already gets a fresh JWT (the SDK's token manager refreshes + // once immediately on start), but returning from the background does not + // -- the token manager only refreshes on a schedule (~14 days out from + // expiration) or when explicitly asked. Force one here so anything that + // could have changed out-of-band (e.g. a network name changed from + // another device or the web dashboard) shows up when the user comes + // back to the app, not just after a local rename or a full logout/login. + private func refreshJwtOnForeground() { + do { + try deviceManager.device?.refreshToken(0) + } catch { + print("Error refreshing JWT on foreground: \(error)") + } + } private var connectEnabled: Bool { @@ -157,6 +173,11 @@ struct NetworkApp: App { .onReceive(deviceManager.$device) { device in updateConnectViewModel(device) } + .onChange(of: scenePhase) { newPhase in + if newPhase == .active { + refreshJwtOnForeground() + } + } #elseif os(macOS) ContentView() .environmentObject(themeManager) @@ -185,6 +206,11 @@ struct NetworkApp: App { .onReceive(deviceManager.$device) { device in updateConnectViewModel(device) } + .onChange(of: scenePhase) { newPhase in + if newPhase == .active { + refreshJwtOnForeground() + } + } .onAppear { if mainWindow == nil { mainWindow = NSApplication.shared.windows.first { window in diff --git a/app/network/Shared/Services/UrApiService/MockUrApiService.swift b/app/network/Shared/Services/UrApiService/MockUrApiService.swift index f27dd46c..dc338996 100644 --- a/app/network/Shared/Services/UrApiService/MockUrApiService.swift +++ b/app/network/Shared/Services/UrApiService/MockUrApiService.swift @@ -49,10 +49,6 @@ class MockUrApiService: UrApiServiceProtocol { SdkValidateReferralCodeResult() } - func upgradeGuest(_ args: SdkUpgradeGuestArgs) async throws -> LoginNetworkResult { - return LoginNetworkResult.failure(LoginError.appleLoginFailed) - } - func fetchSubscriptionBalance() async throws -> SdkSubscriptionBalanceResult { return SdkSubscriptionBalanceResult() } @@ -97,6 +93,38 @@ class MockUrApiService: UrApiServiceProtocol { return SdkUnlinkReferralNetworkResult() } + func loginWithSeedphrase(seedphrase: String) async throws -> AuthLoginResult { + return AuthLoginResult.failure(LoginError.inProgress) + } + + func createInstantAccount() async throws -> (jwt: String, seedphrase: String) { + return ("mock-jwt", "mock seedphrase words here for testing purposes only") + } + + func generateSeedphrase() async throws -> SdkGenerateSeedphraseResult { + return SdkGenerateSeedphraseResult() + } + + func regenerateSeedphrase() async throws -> SdkRegenerateSeedphraseResult { + return SdkRegenerateSeedphraseResult() + } + + func addAuth(_ args: SdkAddAuthArgs) async throws -> SdkAddAuthResult { + return SdkAddAuthResult() + } + + func removeAuth(authType: String) async throws -> SdkRemoveAuthResult { + return SdkRemoveAuthResult() + } + + func changeNetworkName(_ newName: String) async throws -> SdkChangeNetworkNameResult { + return SdkChangeNetworkNameResult() + } + + func claimNetworkName(_ newName: String) async throws -> SdkClaimNetworkNameResult { + return SdkClaimNetworkNameResult() + } + func redeemBalanceCode(_ code: String) async throws -> SdkRedeemBalanceCodeResult { return SdkRedeemBalanceCodeResult() } diff --git a/app/network/Shared/Services/UrApiService/UrApiService.swift b/app/network/Shared/Services/UrApiService/UrApiService.swift index 7eceb04b..0eac82fa 100644 --- a/app/network/Shared/Services/UrApiService/UrApiService.swift +++ b/app/network/Shared/Services/UrApiService/UrApiService.swift @@ -395,7 +395,56 @@ extension UrApiService { } } - func createNetwork(_ args: SdkNetworkCreateArgs) async throws -> LoginNetworkResult { + func loginWithSeedphrase(seedphrase: String) async throws -> AuthLoginResult { + let api = try requireApi() + return try await withCheckedThrowingContinuation { continuation in + + let callback = AuthLoginCallback { result, error in + + if let error { + continuation.resume(throwing: error) + return + } + + guard let result else { + continuation.resume(throwing: NSError(domain: "UrApiService", code: -1, userInfo: [NSLocalizedDescriptionKey: "No result found"])) + return + } + + if let resultError = result.error { + continuation.resume(throwing: NSError(domain: "UrApiService", code: -1, userInfo: [NSLocalizedDescriptionKey: "result.error exists \(resultError.message)"])) + return + } + + if let network = result.network { + switch self.nonEmptyJwt(network.byJwt, context: "loginWithSeedphrase") { + case .success(let jwt): + continuation.resume(returning: .login(jwt)) + case .failure(let error): + continuation.resume(throwing: error) + } + return + } + + continuation.resume(returning: .failure(NSError(domain: "UrApiService", code: -1, userInfo: [NSLocalizedDescriptionKey: "Seedphrase login returned no network"]))) + + } + + let args = SdkAuthLoginArgs() + // Normalize: lowercase, trim, collapse multiple spaces + let normalized = seedphrase + .lowercased() + .trimmingCharacters(in: .whitespacesAndNewlines) + .components(separatedBy: .whitespacesAndNewlines) + .filter { !$0.isEmpty } + .joined(separator: " ") + args.seedphrase = normalized + api.authLogin(args, callback: callback) + + } + } + + func createInstantAccount() async throws -> (jwt: String, seedphrase: String) { let api = try requireApi() return try await withCheckedThrowingContinuation { continuation in @@ -407,7 +456,7 @@ extension UrApiService { } guard let result = result else { - continuation.resume(throwing: NSError(domain: "UrApiService", code: -1, userInfo: [NSLocalizedDescriptionKey: "createNetwork returned nil result"])) + continuation.resume(throwing: NSError(domain: "UrApiService", code: -1, userInfo: [NSLocalizedDescriptionKey: "createInstantAccount returned nil result"])) return } @@ -417,14 +466,21 @@ extension UrApiService { } if result.verificationRequired != nil { - continuation.resume(returning: .successWithVerificationRequired) + continuation.resume(throwing: NSError(domain: "UrApiService", code: -1, userInfo: [NSLocalizedDescriptionKey: "Verification required for instant account creation"])) return } + guard !result.seedphrase.isEmpty else { + continuation.resume(throwing: NSError(domain: "UrApiService", code: -1, userInfo: [NSLocalizedDescriptionKey: "No seedphrase in result"])) + return + } + + let seedphrase = result.seedphrase + if let network = result.network { - switch self.nonEmptyJwt(network.byJwt, context: "createNetwork") { + switch self.nonEmptyJwt(network.byJwt, context: "createInstantAccount") { case .success(let jwt): - continuation.resume(returning: .successWithJwt(jwt)) + continuation.resume(returning: (jwt, seedphrase)) case .failure(let error): continuation.resume(throwing: error) } @@ -436,16 +492,19 @@ extension UrApiService { } + let args = SdkNetworkCreateArgs() + args.terms = true + // No userAuth, password, authJwt, walletAuth — triggers seedphrase path api.networkCreate(args, callback: callback) } } - func upgradeGuest(_ args: SdkUpgradeGuestArgs) async throws -> LoginNetworkResult { + func createNetwork(_ args: SdkNetworkCreateArgs) async throws -> LoginNetworkResult { let api = try requireApi() return try await withCheckedThrowingContinuation { continuation in - let callback = UpgradeGuestCallback { result, err in + let callback = NetworkCreateCallback { result, err in if let err = err { continuation.resume(throwing: err) @@ -453,12 +512,12 @@ extension UrApiService { } guard let result = result else { - continuation.resume(throwing: NSError(domain: "UrApiService", code: -1, userInfo: [NSLocalizedDescriptionKey: "upgradeGuest returned nil result"])) + continuation.resume(throwing: NSError(domain: "UrApiService", code: -1, userInfo: [NSLocalizedDescriptionKey: "createNetwork returned nil result"])) return } if let resultError = result.error { - continuation.resume(throwing: UrApiError.resultError(message: resultError.message)) + continuation.resume(throwing: NSError(domain: "UrApiService", code: -1, userInfo: [NSLocalizedDescriptionKey: resultError.message])) return } @@ -468,7 +527,7 @@ extension UrApiService { } if let network = result.network { - switch self.nonEmptyJwt(network.byJwt, context: "upgradeGuest") { + switch self.nonEmptyJwt(network.byJwt, context: "createNetwork") { case .success(let jwt): continuation.resume(returning: .successWithJwt(jwt)) case .failure(let error): @@ -476,19 +535,219 @@ extension UrApiService { } return } else { - continuation.resume(throwing: NSError(domain: "UrApiService", code: 0, userInfo: [NSLocalizedDescriptionKey: "No network found in result"])) + continuation.resume(throwing: NSError(domain: "UrApiService", code: 0, userInfo: [NSLocalizedDescriptionKey: "No network object found in result"])) return } } - api.upgradeGuest(args, callback: callback) + api.networkCreate(args, callback: callback) } } +} + +// MARK: — Account Management: Seedphrase + +extension UrApiService { + + func generateSeedphrase() async throws -> SdkGenerateSeedphraseResult { + let api = try requireApi() + return try await withCheckedThrowingContinuation { continuation in + + let callback = GenerateSeedphraseCallback { result, err in + + if let err = err { + continuation.resume(throwing: err) + return + } + + guard let result = result else { + continuation.resume(throwing: NSError(domain: self.domain, code: -1, userInfo: [NSLocalizedDescriptionKey: "generateSeedphrase returned nil result"])) + return + } + + if let errMsg = result.error?.message { + continuation.resume(throwing: NSError(domain: self.domain, code: -1, userInfo: [NSLocalizedDescriptionKey: errMsg])) + return + } + + continuation.resume(returning: result) + } + + let args = SdkGenerateSeedphraseArgs() + api.generateSeedphrase(args, callback: callback) + } + } + + func regenerateSeedphrase() async throws -> SdkRegenerateSeedphraseResult { + let api = try requireApi() + return try await withCheckedThrowingContinuation { continuation in + + let callback = RegenerateSeedphraseCallback { result, err in + + if let err = err { + continuation.resume(throwing: err) + return + } + + guard let result = result else { + continuation.resume(throwing: NSError(domain: self.domain, code: -1, userInfo: [NSLocalizedDescriptionKey: "regenerateSeedphrase returned nil result"])) + return + } + + if let errMsg = result.error?.message { + continuation.resume(throwing: NSError(domain: self.domain, code: -1, userInfo: [NSLocalizedDescriptionKey: errMsg])) + return + } + + continuation.resume(returning: result) + } + + let args = SdkRegenerateSeedphraseArgs() + api.regenerateSeedphrase(args, callback: callback) + } + } + +} + +// MARK: — Account Management: Auth Methods + +extension UrApiService { + + func addAuth(_ args: SdkAddAuthArgs) async throws -> SdkAddAuthResult { + let api = try requireApi() + return try await withCheckedThrowingContinuation { continuation in + + let callback = AddAuthCallback { result, err in + + if let err = err { + continuation.resume(throwing: err) + return + } + + guard let result = result else { + continuation.resume(throwing: NSError(domain: self.domain, code: -1, userInfo: [NSLocalizedDescriptionKey: "addAuth returned nil result"])) + return + } + + if let errMsg = result.error?.message { + continuation.resume(throwing: NSError(domain: self.domain, code: -1, userInfo: [NSLocalizedDescriptionKey: errMsg])) + return + } + + continuation.resume(returning: result) + } + + api.addAuth(args, callback: callback) + } + } + + func removeAuth(authType: String) async throws -> SdkRemoveAuthResult { + let api = try requireApi() + return try await withCheckedThrowingContinuation { continuation in + + let callback = RemoveAuthCallback { result, err in + + if let err = err { + continuation.resume(throwing: err) + return + } + + guard let result = result else { + continuation.resume(throwing: NSError(domain: self.domain, code: -1, userInfo: [NSLocalizedDescriptionKey: "removeAuth returned nil result"])) + return + } + + if let errMsg = result.error?.message { + continuation.resume(throwing: NSError(domain: self.domain, code: -1, userInfo: [NSLocalizedDescriptionKey: errMsg])) + return + } + + continuation.resume(returning: result) + } + + let args = SdkRemoveAuthArgs() + args.authType = authType + api.removeAuth(args, callback: callback) + } + } + +} + +// MARK: — Account Management: Network Name + +extension UrApiService { + + func changeNetworkName(_ newName: String) async throws -> SdkChangeNetworkNameResult { + let api = try requireApi() + return try await withCheckedThrowingContinuation { continuation in + + let callback = ChangeNetworkNameCallback { result, err in + + if let err = err { + continuation.resume(throwing: err) + return + } + + guard let result = result else { + continuation.resume(throwing: NSError(domain: self.domain, code: -1, userInfo: [NSLocalizedDescriptionKey: "changeNetworkName returned nil result"])) + return + } + + if let errMsg = result.error?.message { + continuation.resume(throwing: NSError(domain: self.domain, code: -1, userInfo: [NSLocalizedDescriptionKey: errMsg])) + return + } + + continuation.resume(returning: result) + } + + let args = SdkChangeNetworkNameArgs() + args.newName = newName + api.changeNetworkName(args, callback: callback) + } + } + + func claimNetworkName(_ newName: String) async throws -> SdkClaimNetworkNameResult { + let api = try requireApi() + return try await withCheckedThrowingContinuation { continuation in + + let callback = ClaimNetworkNameCallback { result, err in + + if let err = err { + continuation.resume(throwing: err) + return + } + + guard let result = result else { + continuation.resume(throwing: NSError(domain: self.domain, code: -1, userInfo: [NSLocalizedDescriptionKey: "claimNetworkName returned nil result"])) + return + } + + if let errMsg = result.error?.message { + continuation.resume(throwing: NSError(domain: self.domain, code: -1, userInfo: [NSLocalizedDescriptionKey: errMsg])) + return + } + + continuation.resume(returning: result) + } + + let args = SdkClaimNetworkNameArgs() + args.newName = newName + api.claimNetworkName(args, callback: callback) + } + } + +} + +// MARK: wallet + +extension UrApiService { + func createAuthCode() async throws -> SdkAuthCodeCreateResult { - + let api = try requireApi() return try await withCheckedThrowingContinuation { continuation in @@ -1049,12 +1308,6 @@ private class ValidateReferralCallback: SdkCallback, SdkUpgradeGuestCallbackProtocol { - func result(_ result: SdkUpgradeGuestResult?, err: Error?) { - handleResult(result, err: err) - } -} - private class GetSubscriptionBalanceCallback: SdkCallback, SdkSubscriptionBalanceCallbackProtocol { func result(_ result: SdkSubscriptionBalanceResult?, err: Error?) { @@ -1304,6 +1557,44 @@ private class DeviceSetNameCallback: SdkCallback< } } +// MARK: — Account Management Callbacks + +private class GenerateSeedphraseCallback: SdkCallback, SdkGenerateSeedphraseCallbackProtocol { + func result(_ result: SdkGenerateSeedphraseResult?, err: Error?) { + handleResult(result, err: err) + } +} + +private class RegenerateSeedphraseCallback: SdkCallback, SdkRegenerateSeedphraseCallbackProtocol { + func result(_ result: SdkRegenerateSeedphraseResult?, err: Error?) { + handleResult(result, err: err) + } +} + +private class AddAuthCallback: SdkCallback, SdkAddAuthCallbackProtocol { + func result(_ result: SdkAddAuthResult?, err: Error?) { + handleResult(result, err: err) + } +} + +private class RemoveAuthCallback: SdkCallback, SdkRemoveAuthCallbackProtocol { + func result(_ result: SdkRemoveAuthResult?, err: Error?) { + handleResult(result, err: err) + } +} + +private class ChangeNetworkNameCallback: SdkCallback, SdkChangeNetworkNameCallbackProtocol { + func result(_ result: SdkChangeNetworkNameResult?, err: Error?) { + handleResult(result, err: err) + } +} + +private class ClaimNetworkNameCallback: SdkCallback, SdkClaimNetworkNameCallbackProtocol { + func result(_ result: SdkClaimNetworkNameResult?, err: Error?) { + handleResult(result, err: err) + } +} + enum DeviceInfoError: Error { case resultEmpty case resultError(message: String) diff --git a/app/network/Shared/Services/UrApiService/UrApiServiceProtocol.swift b/app/network/Shared/Services/UrApiService/UrApiServiceProtocol.swift index c1e481e9..1cd7c201 100644 --- a/app/network/Shared/Services/UrApiService/UrApiServiceProtocol.swift +++ b/app/network/Shared/Services/UrApiService/UrApiServiceProtocol.swift @@ -34,7 +34,6 @@ protocol UrApiServiceProtocol { func authLogin(_ args: SdkAuthLoginArgs) async throws -> AuthLoginResult func createNetwork(_ args: SdkNetworkCreateArgs) async throws -> LoginNetworkResult func validateReferralCode(_ code: String) async throws -> SdkValidateReferralCodeResult - func upgradeGuest(_ args: SdkUpgradeGuestArgs) async throws -> LoginNetworkResult func createAuthCode() async throws -> SdkAuthCodeCreateResult func authCodeLogin(_ args: SdkAuthCodeLoginArgs) async throws -> SdkAuthCodeLoginResult func authWalletChallenge(_ args: SdkAuthWalletChallengeArgs) async throws -> SdkAuthWalletChallengeResult @@ -66,11 +65,31 @@ protocol UrApiServiceProtocol { /** * Settings */ + func loginWithSeedphrase(seedphrase: String) async throws -> AuthLoginResult + func createInstantAccount() async throws -> (jwt: String, seedphrase: String) func deleteAccount() async throws -> SdkNetworkDeleteResult func getReferralNetwork() async throws -> SdkGetReferralNetworkResult func setNetworkReferral(_ referralCode: String) async throws -> SdkSetNetworkReferralResult func unlinkReferralNetwork() async throws -> SdkUnlinkReferralNetworkResult + /** + * Account Management — Seedphrase + */ + func generateSeedphrase() async throws -> SdkGenerateSeedphraseResult + func regenerateSeedphrase() async throws -> SdkRegenerateSeedphraseResult + + /** + * Account Management — Auth Methods + */ + func addAuth(_ args: SdkAddAuthArgs) async throws -> SdkAddAuthResult + func removeAuth(authType: String) async throws -> SdkRemoveAuthResult + + /** + * Account Management — Network Name + */ + func changeNetworkName(_ newName: String) async throws -> SdkChangeNetworkNameResult + func claimNetworkName(_ newName: String) async throws -> SdkClaimNetworkNameResult + /** * Device */ diff --git a/app/network/Shared/ViewModels/ConnectWalletProviderViewModel.swift b/app/network/Shared/ViewModels/ConnectWalletProviderViewModel.swift index 82f7b79d..987ebbb4 100644 --- a/app/network/Shared/ViewModels/ConnectWalletProviderViewModel.swift +++ b/app/network/Shared/ViewModels/ConnectWalletProviderViewModel.swift @@ -28,6 +28,7 @@ class ConnectWalletProviderViewModel: ObservableObject { var connectedWalletProvider: ConnectedWalletProvider? = nil let welcomeMessage = "Welcome to URnetwork" + let claimSeekerTokenMessage = "Claim point multiplier by holding Seeker Pre-order or Saga Genesis token" /** * Solflare @@ -53,6 +54,12 @@ class ConnectWalletProviderViewModel: ObservableObject { private let bittensorSignMessageRedirectLink = "urnetwork://bittensor-sign-message" private let bittensorConnectRedirectLink = "urnetwork://bittensor-connect" + // When set, the wallet deep link onSignature routes here instead of the default multiplier claim flow + var pendingAddAuthSignatureHandler: ((String, String) async -> Void)? + + // Challenge message to sign during wallet auth addition + var pendingWalletAuthMessage: String? + init() { self.createKeyPair() } diff --git a/app/network/Shared/ViewModels/GuestUpgradeViewModel.swift b/app/network/Shared/ViewModels/GuestUpgradeViewModel.swift deleted file mode 100644 index 99a04080..00000000 --- a/app/network/Shared/ViewModels/GuestUpgradeViewModel.swift +++ /dev/null @@ -1,111 +0,0 @@ -// -// GuestUpgradeViewModel.swift -// URnetwork -// -// Created by Stuart Kuentzel on 2025/01/19. -// - -import Foundation -import URnetworkSdk - -@MainActor -class GuestUpgradeViewModel: ObservableObject { - - @Published private(set) var isUpgrading: Bool = false - - private var api: SdkApi - - let domain = "[GuestUpgradeViewModel]" - - init(api: SdkApi) { - self.api = api - } - - func linkGuestToExistingLogin(args: SdkUpgradeGuestExistingArgs) async -> AuthLoginResult { - - if isUpgrading { - return .failure(LoginError.inProgress) - } - - isUpgrading = true - defer { - isUpgrading = false - } - - do { - - let result: AuthLoginResult = try await withCheckedThrowingContinuation { continuation in - - let callback = UpgradeGuestExistingCallback { result, error in - - guard let result else { - continuation.resume(throwing: error ?? NSError(domain: "[GuestUpgradeViewModel]", code: -1, userInfo: [NSLocalizedDescriptionKey: "No result found"])) - - return - } - - if let resultError = result.error { - - continuation.resume(throwing: NSError(domain: "[GuestUpgradeViewModel]", code: -1, userInfo: [NSLocalizedDescriptionKey: "result.error exists \(resultError.message)"])) - - return - } - - /** - * In the case a guest user is upgrading to an existing account - * but the existing account has not yet been verified - */ - if let verificationRequired = result.verificationRequired { - continuation.resume(returning: .verificationRequired(verificationRequired.userAuth)) - return - } - - /** - * JWT exists, proceed to authenticate network - */ - if let network = result.network { - guard !network.byJwt.isEmpty else { - continuation.resume(throwing: NSError(domain: "[GuestUpgradeViewModel]", code: -1, userInfo: [NSLocalizedDescriptionKey: "byJWT is empty"])) - return - } - - continuation.resume(returning: .login(network.byJwt)) - return - } - - /** - * No network exists - * Navigate to create view - */ - let authLoginArgs = SdkAuthLoginArgs() - - authLoginArgs.authJwt = args.authJwt - authLoginArgs.authJwtType = args.authJwtType - authLoginArgs.userAuth = args.userAuth - authLoginArgs.walletAuth = args.walletAuth - - - continuation.resume(returning: .create(authLoginArgs)) - - - } - - api.upgradeGuestExisting(args, callback: callback) - - } - - return result - - } catch(let error) { - return .failure(error) - } - - } - -} - -private class UpgradeGuestExistingCallback: SdkCallback, SdkUpgradeGuestExistingCallbackProtocol { - func result(_ result: SdkUpgradeGuestExistingResult?, err: Error?) { - handleResult(result, err: err) - } -} From bb9404ded0c575f585a2032c407136a94ba04a87 Mon Sep 17 00:00:00 2001 From: ryanmello07 Date: Tue, 21 Jul 2026 09:40:12 +0100 Subject: [PATCH 105/109] chore: retrigger CI to pick up SDK beta/custom-server update From 0a7e3b6a3b04066b7f4e7c818b67e0721d33aa0e Mon Sep 17 00:00:00 2001 From: ryanmello07 Date: Tue, 21 Jul 2026 09:47:52 +0100 Subject: [PATCH 106/109] chore: retrigger CI to pick up connect beta/custom-server update From 26e758a34d54a8b54c0f02d8fe21c07c7bc6f02a Mon Sep 17 00:00:00 2001 From: Ryanmello07 <67509637+Ryanmello07@users.noreply.github.com> Date: Tue, 21 Jul 2026 10:56:01 -0700 Subject: [PATCH 107/109] Delete beta-build.yml Not for upstream --- .github/workflows/beta-build.yml | 152 ------------------------------- 1 file changed, 152 deletions(-) delete mode 100644 .github/workflows/beta-build.yml diff --git a/.github/workflows/beta-build.yml b/.github/workflows/beta-build.yml deleted file mode 100644 index a894d8c7..00000000 --- a/.github/workflows/beta-build.yml +++ /dev/null @@ -1,152 +0,0 @@ -name: Beta Build — iOS & macOS - -on: - push: - branches: - - beta/custom-server - pull_request: - branches: - - beta/custom-server - workflow_dispatch: - -permissions: - contents: write - -env: - WARP_VERSION: beta-${{ github.run_number }} - -jobs: - build-ios: - name: Build unsigned iOS and macOS apps - runs-on: macos-latest - steps: - - name: Check out iOS repo - uses: actions/checkout@v4 - with: - path: urnetwork-ios - - - name: Check out forked SDK - uses: actions/checkout@v4 - with: - repository: Ryanmello07/urnetwork-sdk - ref: beta/custom-server - path: urnetwork-sdk - - - name: Set up Go - uses: actions/setup-go@v5 - with: - go-version: stable - - - name: Check out connect dependency - run: git clone --depth 1 --branch beta/custom-server https://github.com/Ryanmello07/connect.git connect - - - name: Check out glog dependency - run: git clone --depth 1 https://github.com/urnetwork/glog.git glog - - - name: Build SDK for iOS - working-directory: urnetwork-sdk/build - env: - BRINGYOUR_HOME: ${{ github.workspace }}/urnetwork-sdk - WARP_HOME: ${{ github.workspace }}/urnetwork-sdk - WARP_VERSION_HOME: ${{ github.workspace }}/urnetwork-sdk/version - run: | - export PATH="$PATH:/usr/local/go/bin:$HOME/go/bin" - make init build_apple - - - name: Resolve Swift packages - working-directory: urnetwork-ios/app - run: | - xcodebuild -resolvePackageDependencies \ - -project app.xcodeproj \ - -scheme URnetwork \ - -derivedDataPath DerivedData - - - name: Archive unsigned iOS app - working-directory: urnetwork-ios/app - run: | - set -o pipefail - xcodebuild archive \ - -project app.xcodeproj \ - -scheme URnetwork \ - -configuration Release \ - -destination 'generic/platform=iOS' \ - -archivePath build/URnetwork.xcarchive \ - -derivedDataPath DerivedData \ - CURRENT_PROJECT_VERSION=${{ github.run_number }} \ - MARKETING_VERSION=beta \ - CODE_SIGNING_ALLOWED=NO \ - CODE_SIGNING_REQUIRED=NO \ - CODE_SIGN_IDENTITY="" \ - PROVISIONING_PROFILE_SPECIFIER="" \ - 2>&1 | tee xcodebuild-archive.log - - - name: Package unsigned IPA - working-directory: urnetwork-ios/app - run: | - APP=$(find build/URnetwork.xcarchive -name 'URnetwork.app' -type d | head -n 1) - if [ -z "$APP" ]; then - echo "Could not find URnetwork.app in archive" - exit 1 - fi - mkdir -p Payload - cp -R "$APP" Payload/ - zip -r URnetwork-beta.ipa Payload - - - name: Archive unsigned macOS app - working-directory: urnetwork-ios/app - run: | - set -o pipefail - xcodebuild archive \ - -project app.xcodeproj \ - -scheme URnetwork \ - -configuration Release \ - -destination 'generic/platform=macOS' \ - -archivePath build/URnetwork-macOS.xcarchive \ - -derivedDataPath DerivedData \ - CURRENT_PROJECT_VERSION=${{ github.run_number }} \ - MARKETING_VERSION=beta \ - CODE_SIGNING_ALLOWED=NO \ - CODE_SIGNING_REQUIRED=NO \ - CODE_SIGN_IDENTITY="" \ - PROVISIONING_PROFILE_SPECIFIER="" \ - 2>&1 | tee xcodebuild-archive-macos.log - - - name: Package unsigned macOS app - working-directory: urnetwork-ios/app - run: | - APP=$(find build/URnetwork-macOS.xcarchive -name 'URnetwork.app' -type d | head -n 1) - if [ -z "$APP" ]; then - echo "Could not find URnetwork.app in macOS archive" - exit 1 - fi - ditto -c -k --sequesterRsrc --keepParent "$APP" URnetwork-beta-macOS.zip - - - name: Upload IPA artifact - uses: actions/upload-artifact@v4 - with: - name: URnetwork-beta-ios - path: urnetwork-ios/app/URnetwork-beta.ipa - if-no-files-found: error - retention-days: 7 - - - name: Upload macOS artifact - uses: actions/upload-artifact@v4 - with: - name: URnetwork-beta-macOS - path: urnetwork-ios/app/URnetwork-beta-macOS.zip - if-no-files-found: error - retention-days: 7 - - - name: Create prerelease - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - TAG: beta-custom-server-${{ github.run_number }}-${{ github.run_attempt }} - run: | - gh release delete "$TAG" --repo "$GITHUB_REPOSITORY" --yes || true - gh release create "$TAG" \ - --repo "$GITHUB_REPOSITORY" \ - --prerelease \ - --title "URnetwork iOS beta custom server ${{ github.run_number }}" \ - --generate-notes \ - urnetwork-ios/app/URnetwork-beta.ipa \ - urnetwork-ios/app/URnetwork-beta-macOS.zip From d41093152fcdcdc66f203d44908212ca4329de3a Mon Sep 17 00:00:00 2001 From: ryanmello07 Date: Tue, 21 Jul 2026 19:45:37 +0100 Subject: [PATCH 108/109] fix: strip fork-only leakage flagged in PR self-review Addresses the three review comments left on this PR before upstream submission: - app/URnetworkSdk/Package.swift: restore upstream's original doc comment and xcframework path (../../../sdk/... instead of the fork's ../../../urnetwork-sdk/...), undoing local dev pointers to the forked SDK checkout. - Remove the fork-only "Claim Seeker Token" flow from Settings: drop ConnectWalletProviderViewModel.claimSeekerTokenMessage, the SolanaSignMessageSheet .sheet(...) block in SettingsView wired to it, and the associated presentSigninWithSolanaSheet/ isSigningMessage/setIsSigningMessage state in SettingsViewModel. The unrelated login-flow usage of SolanaSignMessageSheet and the handleWalletDeepLink/.onOpenURL wallet AddAuth deep-link handling in Settings are untouched. --- app/URnetworkSdk/Package.swift | 6 ++---- .../Main/Account/Settings/SettingsView.swift | 16 ---------------- .../Account/Settings/SettingsViewModel.swift | 10 +--------- .../ConnectWalletProviderViewModel.swift | 1 - 4 files changed, 3 insertions(+), 30 deletions(-) diff --git a/app/URnetworkSdk/Package.swift b/app/URnetworkSdk/Package.swift index 49dad768..4b45d39b 100644 --- a/app/URnetworkSdk/Package.swift +++ b/app/URnetworkSdk/Package.swift @@ -1,9 +1,7 @@ // swift-tools-version:5.3 import PackageDescription -// Points to the local checkout of the forked SDK: -// https://github.com/Ryanmello07/urnetwork-sdk -// Build the XCFramework from that repo before using this package. +// see https://developer.apple.com/documentation/xcode/distributing-binary-frameworks-as-swift-packages let package = Package( name: "URnetworkSdk", products: [ @@ -15,7 +13,7 @@ let package = Package( targets: [ .binaryTarget( name: "URnetworkSdkBinary", - path: "../../../urnetwork-sdk/build/apple/URnetworkSdk.xcframework" + path: "../../../sdk/build/apple/URnetworkSdk.xcframework" ), .testTarget( name: "URnetworkSdkTests", diff --git a/app/network/Main/Account/Settings/SettingsView.swift b/app/network/Main/Account/Settings/SettingsView.swift index b7c5a2d5..2e5104ae 100644 --- a/app/network/Main/Account/Settings/SettingsView.swift +++ b/app/network/Main/Account/Settings/SettingsView.swift @@ -112,22 +112,6 @@ struct SettingsView: View { } } - .sheet(isPresented: $viewModel.presentSigninWithSolanaSheet) { - - SolanaSignMessageSheet( - isSigningMessage: viewModel.isSigningMessage, - setIsSigningMessage: viewModel.setIsSigningMessage, - signButtonText: "Confirm Seeker Token", - signButtonLabelText: "Claim multiplier", - message: connectWalletProviderViewModel.claimSeekerTokenMessage, - dismiss: { - viewModel.presentSigninWithSolanaSheet = false - } - ) - .environmentObject(themeManager) - .environmentObject(connectWalletProviderViewModel) - .presentationDetents([.height(148)]) - } .onOpenURL { url in handleWalletDeepLink(url) } diff --git a/app/network/Main/Account/Settings/SettingsViewModel.swift b/app/network/Main/Account/Settings/SettingsViewModel.swift index 76a53812..b06c395c 100644 --- a/app/network/Main/Account/Settings/SettingsViewModel.swift +++ b/app/network/Main/Account/Settings/SettingsViewModel.swift @@ -20,15 +20,7 @@ extension SettingsView { let api: UrApiServiceProtocol @Published var presentUpdateReferralNetworkSheet: Bool = false - - // MARK: - Solana/Wallet Sign-In (restored from upstream merge) - @Published var presentSigninWithSolanaSheet: Bool = false - @Published private(set) var isSigningMessage: Bool = false - - func setIsSigningMessage(_ isSigning: Bool) -> Void { - isSigningMessage = isSigning - } - + @Published var version: String = "" var networkUserViewModel: NetworkUserViewModel? diff --git a/app/network/Shared/ViewModels/ConnectWalletProviderViewModel.swift b/app/network/Shared/ViewModels/ConnectWalletProviderViewModel.swift index 987ebbb4..af751c7d 100644 --- a/app/network/Shared/ViewModels/ConnectWalletProviderViewModel.swift +++ b/app/network/Shared/ViewModels/ConnectWalletProviderViewModel.swift @@ -28,7 +28,6 @@ class ConnectWalletProviderViewModel: ObservableObject { var connectedWalletProvider: ConnectedWalletProvider? = nil let welcomeMessage = "Welcome to URnetwork" - let claimSeekerTokenMessage = "Claim point multiplier by holding Seeker Pre-order or Saga Genesis token" /** * Solflare From 7d0550a3ab3ae8ca2dbcc5d3de84d36d6e381a46 Mon Sep 17 00:00:00 2001 From: ryanmello07 Date: Tue, 21 Jul 2026 19:49:32 +0100 Subject: [PATCH 109/109] Remove orphaned "Confirm Seeker Token" localization entry Leftover from the removed fork-only Settings seeker-token sheet (SettingsView.swift/SettingsViewModel.swift, previous commit) -- this string-table entry had no remaining code reference. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01QtgqtCmKJRXdsQ5ktiqwkg --- .../Shared/Resources/Localizable.xcstrings | 167 ------------------ 1 file changed, 167 deletions(-) diff --git a/app/network/Shared/Resources/Localizable.xcstrings b/app/network/Shared/Resources/Localizable.xcstrings index ff73f85c..65ab2865 100644 --- a/app/network/Shared/Resources/Localizable.xcstrings +++ b/app/network/Shared/Resources/Localizable.xcstrings @@ -14788,173 +14788,6 @@ } } }, - "Confirm Seeker Token" : { - "comment" : "Used in Main/Account/Settings/SettingsView.swift.", - "localizations" : { - "ar" : { - "stringUnit" : { - "state" : "translated", - "value" : "تأكيد رمز Seeker" - } - }, - "cs" : { - "stringUnit" : { - "state" : "translated", - "value" : "Potvrďte Seeker Token" - } - }, - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Seeker-Token bestätigen" - } - }, - "el" : { - "stringUnit" : { - "state" : "translated", - "value" : "Επιβεβαίωση Token Seeker" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Confirm Seeker Token" - } - }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Confirmar Token de Seeker" - } - }, - "es-419" : { - "stringUnit" : { - "state" : "translated", - "value" : "Confirmar Token de Seeker" - } - }, - "es-MX" : { - "stringUnit" : { - "state" : "translated", - "value" : "Confirmar Token de Seeker" - } - }, - "fr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Confirmer le jeton Seeker" - } - }, - "he" : { - "stringUnit" : { - "state" : "translated", - "value" : "אמת אסימון Seeker" - } - }, - "hi" : { - "stringUnit" : { - "state" : "translated", - "value" : "सीकर टोकन की पुष्टि करें" - } - }, - "id" : { - "stringUnit" : { - "state" : "translated", - "value" : "Konfirmasi Token Seeker" - } - }, - "it" : { - "stringUnit" : { - "state" : "translated", - "value" : "Conferma Token Seeker" - } - }, - "ja" : { - "stringUnit" : { - "state" : "translated", - "value" : "シーカートークンを確認" - } - }, - "ko" : { - "stringUnit" : { - "state" : "translated", - "value" : "Seeker 토큰 확인" - } - }, - "nl" : { - "stringUnit" : { - "state" : "translated", - "value" : "Bevestig Seeker Token" - } - }, - "pl" : { - "stringUnit" : { - "state" : "translated", - "value" : "Potwierdź token Seeker" - } - }, - "pt" : { - "stringUnit" : { - "state" : "translated", - "value" : "Confirmar Token Seeker" - } - }, - "pt-BR" : { - "stringUnit" : { - "state" : "translated", - "value" : "Confirmar Token Seeker" - } - }, - "pt-PT" : { - "stringUnit" : { - "state" : "translated", - "value" : "Confirmar Token Seeker" - } - }, - "ru" : { - "stringUnit" : { - "state" : "translated", - "value" : "Подтвердить токен Seeker" - } - }, - "sv" : { - "stringUnit" : { - "state" : "translated", - "value" : "Bekräfta Seeker-token" - } - }, - "th" : { - "stringUnit" : { - "state" : "translated", - "value" : "ยืนยัน Seeker Token" - } - }, - "uk" : { - "stringUnit" : { - "state" : "translated", - "value" : "Підтвердити токен Seeker" - } - }, - "vi" : { - "stringUnit" : { - "state" : "translated", - "value" : "Xác nhận Token Seeker" - } - }, - "zh-Hans" : { - "stringUnit" : { - "state" : "translated", - "value" : "确认 Seeker 令牌" - } - }, - "zh-HK" : { - "stringUnit" : { - "state" : "translated", - "value" : "確認 Seeker 代幣" - } - } - } - }, "Connect" : { "comment" : "Button text to connect to URnetwork VPN", "localizations" : {