diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c5e58181f..4bdf6f1d5 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -37,12 +37,12 @@ jobs: echo "GitHub event: $GITHUB_EVENT" - name: Check out repository - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 with: submodules: 'true' - name: Set Up Ruby - uses: ruby/setup-ruby@6bd3d993c602f6b675728ebaecb2b569ff86e99b # v1.174.0 + uses: ruby/setup-ruby@cacc9f1c0b3f4eb8a16a6bb0ed10897b43b9de49 # v1.176.0 with: bundler-cache: true ruby-version: 3.2.2 @@ -214,6 +214,10 @@ jobs: run: | plutil -replace ITSEncryptionExportComplianceCode -string 3dd3e32f-efa6-4d99-b410-28aa28b1cb77 Bitwarden/Application/Support/Info.plist + - name: Update APNS entitlements + run: | + plutil -replace aps-environment -string production Bitwarden/Application/Support/Bitwarden.entitlements + - name: Install Mint, xcbeautify, and yq run: | brew install mint xcbeautify yq @@ -243,7 +247,7 @@ jobs: ./Scripts/build.sh - name: Upload IPA - uses: actions/upload-artifact@1746f4ab65b179e0ea60a494b83293b640dd5bba # v4.3.2 + uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3 with: name: Bitwarden iOS path: build/Bitwarden/Bitwarden.ipa diff --git a/.github/workflows/cache-dependencies.yml b/.github/workflows/cache-dependencies.yml index c53e56984..0c9f4497c 100644 --- a/.github/workflows/cache-dependencies.yml +++ b/.github/workflows/cache-dependencies.yml @@ -17,10 +17,10 @@ jobs: runs-on: macos-14 steps: - name: Checkout - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 - name: Setup Ruby - uses: ruby/setup-ruby@6bd3d993c602f6b675728ebaecb2b569ff86e99b # v1.174.0 + uses: ruby/setup-ruby@cacc9f1c0b3f4eb8a16a6bb0ed10897b43b9de49 # v1.176.0 with: bundler-cache: true ruby-version: 3.2.2 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 58fb24479..119e2188c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -16,10 +16,10 @@ jobs: runs-on: macos-14-xlarge steps: - name: Checkout - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 - name: Setup Ruby - uses: ruby/setup-ruby@6bd3d993c602f6b675728ebaecb2b569ff86e99b # v1.174.0 + uses: ruby/setup-ruby@cacc9f1c0b3f4eb8a16a6bb0ed10897b43b9de49 # v1.176.0 with: bundler-cache: true ruby-version: 3.2.2 diff --git a/Bitwarden/Application/AppDelegate.swift b/Bitwarden/Application/AppDelegate.swift index d4230ce07..cb69624c2 100644 --- a/Bitwarden/Application/AppDelegate.swift +++ b/Bitwarden/Application/AppDelegate.swift @@ -62,6 +62,14 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD appProcessor?.failedToRegister(error) } + func application( + _ application: UIApplication, + didReceiveRemoteNotification userInfo: [AnyHashable: Any] + ) async -> UIBackgroundFetchResult { + await appProcessor?.messageReceived(userInfo) + return .newData + } + /// Received a response to a push notification alert. func userNotificationCenter( _: UNUserNotificationCenter, diff --git a/BitwardenShared/Core/Auth/Models/Enum/TwoFactorAuthMethod.swift b/BitwardenShared/Core/Auth/Models/Enum/TwoFactorAuthMethod.swift index ab75fe479..fc210e3e6 100644 --- a/BitwardenShared/Core/Auth/Models/Enum/TwoFactorAuthMethod.swift +++ b/BitwardenShared/Core/Auth/Models/Enum/TwoFactorAuthMethod.swift @@ -99,7 +99,7 @@ enum TwoFactorAuthMethod: Int { case .yubiKey: Localizations.yubiKeyInstructionIos case .webAuthn: - Localizations.continueToCompleteWebAuthnVerfication + Localizations.continueToCompleteWebAuthnVerification default: "" } diff --git a/BitwardenShared/Core/Auth/Repositories/AuthRepository.swift b/BitwardenShared/Core/Auth/Repositories/AuthRepository.swift index 2bdcd2aef..8f532d3ca 100644 --- a/BitwardenShared/Core/Auth/Repositories/AuthRepository.swift +++ b/BitwardenShared/Core/Auth/Repositories/AuthRepository.swift @@ -297,6 +297,9 @@ class DefaultAuthRepository { /// The service that handles common client functionality such as encryption and decryption. private let clientService: ClientService + /// The services to get server-specified configuration. + private let configService: ConfigService + /// The service used by the application to manage the environment settings. private let environmentService: EnvironmentService @@ -330,6 +333,7 @@ class DefaultAuthRepository { /// - authService: The service used that handles some of the auth logic. /// - biometricsRepository: The service to use system Biometrics for vault unlock. /// - clientService: The service that handles common client functionality such as encryption and decryption. + /// - configService: The service to get server-specified configuration. /// - environmentService: The service used by the application to manage the environment settings. /// - keychainService: The keychain service used by the application. /// - organizationAPIService: The service used by the application to make organization-related API requests. @@ -345,6 +349,7 @@ class DefaultAuthRepository { authService: AuthService, biometricsRepository: BiometricsRepository, clientService: ClientService, + configService: ConfigService, environmentService: EnvironmentService, keychainService: KeychainRepository, organizationAPIService: OrganizationAPIService, @@ -358,6 +363,7 @@ class DefaultAuthRepository { self.authService = authService self.biometricsRepository = biometricsRepository self.clientService = clientService + self.configService = configService self.environmentService = environmentService self.keychainService = keychainService self.organizationAPIService = organizationAPIService @@ -394,6 +400,13 @@ extension DefaultAuthRepository: AuthRepository { publicKey: registrationKeys.publicKey )) + try await stateService.setAccountEncryptionKeys( + AccountEncryptionKeys( + encryptedPrivateKey: registrationKeys.privateKey, + encryptedUserKey: nil + ) + ) + try await organizationUserAPIService.organizationUserResetPasswordEnrollment( organizationId: enrollStatus.id, requestModel: OrganizationUserResetPasswordEnrollmentRequestModel( @@ -520,6 +533,7 @@ extension DefaultAuthRepository: AuthRepository { func setActiveAccount(userId: String) async throws -> Account { try await stateService.setActiveAccount(userId: userId) await environmentService.loadURLsForActiveAccount() + _ = await configService.getConfig() return try await stateService.getActiveAccount() } @@ -607,7 +621,7 @@ extension DefaultAuthRepository: AuthRepository { func setPins(_ pin: String, requirePasswordAfterRestart: Bool) async throws { let pinKey = try await clientService.crypto().derivePinKey(pin: pin) try await stateService.setPinKeys( - pinKeyEncryptedUserKey: pinKey.encryptedPin, + encryptedPin: pinKey.encryptedPin, pinProtectedUserKey: pinKey.pinProtectedUserKey, requirePasswordAfterRestart: requirePasswordAfterRestart ) @@ -808,9 +822,9 @@ extension DefaultAuthRepository: AuthRepository { // If the user has a pin, but requires master password after restart, set the pin // protected user key in memory for future unlocks prior to app restart. - if let pinKeyEncryptedUserKey = try await stateService.pinKeyEncryptedUserKey() { + if let encryptedPin = try await stateService.getEncryptedPin() { let pinProtectedUserKey = try await clientService.crypto().derivePinUserKey( - encryptedPin: pinKeyEncryptedUserKey + encryptedPin: encryptedPin ) try await stateService.setPinProtectedUserKeyToMemory(pinProtectedUserKey) } diff --git a/BitwardenShared/Core/Auth/Repositories/AuthRepositoryTests.swift b/BitwardenShared/Core/Auth/Repositories/AuthRepositoryTests.swift index eb4a6edc6..e21bcfc6d 100644 --- a/BitwardenShared/Core/Auth/Repositories/AuthRepositoryTests.swift +++ b/BitwardenShared/Core/Auth/Repositories/AuthRepositoryTests.swift @@ -12,6 +12,7 @@ class AuthRepositoryTests: BitwardenTestCase { // swiftlint:disable:this type_bo var biometricsRepository: MockBiometricsRepository! var client: MockHTTPClient! var clientService: MockClientService! + var configService: MockConfigService! var environmentService: MockEnvironmentService! var keychainService: MockKeychainRepository! var organizationService: MockOrganizationService! @@ -86,6 +87,7 @@ class AuthRepositoryTests: BitwardenTestCase { // swiftlint:disable:this type_bo accountAPIService = APIService(client: client) authService = MockAuthService() biometricsRepository = MockBiometricsRepository() + configService = MockConfigService() environmentService = MockEnvironmentService() keychainService = MockKeychainRepository() organizationService = MockOrganizationService() @@ -98,6 +100,7 @@ class AuthRepositoryTests: BitwardenTestCase { // swiftlint:disable:this type_bo authService: authService, biometricsRepository: biometricsRepository, clientService: clientService, + configService: configService, environmentService: environmentService, keychainService: keychainService, organizationAPIService: APIService(client: client), @@ -117,6 +120,7 @@ class AuthRepositoryTests: BitwardenTestCase { // swiftlint:disable:this type_bo biometricsRepository = nil client = nil clientService = nil + configService = nil environmentService = nil keychainService = nil organizationService = nil @@ -133,12 +137,12 @@ class AuthRepositoryTests: BitwardenTestCase { // swiftlint:disable:this type_bo let userId = Account.fixture().profile.userId stateService.pinProtectedUserKeyValue[userId] = "123" - stateService.pinKeyEncryptedUserKeyValue[userId] = "123" + stateService.encryptedPinByUserId[userId] = "123" stateService.accountVolatileData[userId]?.pinProtectedUserKey = "123" try await subject.clearPins() XCTAssertNil(stateService.pinProtectedUserKeyValue[userId]) - XCTAssertNil(stateService.pinKeyEncryptedUserKeyValue[userId]) + XCTAssertNil(stateService.encryptedPinByUserId[userId]) XCTAssertNil(stateService.accountVolatileData[userId]?.pinProtectedUserKey) } @@ -171,6 +175,10 @@ class AuthRepositoryTests: BitwardenTestCase { // swiftlint:disable:this type_bo XCTAssertEqual(clientService.mockAuth.makeRegisterTdeKeysEmail, "user@bitwarden.com") XCTAssertEqual(clientService.mockAuth.makeRegisterTdeKeysOrgPublicKey, "MIIBIjAN...2QIDAQAB") XCTAssertEqual(clientService.mockAuth.makeRegisterTdeKeysRememberDevice, true) + XCTAssertEqual( + stateService.accountEncryptionKeys["1"], + AccountEncryptionKeys(encryptedPrivateKey: "privateKey", encryptedUserKey: nil) + ) } /// `createNewSsoUser()` creates a new account for sso JIT user and don't trust device. @@ -200,6 +208,10 @@ class AuthRepositoryTests: BitwardenTestCase { // swiftlint:disable:this type_bo XCTAssertNil(trustDeviceService.trustDeviceWithExistingKeysValue) XCTAssertEqual(clientService.mockAuth.makeRegisterTdeKeysOrgPublicKey, "MIIBIjAN...2QIDAQAB") XCTAssertEqual(clientService.mockAuth.makeRegisterTdeKeysRememberDevice, false) + XCTAssertEqual( + stateService.accountEncryptionKeys["1"], + AccountEncryptionKeys(encryptedPrivateKey: "privateKey", encryptedUserKey: nil) + ) } /// `deleteAccount()` deletes the active account and removes it from the state. @@ -1029,7 +1041,7 @@ class AuthRepositoryTests: BitwardenTestCase { // swiftlint:disable:this type_bo let userId = account.profile.userId try await subject.setPins("123", requirePasswordAfterRestart: true) XCTAssertEqual(stateService.pinProtectedUserKeyValue[userId], "12") - XCTAssertEqual(stateService.pinKeyEncryptedUserKeyValue[userId], "34") + XCTAssertEqual(stateService.encryptedPinByUserId[userId], "34") XCTAssertEqual(stateService.accountVolatileData[ userId, default: AccountVolatileData() @@ -1299,7 +1311,7 @@ class AuthRepositoryTests: BitwardenTestCase { // swiftlint:disable:this type_bo "1": AccountEncryptionKeys(encryptedPrivateKey: "PRIVATE_KEY", encryptedUserKey: "USER_KEY"), ] - stateService.pinKeyEncryptedUserKeyValue[account.profile.userId] = "123" + stateService.encryptedPinByUserId[account.profile.userId] = "123" stateService.pinProtectedUserKeyValue[account.profile.userId] = "123" await assertAsyncDoesNotThrow { diff --git a/BitwardenShared/Core/Auth/Services/API/Auth/Requests/CheckLoginRequestRequest.swift b/BitwardenShared/Core/Auth/Services/API/Auth/Requests/CheckLoginRequestRequest.swift index 89a20294f..52c224232 100644 --- a/BitwardenShared/Core/Auth/Services/API/Auth/Requests/CheckLoginRequestRequest.swift +++ b/BitwardenShared/Core/Auth/Services/API/Auth/Requests/CheckLoginRequestRequest.swift @@ -1,6 +1,12 @@ import Foundation import Networking +/// Errors thrown from validating a `CheckLoginRequestRequest` response. +enum CheckLoginRequestError: Error { + /// The request has expired. + case expired +} + // MARK: - CheckLoginRequestRequest /// A request for checking the status of a login request for an unauthenticated user. @@ -24,4 +30,12 @@ struct CheckLoginRequestRequest: Request { /// The query item for this request. var query: [URLQueryItem] { [URLQueryItem(name: "code", value: accessCode)] } + + // MARK: Request + + func validate(_ response: HTTPResponse) throws { + if response.statusCode == 404 { + throw CheckLoginRequestError.expired + } + } } diff --git a/BitwardenShared/Core/Auth/Services/API/Auth/Requests/CheckLoginRequestRequestTests.swift b/BitwardenShared/Core/Auth/Services/API/Auth/Requests/CheckLoginRequestRequestTests.swift new file mode 100644 index 000000000..b47e31eb7 --- /dev/null +++ b/BitwardenShared/Core/Auth/Services/API/Auth/Requests/CheckLoginRequestRequestTests.swift @@ -0,0 +1,50 @@ +import Networking +import XCTest + +@testable import BitwardenShared + +class CheckLoginRequestRequestTests: BitwardenTestCase { + // MARK: Properties + + var subject: CheckLoginRequestRequest! + + // MARK: Setup & Teardown + + override func setUp() { + super.setUp() + subject = CheckLoginRequestRequest(accessCode: "ACCESS_CODE", id: "ID") + } + + override func tearDown() { + super.tearDown() + subject = nil + } + + // MARK: Tests + + /// `method` returns the method of the request. + func test_method() { + XCTAssertEqual(subject.method, .get) + } + + /// `path` returns the path of the request. + func test_path() { + XCTAssertEqual(subject.path, "/auth-requests/ID/response") + } + + /// `query` returns the query items of the request. + func test_query() { + XCTAssertEqual(subject.query, [URLQueryItem(name: "code", value: "ACCESS_CODE")]) + } + + /// `validate(_:)` validates the response for the request and throws an error if the request is expired. + func test_validate() { + XCTAssertNoThrow(try subject.validate(.success())) + XCTAssertNoThrow(try subject.validate(.failure(statusCode: 400))) + XCTAssertNoThrow(try subject.validate(.failure(statusCode: 500))) + + XCTAssertThrowsError(try subject.validate(.failure(statusCode: 404))) { error in + XCTAssertEqual(error as? CheckLoginRequestError, .expired) + } + } +} diff --git a/BitwardenShared/Core/Platform/Models/Domain/ServerConfig.swift b/BitwardenShared/Core/Platform/Models/Domain/ServerConfig.swift new file mode 100644 index 000000000..75d8dc4c9 --- /dev/null +++ b/BitwardenShared/Core/Platform/Models/Domain/ServerConfig.swift @@ -0,0 +1,91 @@ +import Foundation + +// MARK: - ServerConfig + +/// Model that represents the configuration provided by the server at a particular time. +/// +struct ServerConfig: Equatable, Codable { + // MARK: Properties + + /// The environment URLs of the server. + let environment: EnvironmentServerConfig? + + /// The particular time of the server configuration. + let date: Date + + /// Feature flags to configure the client. + let featureStates: [FeatureFlag: AnyCodable] + + /// The git hash of the server. + let gitHash: String + + /// Third party server information. + let server: ThirdPartyServerConfig? + + /// The version of the server. + let version: String + + init(date: Date, responseModel: ConfigResponseModel) { + environment = responseModel.environment.map(EnvironmentServerConfig.init) + self.date = date + let features: [(FeatureFlag, AnyCodable)] + features = responseModel.featureStates.compactMap { key, value in + guard let flag = FeatureFlag(rawValue: key) else { return nil } + return (flag, value) + } + featureStates = Dictionary(uniqueKeysWithValues: features) + + gitHash = responseModel.gitHash + server = responseModel.server.map(ThirdPartyServerConfig.init) + version = responseModel.version + } +} + +// MARK: - ThirdPartyServerConfig + +/// Model for third-party configuration of the server. +/// +struct ThirdPartyServerConfig: Equatable, Codable { + /// The name of the third party configuration. + let name: String + + /// The URL of the third party configuration. + let url: String + + init(responseModel: ThirdPartyConfigResponseModel) { + name = responseModel.name + url = responseModel.url + } +} + +// MARK: - EnvironmentServerConfig + +/// Model for the environment URLs in a server configuration. +struct EnvironmentServerConfig: Equatable, Codable { + /// The API URL. + let api: String? + + /// The Cloud Region (e.g. "US") + let cloudRegion: String? + + /// The Identity URL. + let identity: String? + + /// The Notifications URL. + let notifications: String? + + /// The SSO URL. + let sso: String? + + /// The Vault URL. + let vault: String? + + init(responseModel: EnvironmentServerConfigResponseModel) { + api = responseModel.api + cloudRegion = responseModel.cloudRegion + identity = responseModel.identity + notifications = responseModel.notifications + sso = responseModel.sso + vault = responseModel.vault + } +} diff --git a/BitwardenShared/Core/Platform/Models/Domain/ServerConfigTests.swift b/BitwardenShared/Core/Platform/Models/Domain/ServerConfigTests.swift new file mode 100644 index 000000000..22fb6fd24 --- /dev/null +++ b/BitwardenShared/Core/Platform/Models/Domain/ServerConfigTests.swift @@ -0,0 +1,26 @@ +import Foundation +import XCTest + +@testable import BitwardenShared + +final class ServerConfigTests: BitwardenTestCase { + // MARK: Tests + + /// `init` properly converts feature flags + func test_init_featureFlags() { + let model = ConfigResponseModel( + environment: nil, + featureStates: [ + "vault-onboarding": .bool(true), + "unassigned-items-banner": .bool(false), + "not-a-real-feature-flag": .int(42), + ], + gitHash: "123", + server: nil, + version: "1.2.3" + ) + + let subject = ServerConfig(date: Date(), responseModel: model) + XCTAssertEqual(subject.featureStates, [.unassignedItemsBanner: .bool(false)]) + } +} diff --git a/BitwardenShared/Core/Platform/Models/Enum/FeatureFlag.swift b/BitwardenShared/Core/Platform/Models/Enum/FeatureFlag.swift new file mode 100644 index 000000000..710482041 --- /dev/null +++ b/BitwardenShared/Core/Platform/Models/Enum/FeatureFlag.swift @@ -0,0 +1,9 @@ +import Foundation + +// MARK: - FeatureFlag + +/// An enum to represent a feature flag sent by the server +/// +enum FeatureFlag: String, Codable { + case unassignedItemsBanner = "unassigned-items-banner" +} diff --git a/BitwardenShared/Core/Platform/Services/API/APIService.swift b/BitwardenShared/Core/Platform/Services/API/APIService.swift index b7d5f317d..223f8c2b9 100644 --- a/BitwardenShared/Core/Platform/Services/API/APIService.swift +++ b/BitwardenShared/Core/Platform/Services/API/APIService.swift @@ -38,7 +38,7 @@ class APIService { tokenService: TokenService ) { let defaultHeadersRequestHandler = DefaultHeadersRequestHandler( - appName: Bundle.main.appName, + appName: "Bitwarden_Mobile", appVersion: Bundle.main.appVersion, buildNumber: Bundle.main.buildNumber, systemDevice: UIDevice.current diff --git a/BitwardenShared/Core/Platform/Services/API/ConfigAPIService.swift b/BitwardenShared/Core/Platform/Services/API/ConfigAPIService.swift index 4e4c7dfb0..42bb2d5b1 100644 --- a/BitwardenShared/Core/Platform/Services/API/ConfigAPIService.swift +++ b/BitwardenShared/Core/Platform/Services/API/ConfigAPIService.swift @@ -1,5 +1,15 @@ +import Networking + /// A protocol for an API service used to make config requests. /// -protocol ConfigAPIService {} +protocol ConfigAPIService { + /// Performs an API request to get the configuration from the backend. + /// + func getConfig() async throws -> ConfigResponseModel +} -extension APIService: ConfigAPIService {} +extension APIService: ConfigAPIService { + func getConfig() async throws -> ConfigResponseModel { + try await apiUnauthenticatedService.send(ConfigRequest()) + } +} diff --git a/BitwardenShared/Core/Platform/Services/API/ConfigAPIServiceTests.swift b/BitwardenShared/Core/Platform/Services/API/ConfigAPIServiceTests.swift new file mode 100644 index 000000000..cb92bea62 --- /dev/null +++ b/BitwardenShared/Core/Platform/Services/API/ConfigAPIServiceTests.swift @@ -0,0 +1,40 @@ +import XCTest + +@testable import BitwardenShared + +class ConfigAPIServiceTests: BitwardenTestCase { + // MARK: Properties + + var client: MockHTTPClient! + var subject: ConfigAPIService! + + // MARK: Setup & Teardown + + override func setUp() { + super.setUp() + + client = MockHTTPClient() + subject = APIService(client: client) + } + + override func tearDown() { + super.tearDown() + + client = nil + subject = nil + } + + // MARK: Tests + + /// `getConfig()` performs the config request. + func test_getConfig() async throws { + client.result = .httpSuccess(testData: .validServerConfig) + + _ = try await subject.getConfig() + + let request = try XCTUnwrap(client.requests.last) + XCTAssertEqual(request.method, .get) + XCTAssertEqual(request.url.absoluteString, "https://example.com/api/config") + XCTAssertNil(request.body) + } +} diff --git a/BitwardenShared/Core/Platform/Services/API/Fixtures/APITestData+Config.swift b/BitwardenShared/Core/Platform/Services/API/Fixtures/APITestData+Config.swift new file mode 100644 index 000000000..a40e7a012 --- /dev/null +++ b/BitwardenShared/Core/Platform/Services/API/Fixtures/APITestData+Config.swift @@ -0,0 +1,7 @@ +import Foundation + +extension APITestData { + // MARK: Server Configuration + + static let validServerConfig = loadFromJsonBundle(resource: "ValidServerConfig") +} diff --git a/BitwardenShared/Core/Platform/Services/API/Fixtures/ValidServerConfig.json b/BitwardenShared/Core/Platform/Services/API/Fixtures/ValidServerConfig.json new file mode 100644 index 000000000..317aa54fc --- /dev/null +++ b/BitwardenShared/Core/Platform/Services/API/Fixtures/ValidServerConfig.json @@ -0,0 +1,36 @@ +{ + "version": "2024.4.2", + "gitHash": "75238191", + "server": null, + "environment": { + "cloudRegion": "US", + "vault": "https://vault.qa.bitwarden.pw", + "api": "https://api.qa.bitwarden.pw", + "identity": "https://identity.qa.bitwarden.pw", + "notifications": "https://notifications.qa.bitwarden.pw", + "sso": "https://sso.qa.bitwarden.pw" + }, + "featureStates": { + "display-eu-environment": true, + "display-kdf-iteration-warning": false, + "passwordless-login": true, + "trusted-device-encryption": true, + "fido2-vault-credentials": true, + "vault-onboarding": true, + "browser-fileless-import": true, + "flexible-collections-v-1": false, + "key-rotation-improvements": true, + "duo-redirect": true, + "flexible-collections-signup": true, + "flexible-collections-migration": true, + "PM-5766-automatic-tax": true, + "PM-5864-dollar-threshold": false, + "show-payment-method-warning-banners": false, + "AC-2101-update-trial-initiation-email": true, + "enable-consolidated-billing": false, + "AC-1795_updated-subscription-status-section": true, + "unassigned-items-banner": true, + "AC-1218-delete-provider": false + }, + "object": "config" +} diff --git a/BitwardenShared/Core/Platform/Services/API/Requests/ConfigRequest.swift b/BitwardenShared/Core/Platform/Services/API/Requests/ConfigRequest.swift new file mode 100644 index 000000000..d0257e592 --- /dev/null +++ b/BitwardenShared/Core/Platform/Services/API/Requests/ConfigRequest.swift @@ -0,0 +1,13 @@ +import Networking + +// MARK: - ConfigRequest + +/// Data model for fetching configuration values from the server. +/// +struct ConfigRequest: Request { + typealias Response = ConfigResponseModel + + let method = HTTPMethod.get + + let path = "/config" +} diff --git a/BitwardenShared/Core/Platform/Services/API/Response/ConfigResponseModel.swift b/BitwardenShared/Core/Platform/Services/API/Response/ConfigResponseModel.swift new file mode 100644 index 000000000..467ba61cd --- /dev/null +++ b/BitwardenShared/Core/Platform/Services/API/Response/ConfigResponseModel.swift @@ -0,0 +1,55 @@ +import Foundation +import Networking + +// MARK: - ConfigResponseModel + +/// API response model for the configuration request. +/// +struct ConfigResponseModel: Equatable, JSONResponse { + // MARK: Properties + + /// The environment URLs of the server. + let environment: EnvironmentServerConfigResponseModel? + + /// Feature flags to configure the client. + let featureStates: [String: AnyCodable] + + /// The git hash of the server. + let gitHash: String + + /// Third party server information. + let server: ThirdPartyConfigResponseModel? + + /// The version of the server. + let version: String +} + +/// API response model for third-party configuration in a configuration response. +struct ThirdPartyConfigResponseModel: Equatable, JSONResponse { + /// The name of the third party configuration. + let name: String + + /// The URL of the third party configuration. + let url: String +} + +/// API response model for the environment URLs in a configuration response. +struct EnvironmentServerConfigResponseModel: Equatable, JSONResponse { + /// The API URL. + let api: String? + + /// The Cloud Region (e.g. "US") + let cloudRegion: String? + + /// The Identity URL. + let identity: String? + + /// The Notifications URL. + let notifications: String? + + /// The SSO URL. + let sso: String? + + /// The Vault URL. + let vault: String? +} diff --git a/BitwardenShared/Core/Platform/Services/ConfigService.swift b/BitwardenShared/Core/Platform/Services/ConfigService.swift new file mode 100644 index 000000000..4bb461c39 --- /dev/null +++ b/BitwardenShared/Core/Platform/Services/ConfigService.swift @@ -0,0 +1,143 @@ +import Foundation +import OSLog + +// MARK: - ConfigService + +/// A protocol for a `ConfigService` that manages the app's config. +/// +protocol ConfigService { + /// Retrieves the current configuration. This will return the on-disk configuration if available, + /// or will retrieve it from the server if not. It will also retrieve the configuration from + /// the server if it is outdated or if the `forceRefresh` argument is `true`. Configurations + /// retrieved from the server are saved to disk. + /// + /// - Parameters: + /// - forceRefresh: If true, forces refreshing the configuration from the server. + /// - Returns: A server configuration if able. + /// + func getConfig(forceRefresh: Bool) async -> ServerConfig? + + /// Retrieves a boolean feature flag. This will use the on-disk configuration if available, + /// or will retrieve it from the server if not. It will also retrieve the configuration from + /// the server if it is outdated or if the `forceRefresh` argument is `true`. + /// + /// - Parameters: + /// - flag: The feature flag to retrieve + /// - defaultValue: The default value to use if the flag is not in the server configuration + /// - forceRefresh: If true, forces refreshing the configuration from the server before retrieval + /// - Returns: The value for the feature flag + func getFeatureFlag(_ flag: FeatureFlag, defaultValue: Bool, forceRefresh: Bool) async -> Bool + + /// Retrieves an integer feature flag. This will use the on-disk configuration if available, + /// or will retrieve it from the server if not. It will also retrieve the configuration from + /// the server if it is outdated or if the `forceRefresh` argument is `true`. + /// + /// - Parameters: + /// - flag: The feature flag to retrieve + /// - defaultValue: The default value to use if the flag is not in the server configuration + /// - forceRefresh: If true, forces refreshing the configuration from the server before retrieval + /// - Returns: The value for the feature flag + func getFeatureFlag(_ flag: FeatureFlag, defaultValue: Int, forceRefresh: Bool) async -> Int + + /// Retrieves a string feature flag. This will use the on-disk configuration if available, + /// or will retrieve it from the server if not. It will also retrieve the configuration from + /// the server if it is outdated or if the `forceRefresh` argument is `true`. + /// + /// - Parameters: + /// - flag: The feature flag to retrieve + /// - defaultValue: The default value to use if the flag is not in the server configuration + /// - forceRefresh: If true, forces refreshing the configuration from the server before retrieval + /// - Returns: The value for the feature flag + func getFeatureFlag(_ flag: FeatureFlag, defaultValue: String?, forceRefresh: Bool) async -> String? +} + +extension ConfigService { + func getConfig() async -> ServerConfig? { + await getConfig(forceRefresh: false) + } +} + +// MARK: - DefaultConfigService + +/// A default implementation of a `ConfigService` that manages the app's config. +/// +class DefaultConfigService: ConfigService { + // MARK: Properties + + /// The API service to make config requests. + private let configApiService: ConfigAPIService + + /// The service used by the application to report non-fatal errors. + private let errorReporter: ErrorReporter + + /// The service used by the application to manage account state. + private let stateService: StateService + + /// The service used to get the present time. + private let timeProvider: TimeProvider + + // MARK: Initialization + + /// Initialize a `DefaultConfigService`. + /// + /// - Parameters: + /// - configApiService: The API service to make config requests. + /// - errorReporter: The service used by the application to report non-fatal errors. + /// - stateService: The service used by the application to manage account state. + /// - timeProvider: The services used to get the present time. + /// + init( + configApiService: ConfigAPIService, + errorReporter: ErrorReporter, + stateService: StateService, + timeProvider: TimeProvider + ) { + self.configApiService = configApiService + self.errorReporter = errorReporter + self.stateService = stateService + self.timeProvider = timeProvider + } + + // MARK: Methods + + func getConfig(forceRefresh: Bool) async -> ServerConfig? { + let localConfig = try? await stateService.getServerConfig() + + let localConfigExpired = localConfig?.date.addingTimeInterval(Constants.minimumConfigSyncInterval) + ?? Date.distantPast + < timeProvider.presentTime + + if forceRefresh || localConfig == nil || localConfigExpired { + do { + let configResponse = try await configApiService.getConfig() + let serverConfig = ServerConfig( + date: timeProvider.presentTime, + responseModel: configResponse + ) + try? await stateService.setServerConfig(serverConfig) + return serverConfig + } catch { + errorReporter.log(error: error) + } + } + + // If we are unable to retrieve a configuration from the server, + // fall back to the local configuration. + return localConfig + } + + func getFeatureFlag(_ flag: FeatureFlag, defaultValue: Bool = false, forceRefresh: Bool = false) async -> Bool { + let configuration = await getConfig(forceRefresh: forceRefresh) + return configuration?.featureStates[flag]?.boolValue ?? defaultValue + } + + func getFeatureFlag(_ flag: FeatureFlag, defaultValue: Int = 0, forceRefresh: Bool = false) async -> Int { + let configuration = await getConfig(forceRefresh: forceRefresh) + return configuration?.featureStates[flag]?.intValue ?? defaultValue + } + + func getFeatureFlag(_ flag: FeatureFlag, defaultValue: String? = nil, forceRefresh: Bool = false) async -> String? { + let configuration = await getConfig(forceRefresh: forceRefresh) + return configuration?.featureStates[flag]?.stringValue ?? defaultValue + } +} diff --git a/BitwardenShared/Core/Platform/Services/ConfigServiceTests.swift b/BitwardenShared/Core/Platform/Services/ConfigServiceTests.swift new file mode 100644 index 000000000..04b05b2e9 --- /dev/null +++ b/BitwardenShared/Core/Platform/Services/ConfigServiceTests.swift @@ -0,0 +1,203 @@ +import XCTest + +@testable import BitwardenShared + +final class ConfigServiceTests: BitwardenTestCase { + // MARK: Properties + + var client: MockHTTPClient! + var configApiService: APIService! + var errorReporter: MockErrorReporter! + var now: Date! + var stateService: MockStateService! + var subject: DefaultConfigService! + var timeProvider: MockTimeProvider! + + // MARK: Setup & Teardown + + override func setUp() { + super.setUp() + + client = MockHTTPClient() + configApiService = APIService(client: client) + errorReporter = MockErrorReporter() + now = Date(year: 2024, month: 2, day: 14, hour: 8, minute: 0, second: 0) + stateService = MockStateService() + timeProvider = MockTimeProvider(.mockTime(now)) + subject = DefaultConfigService( + configApiService: configApiService, + errorReporter: errorReporter, + stateService: stateService, + timeProvider: timeProvider + ) + let account = Account.fixture(profile: Account.AccountProfile.fixture(userId: "1")) + stateService.activeAccount = account + } + + override func tearDown() { + super.tearDown() + + stateService = nil + subject = nil + } + + // MARK: Tests + + /// `getConfig(:)` gets the configuration from the server if `forceRefresh` is true + func test_getConfig_local_forceRefresh() async { + stateService.serverConfig["1"] = ServerConfig( + date: Date(year: 2024, month: 2, day: 14, hour: 7, minute: 50, second: 0), + responseModel: ConfigResponseModel( + environment: nil, + featureStates: [:], + gitHash: "75238192", + server: nil, + version: "2024.4.0" + ) + ) + client.result = .httpSuccess(testData: .validServerConfig) + let response = await subject.getConfig(forceRefresh: true) + XCTAssertEqual(client.requests.count, 1) + XCTAssertEqual(response?.gitHash, "75238191") + } + + /// `getConfig(:)` uses the local configuration if it is expired + func test_getConfig_local_expired() async { + stateService.serverConfig["1"] = ServerConfig( + date: Date(year: 2024, month: 2, day: 10, hour: 8, minute: 0, second: 0), + responseModel: ConfigResponseModel( + environment: nil, + featureStates: [:], + gitHash: "75238192", + server: nil, + version: "2024.4.0" + ) + ) + client.result = .httpSuccess(testData: .validServerConfig) + let response = await subject.getConfig(forceRefresh: false) + XCTAssertEqual(client.requests.count, 1) + XCTAssertEqual(response?.gitHash, "75238191") + } + + /// `getConfig(:)` uses the local configuration if it's not expired + func test_getConfig_local_notExpired() async { + stateService.serverConfig["1"] = ServerConfig( + date: Date(year: 2024, month: 2, day: 14, hour: 7, minute: 50, second: 0), + responseModel: ConfigResponseModel( + environment: nil, + featureStates: [:], + gitHash: "75238192", + server: nil, + version: "2024.4.0" + ) + ) + let response = await subject.getConfig(forceRefresh: false) + XCTAssertEqual(client.requests.count, 0) + XCTAssertEqual(response?.gitHash, "75238192") + } + + /// `getConfig(:)` gets the configuration from the server if there is no local configuration + func test_getConfig_noLocal() async { + client.result = .httpSuccess(testData: .validServerConfig) + let response = await subject.getConfig(forceRefresh: false) + XCTAssertEqual(client.requests.count, 1) + XCTAssertEqual(response?.gitHash, "75238191") + XCTAssertEqual(response?.featureStates[.unassignedItemsBanner], .bool(true)) + } + + /// `getFeatureFlag(:)` can return a boolean if it's in the configuration + func test_getFeatureFlag_bool_exists() async { + stateService.serverConfig["1"] = ServerConfig( + date: Date(year: 2024, month: 2, day: 14, hour: 7, minute: 50, second: 0), + responseModel: ConfigResponseModel( + environment: nil, + featureStates: ["unassigned-items-banner": .bool(true)], + gitHash: "75238191", + server: nil, + version: "2024.4.0" + ) + ) + let value = await subject.getFeatureFlag(.unassignedItemsBanner, defaultValue: false, forceRefresh: false) + XCTAssertTrue(value) + } + + /// `getFeatureFlag(:)` returns the default value for booleans + func test_getFeatureFlag_bool_fallback() async { + stateService.serverConfig["1"] = ServerConfig( + date: Date(year: 2024, month: 2, day: 14, hour: 7, minute: 50, second: 0), + responseModel: ConfigResponseModel( + environment: nil, + featureStates: [:], + gitHash: "75238191", + server: nil, + version: "2024.4.0" + ) + ) + let value = await subject.getFeatureFlag(.unassignedItemsBanner, defaultValue: true, forceRefresh: false) + XCTAssertTrue(value) + } + + /// `getFeatureFlag(:)` can return an integer if it's in the configuration + func test_getFeatureFlag_int_exists() async { + stateService.serverConfig["1"] = ServerConfig( + date: Date(year: 2024, month: 2, day: 14, hour: 7, minute: 50, second: 0), + responseModel: ConfigResponseModel( + environment: nil, + featureStates: ["unassigned-items-banner": .int(42)], + gitHash: "75238191", + server: nil, + version: "2024.4.0" + ) + ) + let value = await subject.getFeatureFlag(.unassignedItemsBanner, defaultValue: 30, forceRefresh: false) + XCTAssertEqual(value, 42) + } + + /// `getFeatureFlag(:)` returns the default value for integers + func test_getFeatureFlag_int_fallback() async { + stateService.serverConfig["1"] = ServerConfig( + date: Date(year: 2024, month: 2, day: 14, hour: 7, minute: 50, second: 0), + responseModel: ConfigResponseModel( + environment: nil, + featureStates: [:], + gitHash: "75238191", + server: nil, + version: "2024.4.0" + ) + ) + let value = await subject.getFeatureFlag(.unassignedItemsBanner, defaultValue: 30, forceRefresh: false) + XCTAssertEqual(value, 30) + } + + /// `getFeatureFlag(:)` can return a string if it's in the configuration + func test_getFeatureFlag_string_exists() async { + stateService.serverConfig["1"] = ServerConfig( + date: Date(year: 2024, month: 2, day: 14, hour: 7, minute: 50, second: 0), + responseModel: ConfigResponseModel( + environment: nil, + featureStates: ["unassigned-items-banner": .string("exists")], + gitHash: "75238191", + server: nil, + version: "2024.4.0" + ) + ) + let value = await subject.getFeatureFlag(.unassignedItemsBanner, defaultValue: "fallback", forceRefresh: false) + XCTAssertEqual(value, "exists") + } + + /// `getFeatureFlag(:)` returns the default value for booleans + func test_getFeatureFlag_string_fallback() async { + stateService.serverConfig["1"] = ServerConfig( + date: Date(year: 2024, month: 2, day: 14, hour: 7, minute: 50, second: 0), + responseModel: ConfigResponseModel( + environment: nil, + featureStates: [:], + gitHash: "75238191", + server: nil, + version: "2024.4.0" + ) + ) + let value = await subject.getFeatureFlag(.unassignedItemsBanner, defaultValue: "fallback", forceRefresh: false) + XCTAssertEqual(value, "fallback") + } +} diff --git a/BitwardenShared/Core/Platform/Services/MigrationService.swift b/BitwardenShared/Core/Platform/Services/MigrationService.swift index dc98842e3..c1529abf2 100644 --- a/BitwardenShared/Core/Platform/Services/MigrationService.swift +++ b/BitwardenShared/Core/Platform/Services/MigrationService.swift @@ -26,6 +26,10 @@ class DefaultMigrationService { /// The repository used to manage keychain items. let keychainRepository: KeychainRepository + /// The shared UserDefaults instance (NOTE: this should be the standard one just for the app, + /// not one in the app group). + let standardUserDefaults: UserDefaults + // MARK: Initialization /// Initialize a `DefaultMigrationService`. @@ -34,15 +38,18 @@ class DefaultMigrationService { /// - appSettingsStore: The service used by the application to persist app setting values. /// - errorReporter: The service used by the application to report non-fatal errors. /// - keychainRepository: The repository used to manage keychain items. + /// - standardUserDefaults: The shared UserDefaults instance. /// init( appSettingsStore: AppSettingsStore, errorReporter: ErrorReporter, - keychainRepository: KeychainRepository + keychainRepository: KeychainRepository, + standardUserDefaults: UserDefaults = .standard ) { self.appSettingsStore = appSettingsStore self.errorReporter = errorReporter self.keychainRepository = keychainRepository + self.standardUserDefaults = standardUserDefaults } // MARK: Private @@ -53,8 +60,13 @@ class DefaultMigrationService { /// - Migrates account tokens from UserDefaults to Keychain. /// - Resets stored date values from Xamarin/Maui, which uses an incompatible format. /// - Clears all keychain values on a fresh app install. + /// - Migrates AppCenter crash logging enabled to Crashlytics. /// private func performMigration1() async throws { + // Migrate AppCenter crash logging enabled to Crashlytics. + let isAppCenterCrashLoggingEnabled = standardUserDefaults.object(forKey: "MSAppCenterCrashesIsEnabled") as? Bool + errorReporter.isEnabled = isAppCenterCrashLoggingEnabled ?? true + guard var state = appSettingsStore.state else { // If state doesn't exist, this is a fresh install. Remove any persisted values in the // keychain from the previous install. diff --git a/BitwardenShared/Core/Platform/Services/MigrationServiceTests.swift b/BitwardenShared/Core/Platform/Services/MigrationServiceTests.swift index b4ac3609e..d3bcba271 100644 --- a/BitwardenShared/Core/Platform/Services/MigrationServiceTests.swift +++ b/BitwardenShared/Core/Platform/Services/MigrationServiceTests.swift @@ -8,6 +8,7 @@ class MigrationServiceTests: BitwardenTestCase { var appSettingsStore: MockAppSettingsStore! var errorReporter: MockErrorReporter! var keychainRepository: MockKeychainRepository! + var standardUserDefaults: UserDefaults! var subject: DefaultMigrationService! // MARK: Setup & Teardown @@ -18,11 +19,15 @@ class MigrationServiceTests: BitwardenTestCase { appSettingsStore = MockAppSettingsStore() errorReporter = MockErrorReporter() keychainRepository = MockKeychainRepository() + standardUserDefaults = UserDefaults(suiteName: "test") + + standardUserDefaults.removeObject(forKey: "MSAppCenterCrashesIsEnabled") subject = DefaultMigrationService( appSettingsStore: appSettingsStore, errorReporter: errorReporter, - keychainRepository: keychainRepository + keychainRepository: keychainRepository, + standardUserDefaults: standardUserDefaults ) } @@ -32,6 +37,7 @@ class MigrationServiceTests: BitwardenTestCase { appSettingsStore = nil errorReporter = nil keychainRepository = nil + standardUserDefaults = nil subject = nil } @@ -106,6 +112,26 @@ class MigrationServiceTests: BitwardenTestCase { } XCTAssertFalse(keychainRepository.deleteAllItemsCalled) + + XCTAssertTrue(errorReporter.isEnabled) + } + + /// `performMigrations()` for migration 1 handles migrating the crashes enabled key from + /// AppCenter when it's set to `false`. + func test_performMigrations_1_withAppCenterCrashesKey_false() async throws { + appSettingsStore.migrationVersion = 0 + standardUserDefaults.setValue(false, forKey: "MSAppCenterCrashesIsEnabled") + await subject.performMigrations() + XCTAssertFalse(errorReporter.isEnabled) + } + + /// `performMigrations()` for migration 1 handles migrating the crashes enabled key from + /// AppCenter when it's set to `true`. + func test_performMigrations_1_withAppCenterCrashesKey_true() async throws { + appSettingsStore.migrationVersion = 0 + standardUserDefaults.setValue(true, forKey: "MSAppCenterCrashesIsEnabled") + await subject.performMigrations() + XCTAssertTrue(errorReporter.isEnabled) } /// `performMigrations()` for migration 1 handles no existing accounts. @@ -118,5 +144,6 @@ class MigrationServiceTests: BitwardenTestCase { XCTAssertEqual(appSettingsStore.migrationVersion, 1) XCTAssertNil(appSettingsStore.state) XCTAssertTrue(keychainRepository.deleteAllItemsCalled) + XCTAssertTrue(errorReporter.isEnabled) } } diff --git a/BitwardenShared/Core/Platform/Services/NotificationService.swift b/BitwardenShared/Core/Platform/Services/NotificationService.swift index 19d457883..1122e3f90 100644 --- a/BitwardenShared/Core/Platform/Services/NotificationService.swift +++ b/BitwardenShared/Core/Platform/Services/NotificationService.swift @@ -253,8 +253,7 @@ class DefaultNotificationService: NotificationService { /// private func decodePayload(_ message: [AnyHashable: Any]) async throws -> PushNotificationData? { // Decode the content of the message. - guard let messageData = message["aps"] as? [AnyHashable: Any], - let messageContent = messageData["data"] as? [AnyHashable: Any] + guard let messageContent = message["data"] as? [AnyHashable: Any] else { return nil } let jsonData = try JSONSerialization.data(withJSONObject: messageContent) let notificationData = try JSONDecoder().decode(PushNotificationData.self, from: jsonData) diff --git a/BitwardenShared/Core/Platform/Services/NotificationServiceTests.swift b/BitwardenShared/Core/Platform/Services/NotificationServiceTests.swift index 77e20da2f..1a010cb00 100644 --- a/BitwardenShared/Core/Platform/Services/NotificationServiceTests.swift +++ b/BitwardenShared/Core/Platform/Services/NotificationServiceTests.swift @@ -96,11 +96,9 @@ class NotificationServiceTests: BitwardenTestCase { // swiftlint:disable:this ty stateService.setIsAuthenticated() appSettingsStore.appId = "10" let message: [AnyHashable: Any] = [ - "aps": [ - "data": [ - "type": "malformed", - "payload": "anything", - ], + "data": [ + "type": "malformed", + "payload": "anything", ], ] @@ -111,17 +109,37 @@ class NotificationServiceTests: BitwardenTestCase { // swiftlint:disable:this ty XCTAssertFalse(errorReporter.errors.isEmpty) } + /// `messageReceived(_:notificationDismissed:notificationTapped:)` handles nil objects in the payload. + func test_messageReceived_nil() async throws { + // Set up the mock data. + stateService.setIsAuthenticated() + appSettingsStore.appId = "10" + // swiftlint:disable line_length + let message: [AnyHashable: Any] = [ + "data": [ + "type": 1, + "payload": "{\"Id\":\"CIPHER ID\",\"UserId\":\"1\",\"OrganizationId\":null,\"CollectionIds\":null,\"RevisionDate\":\"2024-04-24T20:22:09.7593112Z\"}", + ], + ] + // swiftlint:enable line_length + + // Test. + await subject.messageReceived(message, notificationDismissed: nil, notificationTapped: nil) + + // Confirm the results. + XCTAssertEqual(syncService.fetchUpsertSyncCipherData?.id, "CIPHER ID") + XCTAssertEqual(syncService.fetchUpsertSyncCipherData?.userId, "1") + } + /// `messageReceived(_:notificationDismissed:notificationTapped:)` handles messages appropriately. func test_messageReceived_syncCipherCreate() async throws { // Set up the mock data. stateService.setIsAuthenticated() appSettingsStore.appId = "10" let message: [AnyHashable: Any] = [ - "aps": [ - "data": [ - "type": NotificationType.syncCipherCreate.rawValue, - "payload": "{\"Id\":\"CIPHER ID\",\"UserId\":\"1\"}", - ], + "data": [ + "type": NotificationType.syncCipherCreate.rawValue, + "payload": "{\"Id\":\"CIPHER ID\",\"UserId\":\"1\"}", ], ] @@ -139,11 +157,9 @@ class NotificationServiceTests: BitwardenTestCase { // swiftlint:disable:this ty stateService.setIsAuthenticated() appSettingsStore.appId = "10" let message: [AnyHashable: Any] = [ - "aps": [ - "data": [ - "type": NotificationType.syncCipherUpdate.rawValue, - "payload": "{\"Id\":\"CIPHER ID\",\"UserId\":\"1\"}", - ], + "data": [ + "type": NotificationType.syncCipherUpdate.rawValue, + "payload": "{\"Id\":\"CIPHER ID\",\"UserId\":\"1\"}", ], ] @@ -161,11 +177,9 @@ class NotificationServiceTests: BitwardenTestCase { // swiftlint:disable:this ty stateService.setIsAuthenticated() appSettingsStore.appId = "10" let message: [AnyHashable: Any] = [ - "aps": [ - "data": [ - "type": NotificationType.syncFolderCreate.rawValue, - "payload": "{\"Id\":\"FOLDER ID\",\"UserId\":\"1\"}", - ], + "data": [ + "type": NotificationType.syncFolderCreate.rawValue, + "payload": "{\"Id\":\"FOLDER ID\",\"UserId\":\"1\"}", ], ] @@ -183,11 +197,9 @@ class NotificationServiceTests: BitwardenTestCase { // swiftlint:disable:this ty stateService.setIsAuthenticated() appSettingsStore.appId = "10" let message: [AnyHashable: Any] = [ - "aps": [ - "data": [ - "type": NotificationType.syncFolderUpdate.rawValue, - "payload": "{\"Id\":\"FOLDER ID\",\"UserId\":\"1\"}", - ], + "data": [ + "type": NotificationType.syncFolderUpdate.rawValue, + "payload": "{\"Id\":\"FOLDER ID\",\"UserId\":\"1\"}", ], ] @@ -205,11 +217,9 @@ class NotificationServiceTests: BitwardenTestCase { // swiftlint:disable:this ty stateService.setIsAuthenticated() appSettingsStore.appId = "10" let message: [AnyHashable: Any] = [ - "aps": [ - "data": [ - "type": NotificationType.syncCipherDelete.rawValue, - "payload": "{\"Id\":\"CIPHER ID\",\"UserId\":\"1\"}", - ], + "data": [ + "type": NotificationType.syncCipherDelete.rawValue, + "payload": "{\"Id\":\"CIPHER ID\",\"UserId\":\"1\"}", ], ] @@ -227,11 +237,9 @@ class NotificationServiceTests: BitwardenTestCase { // swiftlint:disable:this ty stateService.setIsAuthenticated() appSettingsStore.appId = "10" let message: [AnyHashable: Any] = [ - "aps": [ - "data": [ - "type": NotificationType.syncLoginDelete.rawValue, - "payload": "{\"Id\":\"CIPHER ID\",\"UserId\":\"1\"}", - ], + "data": [ + "type": NotificationType.syncLoginDelete.rawValue, + "payload": "{\"Id\":\"CIPHER ID\",\"UserId\":\"1\"}", ], ] @@ -249,11 +257,9 @@ class NotificationServiceTests: BitwardenTestCase { // swiftlint:disable:this ty stateService.setIsAuthenticated() appSettingsStore.appId = "10" let message: [AnyHashable: Any] = [ - "aps": [ - "data": [ - "type": NotificationType.syncFolderDelete.rawValue, - "payload": "{\"Id\":\"FOLDER ID\",\"UserId\":\"1\"}", - ], + "data": [ + "type": NotificationType.syncFolderDelete.rawValue, + "payload": "{\"Id\":\"FOLDER ID\",\"UserId\":\"1\"}", ], ] @@ -271,11 +277,9 @@ class NotificationServiceTests: BitwardenTestCase { // swiftlint:disable:this ty stateService.setIsAuthenticated() appSettingsStore.appId = "10" let message: [AnyHashable: Any] = [ - "aps": [ - "data": [ - "type": NotificationType.syncSendCreate.rawValue, - "payload": "{\"Id\":\"SEND ID\",\"UserId\":\"1\"}", - ], + "data": [ + "type": NotificationType.syncSendCreate.rawValue, + "payload": "{\"Id\":\"SEND ID\",\"UserId\":\"1\"}", ], ] @@ -293,11 +297,9 @@ class NotificationServiceTests: BitwardenTestCase { // swiftlint:disable:this ty stateService.setIsAuthenticated() appSettingsStore.appId = "10" let message: [AnyHashable: Any] = [ - "aps": [ - "data": [ - "type": NotificationType.syncSendUpdate.rawValue, - "payload": "{\"Id\":\"SEND ID\",\"UserId\":\"1\"}", - ], + "data": [ + "type": NotificationType.syncSendUpdate.rawValue, + "payload": "{\"Id\":\"SEND ID\",\"UserId\":\"1\"}", ], ] @@ -315,11 +317,9 @@ class NotificationServiceTests: BitwardenTestCase { // swiftlint:disable:this ty stateService.setIsAuthenticated() appSettingsStore.appId = "10" let message: [AnyHashable: Any] = [ - "aps": [ - "data": [ - "type": NotificationType.syncSendDelete.rawValue, - "payload": "{\"Id\":\"SEND ID\",\"UserId\":\"1\"}", - ], + "data": [ + "type": NotificationType.syncSendDelete.rawValue, + "payload": "{\"Id\":\"SEND ID\",\"UserId\":\"1\"}", ], ] @@ -337,11 +337,9 @@ class NotificationServiceTests: BitwardenTestCase { // swiftlint:disable:this ty stateService.setIsAuthenticated() appSettingsStore.appId = "10" let message: [AnyHashable: Any] = [ - "aps": [ - "data": [ - "type": NotificationType.syncOrgKeys.rawValue, - "payload": "anything", - ], + "data": [ + "type": NotificationType.syncOrgKeys.rawValue, + "payload": "anything", ], ] @@ -358,11 +356,9 @@ class NotificationServiceTests: BitwardenTestCase { // swiftlint:disable:this ty stateService.setIsAuthenticated() appSettingsStore.appId = "10" let message: [AnyHashable: Any] = [ - "aps": [ - "data": [ - "type": NotificationType.syncVault.rawValue, - "payload": "anything", - ], + "data": [ + "type": NotificationType.syncVault.rawValue, + "payload": "anything", ], ] @@ -384,11 +380,9 @@ class NotificationServiceTests: BitwardenTestCase { // swiftlint:disable:this ty let loginRequestNotification = LoginRequestNotification(id: "requestId", userId: "differentUser") let notificationData = try JSONEncoder().encode(loginRequestNotification) let message: [AnyHashable: Any] = [ - "aps": [ - "data": [ - "type": NotificationType.authRequest.rawValue, - "payload": String(data: notificationData, encoding: .utf8) ?? "", - ], + "data": [ + "type": NotificationType.authRequest.rawValue, + "payload": String(data: notificationData, encoding: .utf8) ?? "", ], ] @@ -413,11 +407,9 @@ class NotificationServiceTests: BitwardenTestCase { // swiftlint:disable:this ty let loginRequestNotification = LoginRequestNotification(id: "requestId", userId: "1") let notificationData = try JSONEncoder().encode(loginRequestNotification) let message: [AnyHashable: Any] = [ - "aps": [ - "data": [ - "type": NotificationType.authRequest.rawValue, - "payload": String(data: notificationData, encoding: .utf8) ?? "", - ], + "data": [ + "type": NotificationType.authRequest.rawValue, + "payload": String(data: notificationData, encoding: .utf8) ?? "", ], ] @@ -439,11 +431,9 @@ class NotificationServiceTests: BitwardenTestCase { // swiftlint:disable:this ty stateService.accounts = [activeAccount, nonActiveAccount] let message: [AnyHashable: Any] = [ - "aps": [ - "data": [ - "type": NotificationType.logOut.rawValue, - "payload": "{\"UserId\":\"\(nonActiveAccount.profile.userId)\"}", - ], + "data": [ + "type": NotificationType.logOut.rawValue, + "payload": "{\"UserId\":\"\(nonActiveAccount.profile.userId)\"}", ], ] @@ -463,11 +453,9 @@ class NotificationServiceTests: BitwardenTestCase { // swiftlint:disable:this ty stateService.accounts = [activeAccount, nonActiveAccount] let message: [AnyHashable: Any] = [ - "aps": [ - "data": [ - "type": NotificationType.logOut.rawValue, - "payload": "{\"UserId\":\"\(activeAccount.profile.userId)\"}", - ], + "data": [ + "type": NotificationType.logOut.rawValue, + "payload": "{\"UserId\":\"\(activeAccount.profile.userId)\"}", ], ] diff --git a/BitwardenShared/Core/Platform/Services/ServiceContainer.swift b/BitwardenShared/Core/Platform/Services/ServiceContainer.swift index 104c82a82..675dbf059 100644 --- a/BitwardenShared/Core/Platform/Services/ServiceContainer.swift +++ b/BitwardenShared/Core/Platform/Services/ServiceContainer.swift @@ -50,6 +50,9 @@ public class ServiceContainer: Services { // swiftlint:disable:this type_body_le /// The service used by the application to handle encryption and decryption tasks. let clientService: ClientService + /// The service to get server-specified configuration + let configService: ConfigService + /// The service used by the application to manage the environment settings. let environmentService: EnvironmentService @@ -142,6 +145,7 @@ public class ServiceContainer: Services { // swiftlint:disable:this type_body_le /// - captchaService: The service used by the application to create captcha related artifacts. /// - cameraService: The service used by the application to manage camera use. /// - clientService: The service used by the application to handle encryption and decryption tasks. + /// - configService: The service to get server-specified configuration. /// - environmentService: The service used by the application to manage the environment settings. /// - errorReporter: The service used by the application to report non-fatal errors. /// - generatorRepository: The repository used by the application to manage generator data for the UI layer. @@ -179,6 +183,7 @@ public class ServiceContainer: Services { // swiftlint:disable:this type_body_le captchaService: CaptchaService, cameraService: CameraService, clientService: ClientService, + configService: ConfigService, environmentService: EnvironmentService, errorReporter: ErrorReporter, exportVaultService: ExportVaultService, @@ -216,6 +221,7 @@ public class ServiceContainer: Services { // swiftlint:disable:this type_body_le self.captchaService = captchaService self.cameraService = cameraService self.clientService = clientService + self.configService = configService self.environmentService = environmentService self.errorReporter = errorReporter self.exportVaultService = exportVaultService @@ -294,6 +300,13 @@ public class ServiceContainer: Services { // swiftlint:disable:this type_body_le let captchaService = DefaultCaptchaService(environmentService: environmentService, stateService: stateService) let notificationCenterService = DefaultNotificationCenterService() + let configService = DefaultConfigService( + configApiService: apiService, + errorReporter: errorReporter, + stateService: stateService, + timeProvider: timeProvider + ) + let folderService = DefaultFolderService( folderAPIService: apiService, folderDataStore: dataStore, @@ -400,6 +413,7 @@ public class ServiceContainer: Services { // swiftlint:disable:this type_body_le authService: authService, biometricsRepository: biometricsRepository, clientService: clientService, + configService: configService, environmentService: environmentService, keychainService: keychainRepository, organizationAPIService: apiService, @@ -478,6 +492,7 @@ public class ServiceContainer: Services { // swiftlint:disable:this type_body_le captchaService: captchaService, cameraService: DefaultCameraService(), clientService: clientService, + configService: configService, environmentService: environmentService, errorReporter: errorReporter, exportVaultService: exportVaultService, @@ -516,6 +531,14 @@ extension ServiceContainer { apiService } + var cipherAPIService: CipherAPIService { + apiService + } + + var configAPIService: ConfigAPIService { + apiService + } + var deviceAPIService: DeviceAPIService { apiService } diff --git a/BitwardenShared/Core/Platform/Services/Services.swift b/BitwardenShared/Core/Platform/Services/Services.swift index 58462b437..63c62413b 100644 --- a/BitwardenShared/Core/Platform/Services/Services.swift +++ b/BitwardenShared/Core/Platform/Services/Services.swift @@ -11,7 +11,9 @@ typealias Services = HasAPIService & HasBiometricsRepository & HasCameraService & HasCaptchaService + & HasCipherAPIService & HasClientService + & HasConfigService & HasDeviceAPIService & HasEnvironmentService & HasErrorReporter @@ -113,6 +115,20 @@ protocol HasClientService { var clientService: ClientService { get } } +/// Protocol for an object that provides a `CipherAPIService`. +/// +protocol HasCipherAPIService { + /// The service used by the application to make cipher-related API requests. + var cipherAPIService: CipherAPIService { get } +} + +/// Protocol for an object that provides a `ConfigService`. +/// +protocol HasConfigService { + /// The service to get server-specified configuration. + var configService: ConfigService { get } +} + /// Protocol for an object that provides a `DeviceAPIService`. /// protocol HasDeviceAPIService { diff --git a/BitwardenShared/Core/Platform/Services/StateService.swift b/BitwardenShared/Core/Platform/Services/StateService.swift index 4ff152485..1d66a8027 100644 --- a/BitwardenShared/Core/Platform/Services/StateService.swift +++ b/BitwardenShared/Core/Platform/Services/StateService.swift @@ -131,6 +131,13 @@ protocol StateService: AnyObject { /// func getDisableAutoTotpCopy(userId: String?) async throws -> Bool + /// The user's pin protected by their user key. + /// + /// - Parameter userId: The user ID associated with the encrypted pin. + /// - Returns: The user's pin protected by their user key. + /// + func getEncryptedPin(userId: String?) async throws -> String? + /// Gets the environment URLs for a user ID. /// /// - Parameter userId: The user ID associated with the environment URLs. @@ -193,6 +200,13 @@ protocol StateService: AnyObject { /// func getPreAuthEnvironmentUrls() async -> EnvironmentUrlData? + /// Gets the server config for a user ID, as set by the server. + /// + /// - Parameter userId: The user ID associated with the server config. Defaults to the active account if `nil`. + /// - Returns: The user's server config. + /// + func getServerConfig(userId: String?) async throws -> ServerConfig? + /// Get whether the device should be trusted. /// /// - Returns: Whether to trust the device. @@ -255,13 +269,6 @@ protocol StateService: AnyObject { /// func logoutAccount(userId: String?) async throws - /// The user's pin key encrypted user key. - /// - /// - Parameter userId: The user ID associated with the pin key encrypted user key. - /// - Returns: The user's pin key encrypted user key. - /// - func pinKeyEncryptedUserKey(userId: String?) async throws -> String? - /// The pin protected user key. /// /// - Parameter userId: The user ID associated with the pin protected user key. @@ -406,12 +413,12 @@ protocol StateService: AnyObject { /// Set's the pin keys. /// /// - Parameters: - /// - pinKeyEncryptedUserKey: The user's encrypted pin. + /// - encryptedPin: The user's encrypted pin. /// - pinProtectedUserKey: The user's pin protected user key. /// - requirePasswordAfterRestart: Whether to require password after app restart. /// func setPinKeys( - pinKeyEncryptedUserKey: String, + encryptedPin: String, pinProtectedUserKey: String, requirePasswordAfterRestart: Bool ) async throws @@ -428,6 +435,14 @@ protocol StateService: AnyObject { /// func setPreAuthEnvironmentUrls(_ urls: EnvironmentUrlData) async + /// Sets the server configuration as provided by a server for a user ID. + /// + /// - Parameters: + /// - configModel: The config values to set as provided by the server. + /// - userId: The user ID associated with the server config. + /// + func setServerConfig(_ config: ServerConfig?, userId: String?) async throws + /// Set whether to trust the device. /// /// - Parameter shouldTrustDevice: Whether to trust the device. @@ -604,6 +619,14 @@ extension StateService { try await getDisableAutoTotpCopy(userId: nil) } + /// The user's pin protected by their user key. + /// + /// - Returns: The user's pin protected by their user key. + /// + func getEncryptedPin() async throws -> String? { + try await getEncryptedPin(userId: nil) + } + /// Gets the environment URLs for the active account. /// /// - Returns: The environment URLs for the active account. @@ -654,6 +677,14 @@ extension StateService { try await getPasswordGenerationOptions(userId: nil) } + /// Gets the server config for the active account. + /// + /// - Returns: The server config sent by the server for the active account. + /// + func getServerConfig() async throws -> ServerConfig? { + try await getServerConfig(userId: nil) + } + /// Gets the session timeout action. /// /// - Returns: The action to perform when a session timeout occurs. @@ -712,14 +743,6 @@ extension StateService { try await logoutAccount(userId: nil) } - /// The user's pin protected user key. - /// - /// - Returns: The pin protected user key. - /// - func pinKeyEncryptedUserKey() async throws -> String? { - try await pinKeyEncryptedUserKey(userId: nil) - } - /// The pin protected user key. /// /// - Returns: The pin protected user key. @@ -824,6 +847,14 @@ extension StateService { try await setPasswordGenerationOptions(options, userId: nil) } + /// Sets the server config for the active account. + /// + /// - Parameter config: The server config. + /// + func setServerConfig(_ config: ServerConfig?) async throws { + try await setServerConfig(config, userId: nil) + } + /// Sets the session timeout action. /// /// - Parameter action: The action to take when the user's session times out. @@ -949,8 +980,8 @@ actor DefaultStateService: StateService { // swiftlint:disable:this type_body_le func clearPins() async throws { let userId = try getActiveAccountUserId() accountVolatileData.removeValue(forKey: userId) + appSettingsStore.setEncryptedPin(nil, userId: userId) appSettingsStore.setPinProtectedUserKey(key: nil, userId: userId) - appSettingsStore.setPinKeyEncryptedUserKey(key: nil, userId: userId) } func deleteAccount() async throws { @@ -1027,6 +1058,11 @@ actor DefaultStateService: StateService { // swiftlint:disable:this type_body_le return appSettingsStore.disableAutoTotpCopy(userId: userId) } + func getEncryptedPin(userId: String?) async throws -> String? { + let userId = try userId ?? getActiveAccountUserId() + return appSettingsStore.encryptedPin(userId: userId) + } + func getEnvironmentUrls(userId: String?) async throws -> EnvironmentUrlData? { let userId = try userId ?? getActiveAccountUserId() return appSettingsStore.state?.accounts[userId]?.settings.environmentUrls @@ -1069,6 +1105,11 @@ actor DefaultStateService: StateService { // swiftlint:disable:this type_body_le appSettingsStore.preAuthEnvironmentUrls } + func getServerConfig(userId: String?) async throws -> ServerConfig? { + let userId = try userId ?? getActiveAccountUserId() + return appSettingsStore.serverConfig(userId: userId) + } + func getShouldTrustDevice(userId: String) async -> Bool? { appSettingsStore.shouldTrustDevice(userId: userId) } @@ -1136,11 +1177,6 @@ actor DefaultStateService: StateService { // swiftlint:disable:this type_body_le try await dataStore.deleteDataForUser(userId: knownUserId) } - func pinKeyEncryptedUserKey(userId: String?) async throws -> String? { - let userId = try userId ?? getActiveAccountUserId() - return appSettingsStore.pinKeyEncryptedUserKey(userId: userId) - } - func pinProtectedUserKey(userId: String?) async throws -> String? { let userId = try userId ?? getActiveAccountUserId() return accountVolatileData[userId]?.pinProtectedUserKey ?? appSettingsStore.pinProtectedUserKey(userId: userId) @@ -1241,7 +1277,7 @@ actor DefaultStateService: StateService { // swiftlint:disable:this type_body_le } func setPinKeys( - pinKeyEncryptedUserKey: String, + encryptedPin: String, pinProtectedUserKey: String, requirePasswordAfterRestart: Bool ) async throws { @@ -1250,7 +1286,7 @@ actor DefaultStateService: StateService { // swiftlint:disable:this type_body_le } else { try appSettingsStore.setPinProtectedUserKey(key: pinProtectedUserKey, userId: getActiveAccountUserId()) } - try appSettingsStore.setPinKeyEncryptedUserKey(key: pinKeyEncryptedUserKey, userId: getActiveAccountUserId()) + try appSettingsStore.setEncryptedPin(encryptedPin, userId: getActiveAccountUserId()) } func setPinProtectedUserKeyToMemory(_ pinProtectedUserKey: String) async throws { @@ -1264,6 +1300,11 @@ actor DefaultStateService: StateService { // swiftlint:disable:this type_body_le appSettingsStore.preAuthEnvironmentUrls = urls } + func setServerConfig(_ config: ServerConfig?, userId: String?) async throws { + let userId = try userId ?? getActiveAccountUserId() + appSettingsStore.setServerConfig(config, userId: userId) + } + func setShouldTrustDevice(_ shouldTrustDevice: Bool?, userId: String) { appSettingsStore.setShouldTrustDevice(shouldTrustDevice: shouldTrustDevice, userId: userId) } diff --git a/BitwardenShared/Core/Platform/Services/StateServiceTests.swift b/BitwardenShared/Core/Platform/Services/StateServiceTests.swift index c48206c77..b7e139ba0 100644 --- a/BitwardenShared/Core/Platform/Services/StateServiceTests.swift +++ b/BitwardenShared/Core/Platform/Services/StateServiceTests.swift @@ -99,10 +99,10 @@ class StateServiceTests: BitwardenTestCase { // swiftlint:disable:this type_body try await subject.clearPins() let pinProtectedUserKey = try await subject.pinProtectedUserKey() - let pinKeyEncryptedUserKey = try await subject.pinKeyEncryptedUserKey() + let encryptedPin = try await subject.getEncryptedPin() XCTAssertNil(pinProtectedUserKey) - XCTAssertNil(pinKeyEncryptedUserKey) + XCTAssertNil(encryptedPin) } /// `deleteAccount()` deletes the active user's account, removing it from the state. @@ -380,6 +380,24 @@ class StateServiceTests: BitwardenTestCase { // swiftlint:disable:this type_body XCTAssertTrue(value) } + /// `getEncryptedPin()` returns the user's pin encrypted by their user key. + func test_getEncryptedPin() async throws { + let account = Account.fixture() + await subject.addAccount(account) + + try await subject.setPinKeys( + encryptedPin: "123", + pinProtectedUserKey: "321", + requirePasswordAfterRestart: true + ) + + let encryptedPin = try await subject.getEncryptedPin() + let pinProtectedUserKey = await subject.accountVolatileData["1"]?.pinProtectedUserKey + + XCTAssertEqual(encryptedPin, "123") + XCTAssertEqual(pinProtectedUserKey, "321") + } + /// `getEnvironmentUrls()` returns the environment URLs for the active account. func test_getEnvironmentUrls() async throws { let urls = EnvironmentUrlData(base: .example) @@ -513,6 +531,24 @@ class StateServiceTests: BitwardenTestCase { // swiftlint:disable:this type_body XCTAssertNil(urls) } + /// `getServerConfig(:)` returns the config values + func test_getServerConfig() async throws { + await subject.addAccount(.fixture()) + let model = ServerConfig( + date: Date(timeIntervalSince1970: 100), + responseModel: ConfigResponseModel( + environment: nil, + featureStates: [:], + gitHash: "1234", + server: nil, + version: "1.2.3" + ) + ) + appSettingsStore.serverConfig["1"] = model + let value = try await subject.getServerConfig() + XCTAssertEqual(value, model) + } + /// `getShowWebIcons` gets the show web icons value. func test_getShowWebIcons() async { appSettingsStore.disableWebIcons = true @@ -885,24 +921,6 @@ class StateServiceTests: BitwardenTestCase { // swiftlint:disable:this type_body XCTAssertEqual(appSettingsStore.encryptedUserKeys, ["2": "2:USER_KEY"]) } - /// `pinKeyEncryptedUserKey()` returns the pin key encrypted user key. - func test_pinKeyEncryptedUserKey() async throws { - let account = Account.fixture() - await subject.addAccount(account) - - try await subject.setPinKeys( - pinKeyEncryptedUserKey: "123", - pinProtectedUserKey: "321", - requirePasswordAfterRestart: true - ) - - let pinKeyEncryptedUserKey = try await subject.pinKeyEncryptedUserKey() - let pinProtectedUserKey = await subject.accountVolatileData["1"]?.pinProtectedUserKey - - XCTAssertEqual(pinKeyEncryptedUserKey, "123") - XCTAssertEqual(pinProtectedUserKey, "321") - } - /// `pinProtectedUserKey(userId:)` returns the pin protected user key. func test_pinProtectedUserKey() async throws { await subject.addAccount(.fixture(profile: .fixture(userId: "1"))) @@ -1184,12 +1202,12 @@ class StateServiceTests: BitwardenTestCase { // swiftlint:disable:this type_body await subject.addAccount(.fixture(profile: .fixture(userId: "1"))) try await subject.setPinKeys( - pinKeyEncryptedUserKey: "123", - pinProtectedUserKey: "123", + encryptedPin: "encryptedPin", + pinProtectedUserKey: "pinProtectedUserKey", requirePasswordAfterRestart: false ) - XCTAssertEqual(appSettingsStore.pinProtectedUserKey["1"], "123") - XCTAssertEqual(appSettingsStore.pinKeyEncryptedUserKey["1"], "123") + XCTAssertEqual(appSettingsStore.pinProtectedUserKey["1"], "pinProtectedUserKey") + XCTAssertEqual(appSettingsStore.encryptedPinByUserId["1"], "encryptedPin") } /// `setPreAuthEnvironmentUrls` saves the pre-auth URLs. @@ -1199,6 +1217,23 @@ class StateServiceTests: BitwardenTestCase { // swiftlint:disable:this type_body XCTAssertEqual(appSettingsStore.preAuthEnvironmentUrls, urls) } + /// `setServerConfig(_:)` sets the config values. + func test_setServerConfig() async throws { + await subject.addAccount(.fixture()) + let model = ServerConfig( + date: Date(timeIntervalSince1970: 100), + responseModel: ConfigResponseModel( + environment: nil, + featureStates: [:], + gitHash: "1234", + server: nil, + version: "1.2.3.4" + ) + ) + try await subject.setServerConfig(model) + XCTAssertEqual(appSettingsStore.serverConfig["1"], model) + } + /// `setShouldTrustDevice` saves the should trust device value. func test_setShouldTrustDevice() async { await subject.setShouldTrustDevice(true, userId: "1") diff --git a/BitwardenShared/Core/Platform/Services/Stores/AppSettingsStore.swift b/BitwardenShared/Core/Platform/Services/Stores/AppSettingsStore.swift index af174b217..4df435cc2 100644 --- a/BitwardenShared/Core/Platform/Services/Stores/AppSettingsStore.swift +++ b/BitwardenShared/Core/Platform/Services/Stores/AppSettingsStore.swift @@ -91,6 +91,13 @@ protocol AppSettingsStore: AnyObject { /// func disableAutoTotpCopy(userId: String) -> Bool + /// The user's pin protected by their user key. + /// + /// - Parameter userId: The user ID associated with the encrypted pin. + /// - Returns: The user's pin protected by their user key. + /// + func encryptedPin(userId: String) -> String? + /// Gets the encrypted private key for the user ID. /// /// - Parameter userId: The user ID associated with the encrypted private key. @@ -148,13 +155,6 @@ protocol AppSettingsStore: AnyObject { /// func passwordGenerationOptions(userId: String) -> PasswordGenerationOptions? - /// The user's pin protected user key. - /// - /// - Parameter userId: The user ID associated with the pin key encrypted user key. - /// - Returns: The pin protected user key. - /// - func pinKeyEncryptedUserKey(userId: String) -> String? - /// The pin protected user key. /// /// - Parameter userId: The user ID associated with the pin protected user key. @@ -162,6 +162,12 @@ protocol AppSettingsStore: AnyObject { /// func pinProtectedUserKey(userId: String) -> String? + /// The server configuration. + /// + /// - Parameter userId: The user ID associated with the server config. + /// - Returns: The server config for that user ID. + func serverConfig(userId: String) -> ServerConfig? + /// Whether the vault should sync on refreshing. /// /// - Parameters: @@ -222,6 +228,14 @@ protocol AppSettingsStore: AnyObject { /// func setDisableAutoTotpCopy(_ disableAutoTotpCopy: Bool?, userId: String) + /// Sets the user's pin protected by their user key. + /// + /// - Parameters: + /// - encryptedPin: The user's pin protected by their user key. + /// - userId: The user ID. + /// + func setEncryptedPin(_ encryptedPin: String?, userId: String) + /// Sets the encrypted private key for a user ID. /// /// - Parameters: @@ -278,21 +292,21 @@ protocol AppSettingsStore: AnyObject { /// func setPasswordGenerationOptions(_ options: PasswordGenerationOptions?, userId: String) - /// Sets the pin key encrypted user key. + /// Sets the pin protected user key. /// /// - Parameters: - /// - key: A pin key encrypted user key derived from the user's pin. + /// - key: A pin protected user key derived from the user's pin. /// - userId: The user ID. /// - func setPinKeyEncryptedUserKey(key: String?, userId: String) + func setPinProtectedUserKey(key: String?, userId: String) - /// Sets the pin protected user key. + /// Sets the server config. /// /// - Parameters: - /// - key: A pin protected user key derived from the user's pin. + /// - config: The server config for the user /// - userId: The user ID. /// - func setPinProtectedUserKey(key: String?, userId: String) + func setServerConfig(_ config: ServerConfig?, userId: String) /// Set whether to trust the device. /// @@ -517,6 +531,7 @@ extension DefaultAppSettingsStore: AppSettingsStore { case defaultUriMatch(userId: String) case disableAutoTotpCopy(userId: String) case disableWebIcons + case encryptedPin(userId: String) case encryptedPrivateKey(userId: String) case encryptedUserKey(userId: String) case lastActiveTime(userId: String) @@ -527,11 +542,11 @@ extension DefaultAppSettingsStore: AppSettingsStore { case migrationVersion case notificationsLastRegistrationDate(userId: String) case passwordGenerationOptions(userId: String) - case pinKeyEncryptedUserKey(userId: String) case pinProtectedUserKey(userId: String) case preAuthEnvironmentUrls case rememberedEmail case rememberedOrgIdentifier + case serverConfig(userId: String) case shouldTrustDevice(userId: String) case state case twoFactorToken(email: String) @@ -570,6 +585,8 @@ extension DefaultAppSettingsStore: AppSettingsStore { key = "disableFavicon" case let .encryptedUserKey(userId): key = "masterKeyEncryptedUserKey_\(userId)" + case let .encryptedPin(userId): + key = "protectedPin_\(userId)" case let .encryptedPrivateKey(userId): key = "encPrivateKey_\(userId)" case let .lastActiveTime(userId): @@ -588,16 +605,16 @@ extension DefaultAppSettingsStore: AppSettingsStore { key = "pushLastRegistrationDate_\(userId)" case let .passwordGenerationOptions(userId): key = "passwordGenerationOptions_\(userId)" - case let .pinKeyEncryptedUserKey(userId): - key = "pinKeyEncryptedUserKey_\(userId)" case let .pinProtectedUserKey(userId): - key = "pinProtectedUserKey_\(userId)" + key = "pinKeyEncryptedUserKey_\(userId)" case .preAuthEnvironmentUrls: key = "preAuthEnvironmentUrls" case .rememberedEmail: key = "rememberedEmail" case .rememberedOrgIdentifier: key = "rememberedOrgIdentifier" + case let .serverConfig(userId): + key = "serverConfig_\(userId)" case let .shouldTrustDevice(userId): key = "shouldTrustDevice_\(userId)" case .state: @@ -713,6 +730,10 @@ extension DefaultAppSettingsStore: AppSettingsStore { fetch(for: .disableAutoTotpCopy(userId: userId)) } + func encryptedPin(userId: String) -> String? { + fetch(for: .encryptedPin(userId: userId)) + } + func encryptedPrivateKey(userId: String) -> String? { fetch(for: .encryptedPrivateKey(userId: userId)) } @@ -745,14 +766,14 @@ extension DefaultAppSettingsStore: AppSettingsStore { fetch(for: .passwordGenerationOptions(userId: userId)) } - func pinKeyEncryptedUserKey(userId: String) -> String? { - fetch(for: .pinKeyEncryptedUserKey(userId: userId)) - } - func pinProtectedUserKey(userId: String) -> String? { fetch(for: .pinProtectedUserKey(userId: userId)) } + func serverConfig(userId: String) -> ServerConfig? { + fetch(for: .serverConfig(userId: userId)) + } + func setAllowSyncOnRefresh(_ allowSyncOnRefresh: Bool?, userId: String) { store(allowSyncOnRefresh, for: .allowSyncOnRefresh(userId: userId)) } @@ -787,6 +808,10 @@ extension DefaultAppSettingsStore: AppSettingsStore { store(disableAutoTotpCopy, for: .disableAutoTotpCopy(userId: userId)) } + func setEncryptedPin(_ encryptedPin: String?, userId: String) { + store(encryptedPin, for: .encryptedPin(userId: userId)) + } + func setEncryptedPrivateKey(key: String?, userId: String) { store(key, for: .encryptedPrivateKey(userId: userId)) } @@ -815,14 +840,14 @@ extension DefaultAppSettingsStore: AppSettingsStore { store(options, for: .passwordGenerationOptions(userId: userId)) } - func setPinKeyEncryptedUserKey(key: String?, userId: String) { - store(key, for: .pinKeyEncryptedUserKey(userId: userId)) - } - func setPinProtectedUserKey(key: String?, userId: String) { store(key, for: .pinProtectedUserKey(userId: userId)) } + func setServerConfig(_ config: ServerConfig?, userId: String) { + store(config, for: .serverConfig(userId: userId)) + } + func setShouldTrustDevice(shouldTrustDevice: Bool?, userId: String) { store(shouldTrustDevice, for: .shouldTrustDevice(userId: userId)) } diff --git a/BitwardenShared/Core/Platform/Services/Stores/AppSettingsStoreTests.swift b/BitwardenShared/Core/Platform/Services/Stores/AppSettingsStoreTests.swift index 28ece3d71..0992647a5 100644 --- a/BitwardenShared/Core/Platform/Services/Stores/AppSettingsStoreTests.swift +++ b/BitwardenShared/Core/Platform/Services/Stores/AppSettingsStoreTests.swift @@ -222,6 +222,14 @@ class AppSettingsStoreTests: BitwardenTestCase { // swiftlint:disable:this type_ XCTAssertFalse(userDefaults.bool(forKey: "bwPreferencesStorage:disableFavicon")) } + /// `.encryptedPin(_:userId:)` can be used to get the user's encrypted pin. + func test_encryptedPin() { + let userId = Account.fixture().profile.userId + subject.setEncryptedPin("123", userId: userId) + let pin = subject.encryptedPin(userId: userId) + XCTAssertEqual(userDefaults.string(forKey: "bwPreferencesStorage:protectedPin_1"), pin) + } + /// `encryptedPrivateKey(userId:)` returns `nil` if there isn't a previously stored value. func test_encryptedPrivateKey_isInitiallyNil() { XCTAssertNil(subject.encryptedPrivateKey(userId: "-1")) @@ -500,20 +508,12 @@ class AppSettingsStoreTests: BitwardenTestCase { // swiftlint:disable:this type_ XCTAssertEqual(subject.passwordGenerationOptions(userId: "2"), options2) } - /// `.pinKeyEncryptedUserKey(userId:)` can be used to get the pin key encrypted user key. - func test_pinKeyEncryptedUserKey() { - let userId = Account.fixture().profile.userId - subject.setPinKeyEncryptedUserKey(key: "123", userId: userId) - let pin = subject.pinKeyEncryptedUserKey(userId: userId) - XCTAssertEqual(userDefaults.string(forKey: "bwPreferencesStorage:pinKeyEncryptedUserKey_1"), pin) - } - /// `.pinProtectedUserKey(userId:)` can be used to get the pin protected user key for a user. func test_pinProtectedUserKey() { let userId = Account.fixture().profile.userId subject.setPinProtectedUserKey(key: "123", userId: userId) let pin = subject.pinProtectedUserKey(userId: userId) - XCTAssertEqual(userDefaults.string(forKey: "bwPreferencesStorage:pinProtectedUserKey_1"), pin) + XCTAssertEqual(userDefaults.string(forKey: "bwPreferencesStorage:pinKeyEncryptedUserKey_1"), pin) } /// `preAuthEnvironmentUrls` returns `nil` if there isn't a previously stored value. @@ -552,6 +552,49 @@ class AppSettingsStoreTests: BitwardenTestCase { // swiftlint:disable:this type_ ) } + /// `serverConfig(:)` is initially `nil` + func test_serverConfig_isInitiallyNil() { + XCTAssertNil(subject.serverConfig(userId: "1")) + } + + /// `serverConfig(:)` can be used to get and set the persisted value in user defaults. + func test_serverConfig_withValue() { + let config = ServerConfig( + date: Date(timeIntervalSince1970: 100), + responseModel: ConfigResponseModel( + environment: EnvironmentServerConfigResponseModel( + api: "https://vault.bitwarden.com", + cloudRegion: "US", + identity: "https://vault.bitwarden.com", + notifications: "https://vault.bitwarden.com", + sso: "https://vault.bitwarden.com", + vault: "https://vault.bitwarden.com" + ), + featureStates: ["feature": .bool(true)], + gitHash: "hash", + server: ThirdPartyConfigResponseModel( + name: "Name", + url: "Url" + ), + version: "version" + ) + ) + subject.setServerConfig(config, userId: "1") + + XCTAssertEqual(subject.serverConfig(userId: "1"), config) + try XCTAssertEqual( + JSONDecoder().decode( + ServerConfig.self, + from: XCTUnwrap( + userDefaults + .string(forKey: "bwPreferencesStorage:serverConfig_1")? + .data(using: .utf8) + ) + ), + config + ) + } + /// `twoFactorToken(email:)` returns `nil` if there isn't a previously stored value. func test_twoFactorToken_isInitiallyNil() { XCTAssertNil(subject.twoFactorToken(email: "anything@email.com")) diff --git a/BitwardenShared/Core/Platform/Services/Stores/TestHelpers/MockAppSettingsStore.swift b/BitwardenShared/Core/Platform/Services/Stores/TestHelpers/MockAppSettingsStore.swift index d132c00b5..9b944f89d 100644 --- a/BitwardenShared/Core/Platform/Services/Stores/TestHelpers/MockAppSettingsStore.swift +++ b/BitwardenShared/Core/Platform/Services/Stores/TestHelpers/MockAppSettingsStore.swift @@ -23,6 +23,7 @@ class MockAppSettingsStore: AppSettingsStore { var connectToWatchByUserId = [String: Bool]() var defaultUriMatchTypeByUserId = [String: UriMatchType]() var disableAutoTotpCopyByUserId = [String: Bool]() + var encryptedPinByUserId = [String: String]() var encryptedPrivateKeys = [String: String]() var encryptedUserKeys = [String: String]() var lastActiveTime = [String: Date]() @@ -30,8 +31,8 @@ class MockAppSettingsStore: AppSettingsStore { var masterPasswordHashes = [String: String]() var notificationsLastRegistrationDates = [String: Date]() var passwordGenerationOptions = [String: PasswordGenerationOptions]() - var pinKeyEncryptedUserKey = [String: String]() var pinProtectedUserKey = [String: String]() + var serverConfig = [String: ServerConfig]() var shouldTrustDevice = [String: Bool?]() var timeoutAction = [String: Int]() var twoFactorTokens = [String: String]() @@ -67,6 +68,10 @@ class MockAppSettingsStore: AppSettingsStore { disableAutoTotpCopyByUserId[userId] ?? false } + func encryptedPin(userId: String) -> String? { + encryptedPinByUserId[userId] + } + func encryptedPrivateKey(userId: String) -> String? { encryptedPrivateKeys[userId] } @@ -95,10 +100,6 @@ class MockAppSettingsStore: AppSettingsStore { passwordGenerationOptions[userId] } - func pinKeyEncryptedUserKey(userId: String) -> String? { - pinKeyEncryptedUserKey[userId] - } - func pinProtectedUserKey(userId: String) -> String? { pinProtectedUserKey[userId] } @@ -107,6 +108,10 @@ class MockAppSettingsStore: AppSettingsStore { twoFactorTokens[email] } + func serverConfig(userId: String) -> ServerConfig? { + serverConfig[userId] + } + func setAllowSyncOnRefresh(_ allowSyncOnRefresh: Bool?, userId: String) { allowSyncOnRefreshes[userId] = allowSyncOnRefresh } @@ -127,6 +132,10 @@ class MockAppSettingsStore: AppSettingsStore { disableAutoTotpCopyByUserId[userId] = disableAutoTotpCopy } + func setEncryptedPin(_ encryptedPin: String?, userId: String) { + encryptedPinByUserId[userId] = encryptedPin + } + func setEncryptedPrivateKey(key: String?, userId: String) { guard let key else { encryptedPrivateKeys.removeValue(forKey: userId) @@ -167,14 +176,14 @@ class MockAppSettingsStore: AppSettingsStore { passwordGenerationOptions[userId] = options } - func setPinKeyEncryptedUserKey(key: String?, userId: String) { - pinKeyEncryptedUserKey[userId] = key - } - func setPinProtectedUserKey(key: String?, userId: String) { pinProtectedUserKey[userId] = key } + func setServerConfig(_ config: ServerConfig?, userId: String) { + serverConfig[userId] = config + } + func setShouldTrustDevice(shouldTrustDevice: Bool?, userId: String) { self.shouldTrustDevice[userId] = shouldTrustDevice } diff --git a/BitwardenShared/Core/Platform/Services/TestHelpers/MockConfigService.swift b/BitwardenShared/Core/Platform/Services/TestHelpers/MockConfigService.swift new file mode 100644 index 000000000..802bd7cd6 --- /dev/null +++ b/BitwardenShared/Core/Platform/Services/TestHelpers/MockConfigService.swift @@ -0,0 +1,30 @@ +import Foundation + +@testable import BitwardenShared + +class MockConfigService: ConfigService { + // MARK: Properties + + var config: ServerConfig? + var featureFlagsBool = [FeatureFlag: Bool]() + var featureFlagsInt = [FeatureFlag: Int]() + var featureFlagsString = [FeatureFlag: String]() + + // MARK: Methods + + func getConfig(forceRefresh: Bool) async -> ServerConfig? { + config + } + + func getFeatureFlag(_ flag: FeatureFlag, defaultValue: Bool, forceRefresh: Bool) async -> Bool { + featureFlagsBool[flag] ?? defaultValue + } + + func getFeatureFlag(_ flag: FeatureFlag, defaultValue: Int, forceRefresh: Bool) async -> Int { + featureFlagsInt[flag] ?? defaultValue + } + + func getFeatureFlag(_ flag: FeatureFlag, defaultValue: String?, forceRefresh: Bool) async -> String? { + featureFlagsString[flag] ?? defaultValue + } +} diff --git a/BitwardenShared/Core/Platform/Services/TestHelpers/MockStateService.swift b/BitwardenShared/Core/Platform/Services/TestHelpers/MockStateService.swift index b9d1caa07..94604d1b4 100644 --- a/BitwardenShared/Core/Platform/Services/TestHelpers/MockStateService.swift +++ b/BitwardenShared/Core/Platform/Services/TestHelpers/MockStateService.swift @@ -26,6 +26,7 @@ class MockStateService: StateService { // swiftlint:disable:this type_body_lengt var timeProvider = MockTimeProvider(.currentTime) var defaultUriMatchTypeByUserId = [String: UriMatchType]() var disableAutoTotpCopyByUserId = [String: Bool]() + var encryptedPinByUserId = [String: String]() var environmentUrls = [String: EnvironmentUrlData]() var forcePasswordResetReason = [String: ForcePasswordResetReason]() var lastActiveTime = [String: Date]() @@ -39,13 +40,13 @@ class MockStateService: StateService { // swiftlint:disable:this type_body_lengt var masterPasswordHashes = [String: String]() var notificationsLastRegistrationDates = [String: Date]() var passwordGenerationOptions = [String: PasswordGenerationOptions]() - var pinKeyEncryptedUserKeyValue = [String: String]() var pinProtectedUserKeyValue = [String: String]() var preAuthEnvironmentUrls: EnvironmentUrlData? var rememberedOrgIdentifier: String? var showWebIcons = true var showWebIconsSubject = CurrentValueSubject(true) var timeoutAction = [String: SessionTimeoutAction]() + var serverConfig = [String: ServerConfig]() var setBiometricAuthenticationEnabledResult: Result = .success(()) var setBiometricIntegrityStateError: Error? var shouldTrustDevice = [String: Bool?]() @@ -69,7 +70,7 @@ class MockStateService: StateService { // swiftlint:disable:this type_body_lengt let userId = try unwrapUserId(nil) accountVolatileData.removeValue(forKey: userId) pinProtectedUserKeyValue[userId] = nil - pinKeyEncryptedUserKeyValue[userId] = nil + encryptedPinByUserId[userId] = nil } func updateProfile(from response: ProfileResponseModel, userId: String) async { @@ -162,6 +163,11 @@ class MockStateService: StateService { // swiftlint:disable:this type_body_lengt return disableAutoTotpCopyByUserId[userId] ?? false } + func getEncryptedPin(userId: String?) async throws -> String? { + let userId = try unwrapUserId(userId) + return encryptedPinByUserId[userId] ?? nil + } + func getEnvironmentUrls(userId: String?) async throws -> EnvironmentUrlData? { let userId = try unwrapUserId(userId) return environmentUrls[userId] @@ -200,6 +206,11 @@ class MockStateService: StateService { // swiftlint:disable:this type_body_lengt preAuthEnvironmentUrls } + func getServerConfig(userId: String?) async throws -> ServerConfig? { + let userId = try unwrapUserId(userId) + return serverConfig[userId] + } + func getShouldTrustDevice(userId: String) async -> Bool? { shouldTrustDevice[userId] ?? false } @@ -242,11 +253,6 @@ class MockStateService: StateService { // swiftlint:disable:this type_body_lengt accountsLoggedOut.append(userId) } - func pinKeyEncryptedUserKey(userId: String?) async throws -> String? { - let userId = try unwrapUserId(userId) - return pinKeyEncryptedUserKeyValue[userId] ?? nil - } - func pinProtectedUserKey(userId: String?) async throws -> String? { let userId = try unwrapUserId(userId) return pinProtectedUserKeyValue[userId] ?? nil @@ -356,13 +362,13 @@ class MockStateService: StateService { // swiftlint:disable:this type_body_lengt } func setPinKeys( - pinKeyEncryptedUserKey: String, + encryptedPin: String, pinProtectedUserKey: String, requirePasswordAfterRestart: Bool ) async throws { let userId = try unwrapUserId(nil) pinProtectedUserKeyValue[userId] = pinProtectedUserKey - pinKeyEncryptedUserKeyValue[userId] = pinKeyEncryptedUserKey + encryptedPinByUserId[userId] = encryptedPin if requirePasswordAfterRestart { accountVolatileData[ @@ -384,6 +390,11 @@ class MockStateService: StateService { // swiftlint:disable:this type_body_lengt preAuthEnvironmentUrls = urls } + func setServerConfig(_ config: ServerConfig?, userId: String?) async throws { + let userId = try unwrapUserId(userId) + serverConfig[userId] = config + } + func setShouldTrustDevice(_ shouldTrustDevice: Bool?, userId: String) async { self.shouldTrustDevice[userId] = shouldTrustDevice } diff --git a/BitwardenShared/Core/Platform/Services/TestHelpers/ServiceContainer+Mocks.swift b/BitwardenShared/Core/Platform/Services/TestHelpers/ServiceContainer+Mocks.swift index 92cbb7916..4f0c61282 100644 --- a/BitwardenShared/Core/Platform/Services/TestHelpers/ServiceContainer+Mocks.swift +++ b/BitwardenShared/Core/Platform/Services/TestHelpers/ServiceContainer+Mocks.swift @@ -14,6 +14,7 @@ extension ServiceContainer { captchaService: CaptchaService = MockCaptchaService(), cameraService: CameraService = MockCameraService(), clientService: ClientService = MockClientService(), + configService: ConfigService = MockConfigService(), environmentService: EnvironmentService = MockEnvironmentService(), errorReporter: ErrorReporter = MockErrorReporter(), exportVaultService: ExportVaultService = MockExportVaultService(), @@ -56,6 +57,7 @@ extension ServiceContainer { captchaService: captchaService, cameraService: cameraService, clientService: clientService, + configService: configService, environmentService: environmentService, errorReporter: errorReporter, exportVaultService: exportVaultService, diff --git a/BitwardenShared/Core/Platform/Utilities/Constants.swift b/BitwardenShared/Core/Platform/Utilities/Constants.swift index 6fe21df7e..192a315d8 100644 --- a/BitwardenShared/Core/Platform/Utilities/Constants.swift +++ b/BitwardenShared/Core/Platform/Utilities/Constants.swift @@ -56,6 +56,9 @@ enum Constants { /// The maximum size of files for upload. static let maxFileSizeBytes = 104_857_600 + /// THe minimum number of minutes before attempting a server config sync again. + static let minimumConfigSyncInterval: TimeInterval = 60 * 60 // 60 minutes + /// A default value for the minimum number of characters required when creating a password. static let minimumPasswordCharacters = 12 diff --git a/BitwardenShared/Core/Vault/Services/API/Cipher/CipherAPIService.swift b/BitwardenShared/Core/Vault/Services/API/Cipher/CipherAPIService.swift index 3780bc593..74bb2a270 100644 --- a/BitwardenShared/Core/Vault/Services/API/Cipher/CipherAPIService.swift +++ b/BitwardenShared/Core/Vault/Services/API/Cipher/CipherAPIService.swift @@ -73,6 +73,12 @@ protocol CipherAPIService { /// func getCipher(withId id: String) async throws -> CipherDetailsResponseModel + /// Performs an API request to see if a user has any unassigned ciphers. This only applies + /// to users who are an organization owner or admin. + /// + /// - Returns: `true` if the user has any unassigned ciphers; `false` otherwise. + func hasUnassignedCiphers() async throws -> Bool + /// Performs an API request to restore a cipher in the user's trash. /// /// - Parameter id: The id of the cipher to be restored. @@ -154,6 +160,11 @@ extension APIService: CipherAPIService { try await apiService.send(GetCipherRequest(cipherId: id)) } + func hasUnassignedCiphers() async throws -> Bool { + let response = try await apiService.send(HasUnassignedCiphersRequest()) + return response.hasUnassignedCiphers + } + func restoreCipher(withID id: String) async throws -> EmptyResponse { try await apiService.send(RestoreCipherRequest(id: id)) } diff --git a/BitwardenShared/Core/Vault/Services/API/Cipher/Requests/HasUnassignedCiphersRequest.swift b/BitwardenShared/Core/Vault/Services/API/Cipher/Requests/HasUnassignedCiphersRequest.swift new file mode 100644 index 000000000..4cd4131d7 --- /dev/null +++ b/BitwardenShared/Core/Vault/Services/API/Cipher/Requests/HasUnassignedCiphersRequest.swift @@ -0,0 +1,12 @@ +import Foundation +import Networking + +// MARK: - HasUnassignedCiphersRequest + +/// A request for determining if a user has any unassigned ciphers. +/// +struct HasUnassignedCiphersRequest: Request { + typealias Response = HasUnassignedCiphersResponseModel + + let path = "/ciphers/has-unassigned-ciphers" +} diff --git a/BitwardenShared/Core/Vault/Services/API/Cipher/Responses/HasUnassignedCiphersResponseModel.swift b/BitwardenShared/Core/Vault/Services/API/Cipher/Responses/HasUnassignedCiphersResponseModel.swift new file mode 100644 index 000000000..4436e4b6d --- /dev/null +++ b/BitwardenShared/Core/Vault/Services/API/Cipher/Responses/HasUnassignedCiphersResponseModel.swift @@ -0,0 +1,28 @@ +import Foundation +import Networking + +// MARK: - HasUnassignedCiphersResponseModel + +/// An object containing a value indicating if the user has unassigned ciphers or not. +struct HasUnassignedCiphersResponseModel: JSONResponse { + static var decoder = JSONDecoder() + + // MARK: Properties + + /// A flag indicating if the user has unassigned ciphers or not. + var hasUnassignedCiphers: Bool + + // MARK: Initialization + + /// Creates a new `HasUnassignedCiphersResponseModel` instance. + /// + /// - Parameter hasUnassignedCiphers: A flag indicating if the user has unassigned ciphers or not. + init(hasUnassignedCiphers: Bool) { + self.hasUnassignedCiphers = hasUnassignedCiphers + } + + init(from decoder: Decoder) throws { + let container = try decoder.singleValueContainer() + hasUnassignedCiphers = try container.decode(Bool.self) + } +} diff --git a/BitwardenShared/UI/Auth/CreateAccount/CreateAccountView.swift b/BitwardenShared/UI/Auth/CreateAccount/CreateAccountView.swift index b151a0004..86bf8824d 100644 --- a/BitwardenShared/UI/Auth/CreateAccount/CreateAccountView.swift +++ b/BitwardenShared/UI/Auth/CreateAccount/CreateAccountView.swift @@ -79,7 +79,7 @@ struct CreateAccountView: View { get: \.emailText, send: CreateAccountAction.emailTextChanged ), - accessibilityIdentifier: "EmailAddressEntry" + accessibilityIdentifier: "CreateAccountEmailAddressEntry" ) .textFieldConfiguration(.email) diff --git a/BitwardenShared/UI/Auth/Login/LoginWithDevice/LoginWithDeviceProcessor.swift b/BitwardenShared/UI/Auth/Login/LoginWithDevice/LoginWithDeviceProcessor.swift index c3d3d70ad..1e78ab4da 100644 --- a/BitwardenShared/UI/Auth/Login/LoginWithDevice/LoginWithDeviceProcessor.swift +++ b/BitwardenShared/UI/Auth/Login/LoginWithDevice/LoginWithDeviceProcessor.swift @@ -139,40 +139,33 @@ final class LoginWithDeviceProcessor: StateProcessor< guard let requestId = self.state.requestId else { throw AuthError.missingData } let request = try await self.services.authService.checkPendingLoginRequest(withId: requestId) - guard let timer = self.checkTimer, timer.isValid else { return } - - // Show an alert and dismiss the view if the request has expired. - guard !request.isExpired else { - // Remove admin pending login request if exists - try await services.authService.setPendingAdminLoginRequest(nil, userId: nil) - - self.checkTimer?.invalidate() - return coordinator.showAlert(.requestExpired { - self.coordinator.navigate(to: .dismiss) - }) + guard request.requestApproved == true else { + if !request.isAnswered { + // Keep waiting and schedule the next timer if the request hasn't been + // answered and approved yet. + setCheckTimer() + } + return } - // Keep waiting if the request hasn't been answered yet. - guard request.isAnswered else { return } - // Remove admin pending login request if exists - try await services.authService.setPendingAdminLoginRequest(nil, userId: nil) - - // If the request has been denied, show an alert and dismiss the view. - if request.requestApproved == false { - self.checkTimer?.invalidate() - coordinator.showAlert(.requestDenied { - self.coordinator.navigate(to: .dismiss) - }) - return - } else if request.requestApproved == true { - // Otherwise, if the request has been approved, stop the update timer - // and attempt to authenticate. - self.checkTimer?.invalidate() - await self.attemptLogin(with: request) - } + try? await services.authService.setPendingAdminLoginRequest(nil, userId: nil) + + // Otherwise, if the request has been approved, stop the update timer + // and attempt to authenticate. + self.checkTimer?.invalidate() + await self.attemptLogin(with: request) + } catch CheckLoginRequestError.expired { + // If the request has expired, stop the timer but don't alert the user and remain + // on the view. + self.checkTimer?.invalidate() } catch { - self.coordinator.showAlert(.networkResponseError(error)) + // For any other errors, stop the timer while the alert is being shown and resume it + // when it's dismissed. + self.checkTimer?.invalidate() + self.coordinator.showAlert(.networkResponseError(error)) { + self.setCheckTimer() + } self.services.errorReporter.log(error: error) } } @@ -232,7 +225,7 @@ final class LoginWithDeviceProcessor: StateProcessor< checkTimer?.invalidate() // Set the timer to auto-check for a response every four seconds. - checkTimer = Timer.scheduledTimer(withTimeInterval: UI.duration(4), repeats: true) { [weak self] _ in + checkTimer = Timer.scheduledTimer(withTimeInterval: UI.duration(4), repeats: false) { [weak self] _ in self?.checkForResponse() } } diff --git a/BitwardenShared/UI/Auth/Login/LoginWithDevice/LoginWithDeviceProcessorTests.swift b/BitwardenShared/UI/Auth/Login/LoginWithDevice/LoginWithDeviceProcessorTests.swift index 9ae468556..f723a13de 100644 --- a/BitwardenShared/UI/Auth/Login/LoginWithDevice/LoginWithDeviceProcessorTests.swift +++ b/BitwardenShared/UI/Auth/Login/LoginWithDevice/LoginWithDeviceProcessorTests.swift @@ -114,46 +114,55 @@ class LoginWithDeviceProcessorTests: BitwardenTestCase { XCTAssertTrue(errorReporter.errors.isEmpty) } - /// `checkForResponse()` shows an alert and dismisses the view if the request has been denied. + /// `checkForResponse()`stops the request timer if the request has been denied. func test_checkForResponse_denied() throws { - let expiredLoginRequest = LoginRequest.fixture(requestApproved: false, responseDate: .now) - authService.checkPendingLoginRequestResult = .success(expiredLoginRequest) + let deniedLoginRequest = LoginRequest.fixture(requestApproved: false, responseDate: .now) + authService.checkPendingLoginRequestResult = .success(deniedLoginRequest) let task = Task { await self.subject.perform(.appeared) } + waitFor(subject.checkTimer?.isValid == false) + task.cancel() + + try XCTAssertFalse(XCTUnwrap(subject.checkTimer).isValid) + XCTAssertTrue(coordinator.alertShown.isEmpty) + XCTAssertTrue(coordinator.routes.isEmpty) + } + + /// `checkForResponse()` stops the request timer if the request returns an error. Once the error + /// alert has been dismissed, requests resume again. + func test_checkForResponse_error() throws { + authService.checkPendingLoginRequestResult = .failure(BitwardenTestError.example) + let task = Task { + await self.subject.perform(.appeared) + } waitFor(!coordinator.alertShown.isEmpty) task.cancel() - XCTAssertEqual(coordinator.alertShown.last, .requestDenied(action: {})) - XCTAssertTrue(subject.checkTimer?.isValid == false) + XCTAssertEqual(coordinator.alertShown, [.networkResponseError(BitwardenTestError.example)]) - let alert = try XCTUnwrap(coordinator.alertShown.last) - Task { try await alert.tapAction(title: Localizations.ok) } - waitFor(!coordinator.routes.isEmpty) - XCTAssertEqual(coordinator.routes, [.dismiss]) + authService.checkPendingLoginRequestResult = .success(.fixture(requestApproved: true)) + coordinator.alertOnDismissed?() + waitFor(!coordinator.events.isEmpty) + + XCTAssertEqual(coordinator.events, [.didCompleteAuth]) } - /// `checkForResponse()` shows an alert and dismisses the view if the request has expired. + /// `checkForResponse()` stops the request timer if the request has expired. func test_checkForResponse_expired() throws { - let expiredLoginRequest = LoginRequest.fixture(creationDate: .distantPast) - authService.checkPendingLoginRequestResult = .success(expiredLoginRequest) + authService.checkPendingLoginRequestResult = .failure(CheckLoginRequestError.expired) let task = Task { await self.subject.perform(.appeared) } - - waitFor(!coordinator.alertShown.isEmpty) + waitFor(subject.checkTimer?.isValid == false) task.cancel() - XCTAssertEqual(coordinator.alertShown.last, .requestExpired(action: {})) - XCTAssertTrue(subject.checkTimer?.isValid == false) - - let alert = try XCTUnwrap(coordinator.alertShown.last) - Task { try await alert.tapAction(title: Localizations.ok) } - waitFor(!coordinator.routes.isEmpty) - XCTAssertEqual(coordinator.routes, [.dismiss]) + try XCTAssertFalse(XCTUnwrap(subject.checkTimer).isValid) + XCTAssertTrue(coordinator.alertShown.isEmpty) + XCTAssertTrue(coordinator.routes.isEmpty) } /// `checkForResponse()` navigates the user to the two factor flow if it's required to complete login. diff --git a/BitwardenShared/UI/Auth/VaultUnlock/VaultUnlockProcessor.swift b/BitwardenShared/UI/Auth/VaultUnlock/VaultUnlockProcessor.swift index 1cc728877..d2ba0c224 100644 --- a/BitwardenShared/UI/Auth/VaultUnlock/VaultUnlockProcessor.swift +++ b/BitwardenShared/UI/Auth/VaultUnlock/VaultUnlockProcessor.swift @@ -111,7 +111,7 @@ class VaultUnlockProcessor: StateProcessor< do { if try await services.authRepository.isPinUnlockAvailable() { state.unlockMethod = .pin - } else if try await services.stateService.pinKeyEncryptedUserKey() != nil { + } else if try await services.stateService.getEncryptedPin() != nil { state.unlockMethod = .password } } catch { diff --git a/BitwardenShared/UI/Auth/VaultUnlock/VaultUnlockProcessorTests.swift b/BitwardenShared/UI/Auth/VaultUnlock/VaultUnlockProcessorTests.swift index e1e3a952c..54b653f7c 100644 --- a/BitwardenShared/UI/Auth/VaultUnlock/VaultUnlockProcessorTests.swift +++ b/BitwardenShared/UI/Auth/VaultUnlock/VaultUnlockProcessorTests.swift @@ -279,7 +279,7 @@ class VaultUnlockProcessorTests: BitwardenTestCase { // swiftlint:disable:this t func test_perform_unlockVault() async throws { stateService.activeAccount = Account.fixture() stateService.pinProtectedUserKeyValue["1"] = "123" - stateService.pinKeyEncryptedUserKeyValue["1"] = "123" + stateService.encryptedPinByUserId["1"] = "123" subject.state.masterPassword = "password" await subject.perform(.unlockVault) diff --git a/BitwardenShared/UI/Platform/Application/AppProcessor.swift b/BitwardenShared/UI/Platform/Application/AppProcessor.swift index c58cec856..5a655de7c 100644 --- a/BitwardenShared/UI/Platform/Application/AppProcessor.swift +++ b/BitwardenShared/UI/Platform/Application/AppProcessor.swift @@ -96,6 +96,7 @@ public class AppProcessor { await services.migrationService.performMigrations() await services.environmentService.loadURLsForActiveAccount() + _ = await services.configService.getConfig() services.application?.registerForRemoteNotifications() diff --git a/BitwardenShared/UI/Platform/Application/Support/Localizations/af.lproj/Localizable.strings b/BitwardenShared/UI/Platform/Application/Support/Localizations/af.lproj/Localizable.strings index 642981325..847041fc5 100644 --- a/BitwardenShared/UI/Platform/Application/Support/Localizations/af.lproj/Localizable.strings +++ b/BitwardenShared/UI/Platform/Application/Support/Localizations/af.lproj/Localizable.strings @@ -864,4 +864,11 @@ "AccountLoggedOutBiometricExceeded" = "Account logged out."; "YourOrganizationPermissionsWereUpdatedRequeringYouToSetAMasterPassword" = "Your organization permissions were updated, requiring you to set a master password."; "YourOrganizationRequiresYouToSetAMasterPassword" = "Your organization requires you to set a master password."; -"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Set up an unlock option to change your vault timeout action."; \ No newline at end of file +"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Set up an unlock option to change your vault timeout action."; +"DuoTwoStepLoginIsRequiredForYourAccount" = "Duo two-step login is required for your account."; +"FollowTheStepsFromDuoToFinishLoggingIn" = "Follow the steps from Duo to finish logging in."; +"LaunchDuo" = "Launch Duo"; +"OrganizationUnassignedItemsMessageUSEUDescriptionLong" = "Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."; +"OrganizationUnassignedItemsMessageSelfHost041624DescriptionLong" = "On May 16, 2024, unassigned organization items will no longer be visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."; +"RemindMeLater" = "Remind me later"; +"Notice" = "Notice"; \ No newline at end of file diff --git a/BitwardenShared/UI/Platform/Application/Support/Localizations/ar.lproj/Localizable.strings b/BitwardenShared/UI/Platform/Application/Support/Localizations/ar.lproj/Localizable.strings index da041655b..a16e428c8 100644 --- a/BitwardenShared/UI/Platform/Application/Support/Localizations/ar.lproj/Localizable.strings +++ b/BitwardenShared/UI/Platform/Application/Support/Localizations/ar.lproj/Localizable.strings @@ -42,7 +42,7 @@ "LogInNoun" = "تسجيل الدخول"; "LogOut" = "تسجيل الخروج"; "LogoutConfirmation" = "هل أنت متأكد من أنك تريد تسجيل الخروج؟"; -"RemoveAccount" = "إزالة الحساب"; +"RemoveAccount" = "أزِل الحساب"; "RemoveAccountConfirmation" = "هل أنت متأكد من أنك تريد إزالة هذا الحساب؟"; "AccountAlreadyAdded" = "تمت إضافة الحساب بالفعل"; "SwitchToAlreadyAddedAccountConfirmation" = "هل ترغب في التبديل إليه الآن؟"; @@ -847,14 +847,14 @@ "ContinueToX" = "الإستمرار إلى %1$@؟"; "ContinueToHelpCenter" = "هل تريد المتابعة إلى مركز المساعدة؟"; "ContinueToContactSupport" = "مواصلة الاتصال بالدعم؟"; -"ContinueToPrivacyPolicy" = "Continue to privacy policy?"; +"ContinueToPrivacyPolicy" = "هل تريد المتابعة إلى سياسة الخصوصية؟"; "ContinueToAppStore" = "هل تريد المتابعة إلى متجر التطبيقات؟"; "TwoStepLoginDescriptionLong" = "اجعل حسابك أكثر أمنا من خلال إعداد تسجيل الدخول بخطوتين في تطبيق Bitwarden على شبكة الإنترنت."; "ChangeMasterPasswordDescriptionLong" = "يمكنك تغيير كلمة المرور الرئيسية الخاصة بك على تطبيق ويب Bitwarden."; "YouCanImportDataToYourVaultOnX" = "يمكنك استيراد البيانات إلى خزانتك على %1$@."; "LearnMoreAboutHowToUseBitwardenOnTheHelpCenter" = "تعرف على المزيد حول كيفية استخدام Bitwarden في مركز المساعدة."; "ContactSupportDescriptionLong" = "لا يمكن العثور على ما تبحث عنه؟ قم بالتواصل مع دعم Bitwarden على bitwarden.com."; -"PrivacyPolicyDescriptionLong" = "Check out our privacy policy on bitwarden.com."; +"PrivacyPolicyDescriptionLong" = "اطلع على سياستنا للخصوصية على bitwarden.com."; "ExploreMoreFeaturesOfYourBitwardenAccountOnTheWebApp" = "استكشف المزيد من الميزات لحساب Bitwarden الخاص بك على تطبيق الويب."; "LearnAboutOrganizationsDescriptionLong" = "يتيح لك Bitwarden مشاركة عناصر خزنتك مع الآخرين باستخدام حساب المؤسسة. تعرف على المزيد على موقع bitwarden.com على شبكة الإنترنت."; "RateAppDescriptionLong" = "ساعد الآخرين في معرفة ما إذا كان Bitwarden مناسبا لهم. قم بزيارة متجر التطبيقات وترك التقييم الآن."; @@ -864,4 +864,11 @@ "AccountLoggedOutBiometricExceeded" = "تم تسجيل الخروج من الحساب."; "YourOrganizationPermissionsWereUpdatedRequeringYouToSetAMasterPassword" = "تم تحديث أذونات مؤسستك ، مما يتطلب عليك تعيين كلمة مرور رئيسية."; "YourOrganizationRequiresYouToSetAMasterPassword" = "تتطلب مؤسستك تعيين كلمة مرور رئيسية."; -"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "أعدنّ ميزة إلغاء القُفْل لتغيير إجراء مهلة المخزن الخاص بك."; \ No newline at end of file +"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "أعدنّ ميزة إلغاء القُفْل لتغيير إجراء مهلة المخزن الخاص بك."; +"DuoTwoStepLoginIsRequiredForYourAccount" = "تسجيل الدخول لـ Duo من خطوتين مطلوب لحسابك."; +"FollowTheStepsFromDuoToFinishLoggingIn" = "اتبع الخطوات من Duo لإنهاء تسجيل الدخول."; +"LaunchDuo" = "تشغيل Duo"; +"OrganizationUnassignedItemsMessageUSEUDescriptionLong" = "Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."; +"OrganizationUnassignedItemsMessageSelfHost041624DescriptionLong" = "On May 16, 2024, unassigned organization items will no longer be visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."; +"RemindMeLater" = "Remind me later"; +"Notice" = "Notice"; \ No newline at end of file diff --git a/BitwardenShared/UI/Platform/Application/Support/Localizations/az.lproj/Localizable.strings b/BitwardenShared/UI/Platform/Application/Support/Localizations/az.lproj/Localizable.strings index 347991ff9..b1824c318 100644 --- a/BitwardenShared/UI/Platform/Application/Support/Localizations/az.lproj/Localizable.strings +++ b/BitwardenShared/UI/Platform/Application/Support/Localizations/az.lproj/Localizable.strings @@ -103,13 +103,13 @@ "ExntesionReenable" = "Tətbiq uzantısını təkrar aktivləşdir"; "ExtensionAlmostDone" = "Demək olar ki, hazırdır!"; "ExtensionEnable" = "Tətbiq uzantısını aktivləşdir"; -"ExtensionInSafari" = "Safari-də paylaş nişanını istifadə edərək Bitwarden-i tapın (məsləhət: Menyunun ən alt sətrinin sağ hissəsinə baxın)."; +"ExtensionInSafari" = "Safari-də paylaş ikonunu istifadə edərək Bitwarden-i tapın (ipucu: menyunun alt sətrində sağa sürüşdürün)."; "ExtensionInstantAccess" = "Parollarınıza dərhal müraciət edin!"; "ExtensionReady" = "Giriş etmək üçün hazırsınız!"; "ExtensionSetup" = "Giriş məlumatlarınıza artıq Safari, Chrome və digər dəstəklənən tətbiqlərdən asanlıqla müraciət edə bilərsiniz."; -"ExtensionSetup2" = "Safari və Chrome-da paylaş nişanını istifadə edərək Bitwarden-i tapın (məsləhət: Paylaş menyusunun ən alt sətrinin sağ hissəsinə baxın)."; -"ExtensionTapIcon" = "Uzantını başlatmaq üçün menyudakı Bitwarden nişanına toxunun."; -"ExtensionTurnOn" = "Safari və digər tətbiqlərdə Bitwarden-i işə salmaq üçün menyunun ən alt sətrindəki \"daha çox\" nişanına toxunun."; +"ExtensionSetup2" = "Safari və Chrome-da paylaş ikonunu istifadə edərək Bitwarden-i tapın (ipucu: paylaşma menyusunun alt sətrində sağa sürüşdürün)."; +"ExtensionTapIcon" = "Uzantını başlatmaq üçün menyudakı Bitwarden ikonuna toxunun."; +"ExtensionTurnOn" = "Safari və digər tətbiqlərdə Bitwarden-i işə salmaq üçün menyunun alt sətrindəki \"daha çox\" ikonuna toxunun."; "Favorite" = "Sevimli"; "Fingerprint" = "Barmaq izi"; "GeneratePassword" = "Parol yarat"; @@ -183,7 +183,7 @@ "Lost2FAApp" = "Kimlik doğrulayıcı tətbiqini itirmisiniz?"; "Items" = "Elementlər"; "ExtensionActivated" = "Uzantı aktivləşdirildi!"; -"Icons" = "Nişanlar"; +"Icons" = "İkonlar"; "Translations" = "Tərcümələr"; "ItemsForUri" = "%1$@ üçün elementlər"; "NoItemsForUri" = "Anbarınızda %1$@ üçün heç bir element yoxdur."; @@ -311,9 +311,9 @@ "ZipPostalCode" = "Zip/ Poçt kodu"; "Address" = "Ünvan"; "Expiration" = "Bitmə vaxtı"; -"ShowWebsiteIcons" = "Veb sayt nişanlarını göstər"; +"ShowWebsiteIcons" = "Veb sayt ikonlarını göstər"; "ShowWebsiteIconsDescription" = "Hər girişin yanında tanına bilən təsvir göstər."; -"IconsUrl" = "Nişan server URL-si"; +"IconsUrl" = "İkon server URL-si"; "AutofillWithBitwarden" = "Bitwarden ilə avto-doldurma"; "VaultIsLocked" = "Anbar kilidlənib"; "GoToMyVault" = "Anbarıma get"; @@ -864,4 +864,11 @@ "AccountLoggedOutBiometricExceeded" = "Hesabdan çıxış edildi."; "YourOrganizationPermissionsWereUpdatedRequeringYouToSetAMasterPassword" = "Təşkilatınızın icazələri güncəlləndi və bir ana parol ayarlamağınızı tələb edir."; "YourOrganizationRequiresYouToSetAMasterPassword" = "Təşkilatınız bir ana parol ayarlamağı tələb edir."; -"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Anbar vaxt bitməsi əməliyyatınızı dəyişdirmək üçün bir kilid açma seçimi qurun."; \ No newline at end of file +"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Anbar vaxt bitməsi əməliyyatınızı dəyişdirmək üçün bir kilid açma seçimi qurun."; +"DuoTwoStepLoginIsRequiredForYourAccount" = "Hesabınız üçün Duo iki addımlı giriş tələb olunur."; +"FollowTheStepsFromDuoToFinishLoggingIn" = "Giriş etməni tamamlamaq üçün Duo-dakı addımları izləyin."; +"LaunchDuo" = "Duo-nu başlat"; +"OrganizationUnassignedItemsMessageUSEUDescriptionLong" = "Təyin edilməmiş təşkilat elementləri artıq Bütün Anbarlar görünüşündə görünməyəndir və yalnız Admin Konsolu vasitəsilə əlçatandır. Bu elementləri görünən etmək üçün Admin Konsolundan bir kolleksiyaya təyin edin."; +"OrganizationUnassignedItemsMessageSelfHost041624DescriptionLong" = "16 May 2024-cü ildən etibarən təyin edilməmiş təşkilat elementləri artıq Bütün Anbarlar görünüşündə görünməyən və yalnız Admin Konsolu vasitəsilə əlçatan olacaq. Bu elementləri görünən etmək üçün Admin Konsolundan bir kolleksiyaya təyin edin."; +"RemindMeLater" = "Daha sonra xatırlat"; +"Notice" = "Xəbərdarlıq"; \ No newline at end of file diff --git a/BitwardenShared/UI/Platform/Application/Support/Localizations/be.lproj/Localizable.strings b/BitwardenShared/UI/Platform/Application/Support/Localizations/be.lproj/Localizable.strings index 5f295dbfe..e089fe646 100644 --- a/BitwardenShared/UI/Platform/Application/Support/Localizations/be.lproj/Localizable.strings +++ b/BitwardenShared/UI/Platform/Application/Support/Localizations/be.lproj/Localizable.strings @@ -782,12 +782,12 @@ "Region" = "Рэгіён"; "UpdateWeakMasterPasswordWarning" = "Ваш асноўны пароль не адпавядае адной або некалькім палітыкам арганізацыі. Для атрымання доступу да сховішча, вы павінны абнавіць яго. Працягваючы, вы выйдзіце з бягучага сеанса і вам неабходна будзе ўвайсці паўторна. Актыўныя сеансы на іншых прыладах могуць заставацца актыўнымі на працягу адной гадзіны."; "CurrentMasterPassword" = "Бягучы асноўны пароль"; -"LoggedIn" = "Logged in!"; +"LoggedIn" = "Вы ўвайшлі!"; "ApproveWithMyOtherDevice" = "Approve with my other device"; -"RequestAdminApproval" = "Request admin approval"; -"ApproveWithMasterPassword" = "Approve with master password"; +"RequestAdminApproval" = "Запытаць ухваленне адміністратара"; +"ApproveWithMasterPassword" = "Ухваліць з дапамогай асноўнага пароля"; "TurnOffUsingPublicDevice" = "Turn off using a public device"; -"RememberThisDevice" = "Remember this device"; +"RememberThisDevice" = "Запомніць гэту прыладу"; "Passkey" = "Passkey"; "Passkeys" = "Passkeys"; "Application" = "Праграма"; @@ -800,11 +800,11 @@ "UnlockingMayFailDueToInsufficientMemoryDecreaseYourKDFMemorySettingsToResolve" = "Unlocking may fail due to insufficient memory. Decrease your KDF memory settings or set up biometric unlock to resolve."; "InvalidAPIKey" = "Памылковы ключ API"; "InvalidAPIToken" = "Памылковы токен API"; -"AdminApprovalRequested" = "Admin approval requested"; -"YourRequestHasBeenSentToYourAdmin" = "Your request has been sent to your admin."; -"YouWillBeNotifiedOnceApproved" = "You will be notified once approved."; -"TroubleLoggingIn" = "Trouble logging in?"; -"LoggingInAsX" = "Logging in as %1$@"; +"AdminApprovalRequested" = "Патрабуецца ўхваленне адміністратара"; +"YourRequestHasBeenSentToYourAdmin" = "Ваш запыт адпраўлены адміністратару."; +"YouWillBeNotifiedOnceApproved" = "Вы атрымаеце апавяшчэння пасля яго ўхвалення."; +"TroubleLoggingIn" = "Праблемы з уваходам?"; +"LoggingInAsX" = "Увайсці як %1$@"; "VaultTimeoutActionChangedToLogOut" = "Vault timeout action changed to log out"; "BlockAutoFill" = "Блакіраваць аўтазапаўненне"; "AutoFillWillNotBeOfferedForTheseURIs" = "Аўтазапаўненне для гэтых URI прапаноўвацца не будзе."; @@ -820,17 +820,17 @@ "ThereAreNoBlockedURIs" = "Заблакіраваных URI пакуль няма"; "TheURIXIsAlreadyBlocked" = "Гэты URI %1$@ ужо заблакіраваны"; "CannotEditMultipleURIsAtOnce" = "Немагчыма рэдагаваць некалькі URI адначасова"; -"LoginApproved" = "Login approved"; +"LoginApproved" = "Уваход ухвалены"; "LogInWithDeviceMustBeSetUpInTheSettingsOfTheBitwardenAppNeedAnotherOption" = "Log in with device must be set up in the settings of the Bitwarden app. Need another option?"; "LogInWithDevice" = "Log in with device"; -"LoggingInOn" = "Logging in on"; -"Vault" = "Vault"; -"Appearance" = "Appearance"; -"AccountSecurity" = "Account security"; -"BitwardenHelpCenter" = "Bitwarden Help Center"; -"ContactBitwardenSupport" = "Contact Bitwarden support"; +"LoggingInOn" = "Увайсці на"; +"Vault" = "Сховішча"; +"Appearance" = "Знешні выгляд"; +"AccountSecurity" = "Бяспеке акаўнта"; +"BitwardenHelpCenter" = "Даведачны цэнтр Bitwarden"; +"ContactBitwardenSupport" = "Звярніцеся ў службу падтрымкі Bitwarden"; "CopyAppInformation" = "Copy app information"; -"SyncNow" = "Sync now"; +"SyncNow" = "Сінхранізаваць зараз"; "UnlockOptions" = "Unlock options"; "SessionTimeout" = "Session timeout"; "SessionTimeoutAction" = "Session timeout action"; @@ -842,7 +842,7 @@ "XHours" = "%1$@ hours"; "AutofillServicesExplanationLong" = "The Android Autofill Framework is used to assist in filling login information into other apps on your device."; "UseInlineAutofillExplanationLong" = "Use inline autofill if your selected keyboard supports it. Otherwise, use the default overlay."; -"AdditionalOptions" = "Additional options"; +"AdditionalOptions" = "Дадатковы параметры"; "ContinueToWebApp" = "Continue to web app?"; "ContinueToX" = "Continue to %1$@?"; "ContinueToHelpCenter" = "Continue to Help center?"; @@ -864,4 +864,11 @@ "AccountLoggedOutBiometricExceeded" = "Account logged out."; "YourOrganizationPermissionsWereUpdatedRequeringYouToSetAMasterPassword" = "Your organization permissions were updated, requiring you to set a master password."; "YourOrganizationRequiresYouToSetAMasterPassword" = "Your organization requires you to set a master password."; -"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Set up an unlock option to change your vault timeout action."; \ No newline at end of file +"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Set up an unlock option to change your vault timeout action."; +"DuoTwoStepLoginIsRequiredForYourAccount" = "Duo two-step login is required for your account."; +"FollowTheStepsFromDuoToFinishLoggingIn" = "Follow the steps from Duo to finish logging in."; +"LaunchDuo" = "Launch Duo"; +"OrganizationUnassignedItemsMessageUSEUDescriptionLong" = "Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."; +"OrganizationUnassignedItemsMessageSelfHost041624DescriptionLong" = "On May 16, 2024, unassigned organization items will no longer be visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."; +"RemindMeLater" = "Remind me later"; +"Notice" = "Notice"; \ No newline at end of file diff --git a/BitwardenShared/UI/Platform/Application/Support/Localizations/bg.lproj/Localizable.strings b/BitwardenShared/UI/Platform/Application/Support/Localizations/bg.lproj/Localizable.strings index 2656dda26..1bb130238 100644 --- a/BitwardenShared/UI/Platform/Application/Support/Localizations/bg.lproj/Localizable.strings +++ b/BitwardenShared/UI/Platform/Application/Support/Localizations/bg.lproj/Localizable.strings @@ -864,4 +864,11 @@ "AccountLoggedOutBiometricExceeded" = "Акаунтът е отписан."; "YourOrganizationPermissionsWereUpdatedRequeringYouToSetAMasterPassword" = "Правата Ви в организацията бяха променени, необходимо е да зададете главна парола."; "YourOrganizationRequiresYouToSetAMasterPassword" = "Организацията Ви изисква да зададете главна парола."; -"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Задайте начин за отключване, за да може да промените действието при изтичане на времето за достъп до трезора."; \ No newline at end of file +"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Задайте начин за отключване, за да може да промените действието при изтичане на времето за достъп до трезора."; +"DuoTwoStepLoginIsRequiredForYourAccount" = "Вашата регистрация изисква двустепенно удостоверяване чрез Duo."; +"FollowTheStepsFromDuoToFinishLoggingIn" = "Следвайте стъпките от Duo, за да завършите вписването."; +"LaunchDuo" = "Стартиране на Duo"; +"OrganizationUnassignedItemsMessageUSEUDescriptionLong" = "Неразпределените елементи на организацията вече не се виждат в изгледа с „Всички трезори“, а са достъпни само през Административната конзола. Добавете тези елементи към някоя колекция в Административната конзола, за да станат видими."; +"OrganizationUnassignedItemsMessageSelfHost041624DescriptionLong" = "От 16 май 2024г. неразпределените елементи на организациите вече няма се виждат в изгледа с „Всички трезори“, а ще бъдат достъпни само през Административната конзола. Добавете тези елементи към някоя колекция в Административната конзола, за да станат видими."; +"RemindMeLater" = "Напомнете ми по-късно"; +"Notice" = "Известие"; \ No newline at end of file diff --git a/BitwardenShared/UI/Platform/Application/Support/Localizations/bn.lproj/Localizable.strings b/BitwardenShared/UI/Platform/Application/Support/Localizations/bn.lproj/Localizable.strings index 435c85ca6..462d07dd5 100644 --- a/BitwardenShared/UI/Platform/Application/Support/Localizations/bn.lproj/Localizable.strings +++ b/BitwardenShared/UI/Platform/Application/Support/Localizations/bn.lproj/Localizable.strings @@ -864,4 +864,11 @@ "AccountLoggedOutBiometricExceeded" = "Account logged out."; "YourOrganizationPermissionsWereUpdatedRequeringYouToSetAMasterPassword" = "Your organization permissions were updated, requiring you to set a master password."; "YourOrganizationRequiresYouToSetAMasterPassword" = "Your organization requires you to set a master password."; -"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Set up an unlock option to change your vault timeout action."; \ No newline at end of file +"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Set up an unlock option to change your vault timeout action."; +"DuoTwoStepLoginIsRequiredForYourAccount" = "Duo two-step login is required for your account."; +"FollowTheStepsFromDuoToFinishLoggingIn" = "Follow the steps from Duo to finish logging in."; +"LaunchDuo" = "Launch Duo"; +"OrganizationUnassignedItemsMessageUSEUDescriptionLong" = "Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."; +"OrganizationUnassignedItemsMessageSelfHost041624DescriptionLong" = "On May 16, 2024, unassigned organization items will no longer be visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."; +"RemindMeLater" = "Remind me later"; +"Notice" = "Notice"; \ No newline at end of file diff --git a/BitwardenShared/UI/Platform/Application/Support/Localizations/bs.lproj/Localizable.strings b/BitwardenShared/UI/Platform/Application/Support/Localizations/bs.lproj/Localizable.strings index 2c4c1630b..084591689 100644 --- a/BitwardenShared/UI/Platform/Application/Support/Localizations/bs.lproj/Localizable.strings +++ b/BitwardenShared/UI/Platform/Application/Support/Localizations/bs.lproj/Localizable.strings @@ -864,4 +864,11 @@ "AccountLoggedOutBiometricExceeded" = "Account logged out."; "YourOrganizationPermissionsWereUpdatedRequeringYouToSetAMasterPassword" = "Your organization permissions were updated, requiring you to set a master password."; "YourOrganizationRequiresYouToSetAMasterPassword" = "Your organization requires you to set a master password."; -"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Set up an unlock option to change your vault timeout action."; \ No newline at end of file +"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Set up an unlock option to change your vault timeout action."; +"DuoTwoStepLoginIsRequiredForYourAccount" = "Duo two-step login is required for your account."; +"FollowTheStepsFromDuoToFinishLoggingIn" = "Follow the steps from Duo to finish logging in."; +"LaunchDuo" = "Launch Duo"; +"OrganizationUnassignedItemsMessageUSEUDescriptionLong" = "Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."; +"OrganizationUnassignedItemsMessageSelfHost041624DescriptionLong" = "On May 16, 2024, unassigned organization items will no longer be visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."; +"RemindMeLater" = "Remind me later"; +"Notice" = "Notice"; \ No newline at end of file diff --git a/BitwardenShared/UI/Platform/Application/Support/Localizations/ca.lproj/Localizable.strings b/BitwardenShared/UI/Platform/Application/Support/Localizations/ca.lproj/Localizable.strings index bbcce1ba5..4710a0935 100644 --- a/BitwardenShared/UI/Platform/Application/Support/Localizations/ca.lproj/Localizable.strings +++ b/BitwardenShared/UI/Platform/Application/Support/Localizations/ca.lproj/Localizable.strings @@ -864,4 +864,11 @@ "AccountLoggedOutBiometricExceeded" = "S'ha tancat la sessió del compte."; "YourOrganizationPermissionsWereUpdatedRequeringYouToSetAMasterPassword" = "Els permisos de la vostra organització s'han actualitzat, cal que establiu una contrasenya mestra."; "YourOrganizationRequiresYouToSetAMasterPassword" = "La vostra organització requereix que establiu una contrasenya mestra."; -"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Configura una opció de desbloqueig per canviar l'acció de temps d'espera de la caixa forta."; \ No newline at end of file +"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Configura una opció de desbloqueig per canviar l'acció de temps d'espera de la caixa forta."; +"DuoTwoStepLoginIsRequiredForYourAccount" = "Es requereix l'inici de sessió en dos passos de DUO al vostre compte."; +"FollowTheStepsFromDuoToFinishLoggingIn" = "Seguiu els passos de Duo per acabar d'iniciar la sessió."; +"LaunchDuo" = "Inicia DUO"; +"OrganizationUnassignedItemsMessageUSEUDescriptionLong" = "Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."; +"OrganizationUnassignedItemsMessageSelfHost041624DescriptionLong" = "On May 16, 2024, unassigned organization items will no longer be visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."; +"RemindMeLater" = "Remind me later"; +"Notice" = "Notice"; \ No newline at end of file diff --git a/BitwardenShared/UI/Platform/Application/Support/Localizations/cs.lproj/Localizable.strings b/BitwardenShared/UI/Platform/Application/Support/Localizations/cs.lproj/Localizable.strings index 4452fe3ac..b83a96d0b 100644 --- a/BitwardenShared/UI/Platform/Application/Support/Localizations/cs.lproj/Localizable.strings +++ b/BitwardenShared/UI/Platform/Application/Support/Localizations/cs.lproj/Localizable.strings @@ -864,4 +864,11 @@ "AccountLoggedOutBiometricExceeded" = "Účet byl odhlášen."; "YourOrganizationPermissionsWereUpdatedRequeringYouToSetAMasterPassword" = "Oprávnění Vaší organizace byla aktualizována. To vyžaduje nastavení hlavního hesla."; "YourOrganizationRequiresYouToSetAMasterPassword" = "Vaše organizace vyžaduje nastavení hlavního hesla."; -"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Nastavte volbu odemknutí, abyste změnili časový limit Vašeho trezoru."; \ No newline at end of file +"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Nastavte volbu odemknutí, abyste změnili časový limit Vašeho trezoru."; +"DuoTwoStepLoginIsRequiredForYourAccount" = "Pro Váš účet je vyžadováno dvoufázové přihlášení DUO."; +"FollowTheStepsFromDuoToFinishLoggingIn" = "Postupujte podle kroků od DUO pro dokončení přihlášení."; +"LaunchDuo" = "Spustit DUO"; +"OrganizationUnassignedItemsMessageUSEUDescriptionLong" = "Nepřiřazené položky organizace již nejsou viditelné ve Vašem zobrazení všech trezorů a jsou nyní přístupné jen v konzoli správce. Přiřaďte tyto položky do kolekce z konzole pro správce, aby byly viditelné."; +"OrganizationUnassignedItemsMessageSelfHost041624DescriptionLong" = "Dne 16. května 2024 již nebudou nepřiřazené položky organizace viditelné v zobrazení Všechny trezory a budou přístupné jen prostřednictvím konzoly správce. Přiřaďte tyto položky do kolekce z konzoly pro správce, aby byly viditelné."; +"RemindMeLater" = "Připomenout později"; +"Notice" = "Upozornění"; \ No newline at end of file diff --git a/BitwardenShared/UI/Platform/Application/Support/Localizations/cy.lproj/Localizable.strings b/BitwardenShared/UI/Platform/Application/Support/Localizations/cy.lproj/Localizable.strings index 8e1ac25c4..4a7c4f08a 100644 --- a/BitwardenShared/UI/Platform/Application/Support/Localizations/cy.lproj/Localizable.strings +++ b/BitwardenShared/UI/Platform/Application/Support/Localizations/cy.lproj/Localizable.strings @@ -53,7 +53,7 @@ "Name" = "Enw"; "No" = "Na"; "Notes" = "Nodiadau"; -"Ok" = "Ok"; +"Ok" = "Iawn"; "Password" = "Cyfrinair"; "Save" = "Cadw"; "Move" = "Symud"; @@ -61,17 +61,17 @@ "Settings" = "Gosodiadau"; "Show" = "Dangos"; "ItemDeleted" = "Eitem wedi'i dileu"; -"Submit" = "Submit"; +"Submit" = "Cyflwyno"; "Sync" = "Cysoni"; "ThankYou" = "Diolch"; "Tools" = "Offer"; "URI" = "URI"; "UseFingerprintToUnlock" = "Use fingerprint to unlock"; "Username" = "Enw defnyddiwr"; -"ValidationFieldRequired" = "The %1$@ field is required."; +"ValidationFieldRequired" = "Mae'r maes %1$@ yn ofynnol."; "ValueHasBeenCopied" = "%1$@ wedi'i gopïo"; "VerifyFingerprint" = "Verify fingerprint"; -"VerifyMasterPassword" = "Verify master password"; +"VerifyMasterPassword" = "Gwirio'r prif gyfrinair"; "VerifyPIN" = "Verify PIN"; "Version" = "Fersiwn"; "View" = "Gweld"; @@ -79,7 +79,7 @@ "Website" = "Gwefan"; "Yes" = "Ydw"; "Account" = "Cyfrif"; -"AccountCreated" = "Your new account has been created! You may now log in."; +"AccountCreated" = "Mae eich cyfrif newydd wedi cael ei greu! Gallwch bellach fewngofnodi."; "AddAnItem" = "Ychwanegu eitem"; "AppExtension" = "App extension"; "AutofillAccessibilityDescription" = "Use the Bitwarden accessibility service to auto-fill your logins across apps and the web."; @@ -200,16 +200,16 @@ "BitwardenAutofillServiceAlert2" = "The easiest way to add new logins to your vault is from the Bitwarden Auto-fill Service. Learn more about using the Bitwarden Auto-fill Service by navigating to the \"Settings\" screen."; "Autofill" = "Llenwi'n awtomatig"; "AutofillOrView" = "Do you want to auto-fill or view this item?"; -"BitwardenAutofillServiceMatchConfirm" = "Are you sure you want to auto-fill this item? It is not a complete match for \"%1$@\"."; -"MatchingItems" = "Matching items"; +"BitwardenAutofillServiceMatchConfirm" = "Ydych chi'n siŵr eich bod am lenwi'r eitem hon? Dyw hi ddim yn cyfateb yn llwyr i \"%1$@\"."; +"MatchingItems" = "Eitemau sy'n cyfateb"; "PossibleMatchingItems" = "Possible matching items"; "Search" = "Chwilio"; "BitwardenAutofillServiceSearch" = "You are searching for an auto-fill item for \"%1$@\"."; -"LearnOrg" = "Learn about organizations"; -"CannotOpenApp" = "Cannot open the app \"%1$@\"."; -"AuthenticatorAppTitle" = "Authenticator app"; -"EnterVerificationCodeApp" = "Enter the 6 digit verification code from your authenticator app."; -"EnterVerificationCodeEmail" = "Enter the 6 digit verification code that was emailed to %1$@."; +"LearnOrg" = "Dysgu am sefydliadau"; +"CannotOpenApp" = "Methu agor yr ap \"%1$@\"."; +"AuthenticatorAppTitle" = "Ap dilysu"; +"EnterVerificationCodeApp" = "Rhowch y cod dilysu 6 nod o'ch ap dilysu."; +"EnterVerificationCodeEmail" = "Rhowch y cod dilysu 6 nod anfonwyd i %1$@."; "LoginUnavailable" = "Login unavailable"; "NoTwoStepAvailable" = "This account has two-step login set up, however, none of the configured two-step providers are supported on this device. Please use a supported device and/or add additional providers that are better supported across devices (such as an authenticator app)."; "RecoveryCodeTitle" = "Cod adfer"; @@ -232,7 +232,7 @@ "AuthenticatorKeyAdded" = "Authenticator key added."; "AuthenticatorKeyReadError" = "Cannot read authenticator key."; "PointYourCameraAtTheQRCode" = "Point your camera at the QR Code.\nScanning will happen automatically."; -"ScanQrTitle" = "Scan QR Code"; +"ScanQrTitle" = "Sganio cod QR"; "Camera" = "Camera"; "Photos" = "Lluniau"; "CopyTotp" = "Copy TOTP"; @@ -314,7 +314,7 @@ "ShowWebsiteIcons" = "Dangos eiconau gwefannau"; "ShowWebsiteIconsDescription" = "Dangos delwedd adnabyddadwy wrth ymyl pob eitem."; "IconsUrl" = "Icons server URL"; -"AutofillWithBitwarden" = "Auto-fill with Bitwarden"; +"AutofillWithBitwarden" = "Llenwi'n awtomatig â Bitwarden"; "VaultIsLocked" = "Vault is locked"; "GoToMyVault" = "Go to my vault"; "Collections" = "Casgliadau"; @@ -324,7 +324,7 @@ "AutofillAccessibilityService" = "Auto-fill Accessibility Service"; "AutofillServiceDescription" = "The Bitwarden auto-fill service uses the Android Autofill Framework to assist in filling login information into other apps on your device."; "BitwardenAutofillServiceDescription" = "Use the Bitwarden auto-fill service to fill login information into other apps."; -"BitwardenAutofillServiceOpenAutofillSettings" = "Open Autofill Settings"; +"BitwardenAutofillServiceOpenAutofillSettings" = "Agor gosodiadau llenwi awtomatig"; "FaceID" = "Face ID"; "FaceIDDirection" = "Use Face ID to verify."; "UseFaceIDToUnlock" = "Use Face ID To Unlock"; @@ -350,7 +350,7 @@ "URIMatchDetection" = "Modd cymharu URIs"; "MatchDetection" = "Cymharu URIs"; "YesAndSave" = "Yes, and save"; -"AutofillAndSave" = "Auto-fill and save"; +"AutofillAndSave" = "Llenwi'n awtomatig a chadw"; "Organization" = "Sefydliad"; "HoldYubikeyNearTop" = "Hold your Yubikey near the top of the device."; "TryAgain" = "Ceisio eto"; @@ -423,7 +423,7 @@ "UnlockVault" = "Datgloi'ch cell"; "ThirtyMinutes" = "ar ôl hanner awr"; "SetPINDescription" = "Set your PIN code for unlocking Bitwarden. Your PIN settings will be reset if you ever fully log out of the application."; -"LoggedInAsOn" = "Logged in as %1$@ on %2$@."; +"LoggedInAsOn" = "Rydych wedi mewngofnodi fel %1$@ ar %2$@."; "VaultLockedMasterPassword" = "Mae eich cell dan glo. Gwiriwch eich prif gyfrinair i barhau."; "VaultLockedPIN" = "Mae eich cell dan glo. Gwiriwch eich cod PIN i barhau."; "VaultLockedIdentity" = "Mae eich cell dan glo. Gwiriwch eich hunaniaeth i barhau."; @@ -439,7 +439,7 @@ "DefaultUriMatchDetection" = "Default URI match detection"; "DefaultUriMatchDetectionDescription" = "Choose the default way that URI match detection is handled for logins when performing actions such as auto-fill."; "Theme" = "Thema"; -"ThemeDescription" = "Newid thema liwiau'r ap."; +"ThemeDescription" = "Newid thema liwiau'r ap"; "ThemeDefault" = "Rhagosodiad (system)"; "DefaultDarkTheme" = "Default dark theme"; "CopyNotes" = "Copïo'r nodyn"; @@ -450,7 +450,7 @@ "Nord" = "Nord"; "SolarizedDark" = "Solarized Dark"; "AutofillBlockedUris" = "Auto-fill blocked URIs"; -"AskToAddLogin" = "Ask to add login"; +"AskToAddLogin" = "Gofyn i ychwanegu manylion mewngofnodi"; "AskToAddLoginDescription" = "Ask to add an item if one isn't found in your vault."; "OnRestart" = "On app restart"; "AutofillServiceNotEnabled" = "Auto-fill makes it easy to securely access your Bitwarden vault from other websites and apps. It looks like you have not set up an auto-fill service for Bitwarden. Set up auto-fill for Bitwarden from the \"Settings\" screen."; @@ -480,7 +480,7 @@ "EncExportKeyWarning" = "This export encrypts your data using your account's encryption key. If you ever rotate your account's encryption key you should export again since you will not be able to decrypt this export file."; "EncExportAccountWarning" = "Account encryption keys are unique to each Bitwarden user account, so you can't import an encrypted export into a different account."; "ExportVaultConfirmationTitle" = "Cadarnhau allforio'r gell"; -"Warning" = "Warning"; +"Warning" = "Rhybudd"; "ExportVaultFailure" = "There was a problem exporting your vault. If the problem persists, you'll need to export from the web vault."; "ExportVaultSuccess" = "Vault exported successfully"; "Clone" = "Clone"; @@ -565,7 +565,7 @@ "MaximumAccessCountInfo" = "If set, users will no longer be able to access this Send once the maximum access count is reached."; "MaximumAccessCountReached" = "Max access count reached"; "CurrentAccessCount" = "Current access count"; -"NewPassword" = "New password"; +"NewPassword" = "Cyfrinair newydd"; "PasswordInfo" = "Optionally require a password for users to access this Send."; "RemovePassword" = "Remove password"; "AreYouSureRemoveSendPassword" = "Are you sure you want to remove the password?"; @@ -680,7 +680,7 @@ "EnvironmentPageUrlsError" = "One or more of the URLs entered are invalid. Please revise it and try to save again."; "GenericErrorMessage" = "We were unable to process your request. Please try again or contact us."; "AllowScreenCapture" = "Caniatáu sgrinluniau"; -"AreYouSureYouWantToEnableScreenCapture" = "Are you sure you want to turn on screen capture?"; +"AreYouSureYouWantToEnableScreenCapture" = "Ydych chi'n siŵr eich bod am ganiatáu sgrinluniau?"; "LogInRequested" = "Login requested"; "AreYouTryingToLogIn" = "Ydych chi'n ceisio mewngofnodi?"; "LogInAttemptByXOnY" = "Cais mewngofnodi gan %1$@ ar %2$@"; @@ -708,10 +708,10 @@ "CatchAllEmail" = "Catch-all email"; "ForwardedEmailAlias" = "Forwarded email alias"; "RandomWord" = "Gair ar hap"; -"EmailRequiredParenthesis" = "Email (required)"; +"EmailRequiredParenthesis" = "Ebost (gofynnol)"; "DomainNameRequiredParenthesis" = "Enw parth (gofynnol)"; -"APIKeyRequiredParenthesis" = "API key (required)"; -"Service" = "Service"; +"APIKeyRequiredParenthesis" = "Allwedd API (gofynnol)"; +"Service" = "Gwasanaeth"; "AddyIo" = "addy.io"; "FirefoxRelay" = "Firefox Relay"; "SimpleLogin" = "SimpleLogin"; @@ -757,7 +757,7 @@ "Language" = "Iaith"; "LanguageChangeXDescription" = "Cafodd yr iaith ei newid i %1$@. Ailgychwynnwch yr ap i weld newidiadau"; "LanguageChangeRequiresAppRestart" = "Bydd angen ailgychwyn yr ap"; -"DefaultSystem" = "Default (System)"; +"DefaultSystem" = "Rhagosodiad (system)"; "Important" = "Pwysig"; "YourMasterPasswordCannotBeRecoveredIfYouForgetItXCharactersMinimum" = "Your master password cannot be recovered if you forget it! %1$@ characters minimum."; "WeakMasterPassword" = "Prif gyfrinair gwan"; @@ -782,7 +782,7 @@ "Region" = "Rhanbarth"; "UpdateWeakMasterPasswordWarning" = "Your master password does not meet one or more of your organization policies. In order to access the vault, you must update your master password now. Proceeding will log you out of your current session, requiring you to log back in. Active sessions on other devices may continue to remain active for up to one hour."; "CurrentMasterPassword" = "Prif gyfrinair presennol"; -"LoggedIn" = "Logged in!"; +"LoggedIn" = "Wedi mewngofnodi!"; "ApproveWithMyOtherDevice" = "Approve with my other device"; "RequestAdminApproval" = "Request admin approval"; "ApproveWithMasterPassword" = "Approve with master password"; @@ -844,10 +844,10 @@ "UseInlineAutofillExplanationLong" = "Use inline autofill if your selected keyboard supports it. Otherwise, use the default overlay."; "AdditionalOptions" = "Dewisiadau ychwanegol"; "ContinueToWebApp" = "Parhau i'r ap gwe?"; -"ContinueToX" = "Continue to %1$@?"; -"ContinueToHelpCenter" = "Continue to Help center?"; +"ContinueToX" = "Parhau i %1$@?"; +"ContinueToHelpCenter" = "Parhau i'r ganolfan gymorth?"; "ContinueToContactSupport" = "Continue to contact support?"; -"ContinueToPrivacyPolicy" = "Continue to privacy policy?"; +"ContinueToPrivacyPolicy" = "Parhau i'r polisi preifatrwydd?"; "ContinueToAppStore" = "Continue to app store?"; "TwoStepLoginDescriptionLong" = "Gallwch wneud eich cyfrif yn fwy diogel drwy alluogi mewngofnodi dau gam yn ap gwe Bitwarden."; "ChangeMasterPasswordDescriptionLong" = "You can change your master password on the Bitwarden web app."; @@ -859,9 +859,16 @@ "LearnAboutOrganizationsDescriptionLong" = "Bitwarden allows you to share your vault items with others by using an organization. Learn more on the bitwarden.com website."; "RateAppDescriptionLong" = "Help others find out if Bitwarden is right for them. Visit the app store and leave a rating now."; "DefaultDarkThemeDescriptionLong" = "Choose the dark theme to use when your device’s dark mode is in use"; -"CreatedXY" = "Created %1$@, %2$@"; -"TooManyAttempts" = "Too many attempts"; +"CreatedXY" = "Crëwyd ar %1$@ am %2$@"; +"TooManyAttempts" = "Gormod o geisiadau"; "AccountLoggedOutBiometricExceeded" = "Account logged out."; "YourOrganizationPermissionsWereUpdatedRequeringYouToSetAMasterPassword" = "Your organization permissions were updated, requiring you to set a master password."; "YourOrganizationRequiresYouToSetAMasterPassword" = "Your organization requires you to set a master password."; -"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Set up an unlock option to change your vault timeout action."; \ No newline at end of file +"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Set up an unlock option to change your vault timeout action."; +"DuoTwoStepLoginIsRequiredForYourAccount" = "Duo two-step login is required for your account."; +"FollowTheStepsFromDuoToFinishLoggingIn" = "Follow the steps from Duo to finish logging in."; +"LaunchDuo" = "Lansio Duo"; +"OrganizationUnassignedItemsMessageUSEUDescriptionLong" = "Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."; +"OrganizationUnassignedItemsMessageSelfHost041624DescriptionLong" = "On May 16, 2024, unassigned organization items will no longer be visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."; +"RemindMeLater" = "Remind me later"; +"Notice" = "Notice"; \ No newline at end of file diff --git a/BitwardenShared/UI/Platform/Application/Support/Localizations/da.lproj/Localizable.strings b/BitwardenShared/UI/Platform/Application/Support/Localizations/da.lproj/Localizable.strings index 925366cd7..6ee5fc70c 100644 --- a/BitwardenShared/UI/Platform/Application/Support/Localizations/da.lproj/Localizable.strings +++ b/BitwardenShared/UI/Platform/Application/Support/Localizations/da.lproj/Localizable.strings @@ -236,7 +236,7 @@ "Camera" = "Kamera"; "Photos" = "Billeder"; "CopyTotp" = "Kopiér TOTP"; -"CopyTotpAutomaticallyDescription" = "Har et login en godkendelsesnøgle, så kopiér TOTP-bekræftelseskoden til udklipsholderen, når login auto-udfyldes."; +"CopyTotpAutomaticallyDescription" = "Har et login en godkendelsesnøgle, kopiér da TOTP-bekræftelseskoden til udklipsholderen, når login autoudfyldes."; "CopyTotpAutomatically" = "Kopiér TOTP automatisk"; "PremiumRequired" = "Premium-medlemskab kræves for at anvende denne funktion."; "AttachementAdded" = "Vedhæftning tilføjet"; @@ -620,7 +620,7 @@ "Fido2ReturnToApp" = "Vend tilbage til app"; "Fido2CheckBrowser" = "Sørg for, at standardbrowseren understøtter WebAuthn og forsøg igen."; "ResetPasswordAutoEnrollInviteWarning" = "Denne organisation har en virksomhedspolitik, der automatisk tilmelder dig til nulstilling af adgangskode. Tilmelding giver organisationsadministratorer mulighed for at skifte din hovedadgangskode."; -"VaultTimeoutPolicyInEffect" = "Dine organisationspolitikker påvirker din boks-timeout. Maksimum tilladte boks-timeout er %1$@ time(r) og %2$@ minut(ter)"; +"VaultTimeoutPolicyInEffect" = "Organisationspolitikkerne har sat den maksimalt tilladte bokstimeout til %1$@ tim(er) og %2$@ minut(ter)."; "VaultTimeoutPolicyWithActionInEffect" = "Organisationspolitikkerne påvirker boks-timeout. Maks. tilladt boks-timeout er %1$@ time(r) og %2$@ minut(ter). Boks-timeout er pt. sat til %3$@."; "VaultTimeoutActionPolicyInEffect" = "Organisationspolitikker har sat boks-timeouthandlingen til %1$@."; "VaultTimeoutToLarge" = "Timeout for din boks overskrider de begrænsninger, der er angivet af din organisation."; @@ -695,7 +695,7 @@ "LogInAccepted" = "Login bekræftet"; "LogInDenied" = "Login afvist"; "ApproveLoginRequests" = "Godkend loginanmodninger"; -"UseThisDeviceToApproveLoginRequestsMadeFromOtherDevices" = "Brug denne enhed til at godkende loginanmodninger fra andre enheder."; +"UseThisDeviceToApproveLoginRequestsMadeFromOtherDevices" = "Brug denne enhed til at godkende loginanmodninger fra andre enheder"; "AllowNotifications" = "Tillad notifikationer"; "ReceivePushNotificationsForNewLoginRequests" = "Modtag push-notifikationer om nye login-anmodninger"; "NoThanks" = "Nej tak"; @@ -797,7 +797,7 @@ "CopyApplication" = "Kopiér applikation"; "AvailableForTwoStepLogin" = "Tilgængelig for totrins-login"; "MasterPasswordRePromptHelp" = "Hjælp til genanmodning om hovedadgangskode"; -"UnlockingMayFailDueToInsufficientMemoryDecreaseYourKDFMemorySettingsToResolve" = "Unlocking may fail due to insufficient memory. Decrease your KDF memory settings or set up biometric unlock to resolve."; +"UnlockingMayFailDueToInsufficientMemoryDecreaseYourKDFMemorySettingsToResolve" = "Oplåsning kan fejle grundet utilstrækkelig hukommelse. Reducér KDF-hukommelsesindstillinger eller opsæt biometrisk oplåsning for at afhjælpe."; "InvalidAPIKey" = "Ugyldig API-nøgle"; "InvalidAPIToken" = "Ugyldigt API-token"; "AdminApprovalRequested" = "Admin-godkendelse udbedt"; @@ -864,4 +864,11 @@ "AccountLoggedOutBiometricExceeded" = "Konto logget ud."; "YourOrganizationPermissionsWereUpdatedRequeringYouToSetAMasterPassword" = "Organisationstilladelserne er blevet opdateret, og der kræves nu oprettelse af en hovedadgangskode."; "YourOrganizationRequiresYouToSetAMasterPassword" = "Organisationen kræver, at der oprettes en hovedadgangskode."; -"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Opsæt en oplåsningsmetode for at ændre Bokstimeouthandlingen."; \ No newline at end of file +"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Opsæt en oplåsningsmetode for at ændre Bokstimeouthandlingen."; +"DuoTwoStepLoginIsRequiredForYourAccount" = "Duo-totrinsindlogning kræves for kontoen."; +"FollowTheStepsFromDuoToFinishLoggingIn" = "Følg trinnene fra Duo for at færdiggøre indlogningen."; +"LaunchDuo" = "Start Duo"; +"OrganizationUnassignedItemsMessageUSEUDescriptionLong" = "Utildelte organisationsemner er ikke længere synlige i Alle Bokse-visningen og er kun tilgængelige via Admin-konsollen. Føj disse emner til en samling fra Admin-konsollen for at gøre dem synlige."; +"OrganizationUnassignedItemsMessageSelfHost041624DescriptionLong" = "Pr. 16. maj 2024 vil utildelte organisationsemner ikke længere være synlige i Alle Bokse-visningen og vil kun være tilgængelige via Admin-konsollen. Tildel disse emner til en samling via Admin-konsollen for at gøre dem synlige."; +"RemindMeLater" = "Påmind senere"; +"Notice" = "Varsling"; \ No newline at end of file diff --git a/BitwardenShared/UI/Platform/Application/Support/Localizations/de.lproj/Localizable.strings b/BitwardenShared/UI/Platform/Application/Support/Localizations/de.lproj/Localizable.strings index 1ebad23fb..54f4ec27d 100644 --- a/BitwardenShared/UI/Platform/Application/Support/Localizations/de.lproj/Localizable.strings +++ b/BitwardenShared/UI/Platform/Application/Support/Localizations/de.lproj/Localizable.strings @@ -864,4 +864,11 @@ "AccountLoggedOutBiometricExceeded" = "Konto abgemeldet."; "YourOrganizationPermissionsWereUpdatedRequeringYouToSetAMasterPassword" = "Deine Organisationsberechtigungen wurden aktualisiert und verlangen, dass du ein Master-Passwort festlegen musst."; "YourOrganizationRequiresYouToSetAMasterPassword" = "Deine Organisation verlangt, dass du ein Master-Passwort festlegen musst."; -"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Richte eine Entsperroption ein, um deine Aktion bei Tresor-Timeout zu ändern."; \ No newline at end of file +"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Richte eine Entsperroption ein, um deine Aktion bei Tresor-Timeout zu ändern."; +"DuoTwoStepLoginIsRequiredForYourAccount" = "Für dein Konto ist die Duo Zwei-Faktor-Authentifizierung erforderlich."; +"FollowTheStepsFromDuoToFinishLoggingIn" = "Folge den Schritten von Duo, um die Anmeldung abzuschließen."; +"LaunchDuo" = "Duo starten"; +"OrganizationUnassignedItemsMessageUSEUDescriptionLong" = "Hinweis: Nicht zugeordnete Organisationseinträge sind nicht mehr in der Ansicht aller Tresore sichtbar und nur über die Administrator-Konsole zugänglich. Weise diese Einträge einer Sammlung aus der Administrator-Konsole zu, um sie sichtbar zu machen."; +"OrganizationUnassignedItemsMessageSelfHost041624DescriptionLong" = "Ab dem 16. Mai 2024 werden nicht zugeordnete Organisationseinträge nicht mehr in der Ansicht aller Tresore sichtbar und nur über die Administrator-Konsole zugänglich sein. Weise diese Einträge einer Sammlung aus der Administrator-Konsole zu, um sie sichtbar zu machen."; +"RemindMeLater" = "Später erinnern"; +"Notice" = "Hinweis"; \ No newline at end of file diff --git a/BitwardenShared/UI/Platform/Application/Support/Localizations/el.lproj/Localizable.strings b/BitwardenShared/UI/Platform/Application/Support/Localizations/el.lproj/Localizable.strings index 2ed106495..f22d5e3c8 100644 --- a/BitwardenShared/UI/Platform/Application/Support/Localizations/el.lproj/Localizable.strings +++ b/BitwardenShared/UI/Platform/Application/Support/Localizations/el.lproj/Localizable.strings @@ -7,21 +7,21 @@ "Bitwarden" = "Bitwarden"; "Cancel" = "Ακύρωση"; "Copy" = "Αντιγραφή"; -"CopyPassword" = "Αντιγραφή Κωδικού"; -"CopyUsername" = "Αντιγραφή Ονόματος Χρήστη"; +"CopyPassword" = "Αντιγραφή κωδικού"; +"CopyUsername" = "Αντιγραφή ονόματος χρήστη"; "Credits" = "Συντελεστές"; "Delete" = "Διαγραφή"; "Deleting" = "Διαγραφή..."; "DoYouReallyWantToDelete" = "Είστε σίγουροι για τη διαγραφή; Αυτό δεν μπορεί να αναιρεθεί."; "Edit" = "Επεξεργασία"; -"EditFolder" = "Επεξεργασία Φακέλου"; +"EditFolder" = "Επεξεργασία φακέλου"; "Email" = "Email"; -"EmailAddress" = "Διεύθυνση Email"; +"EmailAddress" = "Διεύθυνση email"; "EmailUs" = "Στείλτε μας email"; "EmailUsDescription" = "Στείλτε μας email για να λάβετε βοήθεια ή να αφήσετε σχόλια."; "EnterPIN" = "Εισαγωγή κωδικού ΡΙΝ."; "Favorites" = "Αγαπημένα"; -"FileBugReport" = "Υποβολή Αναφοράς Σφάλματος"; +"FileBugReport" = "Υποβολή αναφοράς σφάλματος"; "FileBugReportDescription" = "Αναφέρετε το προβλήμα στο GitHub."; "FingerprintDirection" = "Χρήση δακτυλικού αποτυπώματος για επαλήθευση."; "Folder" = "Φάκελος"; @@ -29,13 +29,13 @@ "FolderDeleted" = "Ο φάκελος διαγράφηκε"; "FolderNone" = "Δεν υπάρχει φάκελος"; "Folders" = "Φάκελοι"; -"FolderUpdated" = "Ο φάκελος ενημερώθηκε."; +"FolderUpdated" = "Ο φάκελος ενημερώθηκε"; "GoToWebsite" = "Μετάβαση στην ιστοσελίδα"; -"HelpAndFeedback" = "Βοήθεια και Σχόλια"; +"HelpAndFeedback" = "Βοήθεια και σχόλια"; "Hide" = "Απόκρυψη"; "InternetConnectionRequiredMessage" = "Συνδεθείτε στο διαδίκτυο πριν συνεχίσετε."; "InternetConnectionRequiredTitle" = "Απαιτείται σύνδεση στο διαδίκτυο"; -"InvalidMasterPassword" = "Μη έγκυρος κύριος κωδικός, δοκιμάστε ξανά."; +"InvalidMasterPassword" = "Μη έγκυρος κύριος κωδικός. Δοκιμάστε ξανά."; "InvalidPIN" = "Μη έγκυρο PIN. Προσπάθηστε ξανά."; "Launch" = "Εκκίνηση"; "LogIn" = "Σύνδεση"; @@ -44,12 +44,12 @@ "LogoutConfirmation" = "Είστε σίγουροι ότι θέλετε να αποσυνδεθείτε;"; "RemoveAccount" = "Αφαίρεση λογαριασμού"; "RemoveAccountConfirmation" = "Είστε βέβαιοι ότι θέλετε να καταργήσετε αυτόν τον λογαριασμό?"; -"AccountAlreadyAdded" = "Ο Λογαριασμός Προστέθηκε Ήδη"; +"AccountAlreadyAdded" = "Ο λογαριασμός έχει ήδη προστεθεί"; "SwitchToAlreadyAddedAccountConfirmation" = "Θα θέλατε να το αλλάξετε τώρα?"; "MasterPassword" = "Κύριος κωδικός"; "More" = "Περισσότερα"; -"MyVault" = "Το Vault μου"; -"Authenticator" = "Εφαρμογή Επαλήθευσης"; +"MyVault" = "Το vault μου"; +"Authenticator" = "Αυθεντικοποιητής"; "Name" = "Όνομα"; "No" = "Όχι"; "Notes" = "Σημειώσεις"; @@ -60,7 +60,7 @@ "Saving" = "Αποθήκευση..."; "Settings" = "Ρυθμίσεις"; "Show" = "Εμφάνιση"; -"ItemDeleted" = "Το στοιχείο έχει διαγραφεί."; +"ItemDeleted" = "Το στοιχείο διαγράφτηκε"; "Submit" = "Υποβολή"; "Sync" = "Συγχρονισμός"; "ThankYou" = "Ευχαριστούμε"; @@ -69,9 +69,9 @@ "UseFingerprintToUnlock" = "Χρήση δακτυλικού αποτυπώματος για ξεκλείδωμα"; "Username" = "Όνομα χρήστη"; "ValidationFieldRequired" = "Το πεδίο %1$@ είναι υποχρεωτικό."; -"ValueHasBeenCopied" = "%1$@ έχει αντιγραφεί."; -"VerifyFingerprint" = "Επαλήθευση Δακτυλικού Αποτυπώματος"; -"VerifyMasterPassword" = "Επαλήθευση Κύριου Κωδικού"; +"ValueHasBeenCopied" = "%1$@ έχει αντιγραφεί"; +"VerifyFingerprint" = "Επαλήθευση δακτυλικού αποτυπώματος"; +"VerifyMasterPassword" = "Επαλήθευση κύριου κωδικού"; "VerifyPIN" = "Επαλήθευση PIN"; "Version" = "Έκδοση"; "View" = "Προβολή"; @@ -81,28 +81,28 @@ "Account" = "Λογαριασμός"; "AccountCreated" = "Ο λογαριασμός σας έχει δημιουργηθεί! Τώρα μπορείτε να συνδεθείτε."; "AddAnItem" = "Προσθήκη στοιχείου"; -"AppExtension" = "Επέκταση Εφαρμογής"; +"AppExtension" = "Επέκταση εφαρμογής"; "AutofillAccessibilityDescription" = "Χρησιμοποιείστε την υπηρεσία προσβασιμότητας Bitwarden, για την αυτόματη συμπλήρωση συνδέσεων σε εφαρμογές και στο διαδίκτυο."; -"AutofillService" = "Υπηρεσία Αυτόματης Συμπλήρωσης"; -"AvoidAmbiguousCharacters" = "Αποφυγή Αμφιλεγόμενων Χαρακτήρων"; -"BitwardenAppExtension" = "Επέκταση Εφαρμογής Bitwarden"; -"BitwardenAppExtensionAlert2" = "Ο ευκολότερος τρόπος για να προσθέστε νέες συνδέσεις στο vault σας, είναι μέσω της επέκτασης εφαρμογής Bitwarden. Μάθετε περισσότερα σχετικά με τη χρήση της επέκτασης, μεταβαίνοντας στις \"Ρυθμίσεις\"."; +"AutofillService" = "Υπηρεσία αυτόματης συμπλήρωσης"; +"AvoidAmbiguousCharacters" = "Αποφυγή αμφιλεγόμενων χαρακτήρων"; +"BitwardenAppExtension" = "Επέκταση εφαρμογής Bitwarden"; +"BitwardenAppExtensionAlert2" = "Ο ευκολότερος τρόπος για να προσθέστε νέες συνδέσεις στο vault σας, είναι μέσω της επέκτασης εφαρμογής Bitwarden. Μάθετε περισσότερα σχετικά με τη χρήση της επέκτασης εφαρμογής Bitwarden, μεταβαίνοντας στις \"Ρυθμίσεις\"."; "BitwardenAppExtensionDescription" = "Χρήση του Bitwarden στο Safari και σε άλλες εφαρμογές για αυτόματη συμπλήρωση των συνδέσεων σας."; "BitwardenAutofillService" = "Υπηρεσία Αυτόματης Συμπλήρωσης Bitwarden"; "BitwardenAutofillAccessibilityServiceDescription" = "Χρησιμοποιείστε την υπηρεσία προσβασιμότητας Bitwarden, για την αυτόματη συμπλήρωση συνδέσεων."; -"ChangeEmail" = "Αλλαγή Email"; +"ChangeEmail" = "Αλλαγή email"; "ChangeEmailConfirmation" = "Μπορείτε να αλλάξετε τη διεύθυνση του email σας στο bitwarden.com. Θέλετε να επισκεφθείτε την ιστοσελίδα τώρα;"; -"ChangeMasterPassword" = "Αλλαγή Κύριου Κωδικού"; +"ChangeMasterPassword" = "Αλλαγή κύριου κωδικού"; "Close" = "Κλείσιμο"; "Continue" = "Συνέχεια"; -"CreateAccount" = "Δημιουργία Λογαριασμού"; +"CreateAccount" = "Δημιουργία λογαριασμού"; "CreatingAccount" = "Δημιουργία λογαριασμού..."; -"EditItem" = "Επεξεργασία Στοιχείου"; -"EnableAutomaticSyncing" = "Ενεργοποίηση Αυτόματου Συγχρονισμού"; +"EditItem" = "Επεξεργασία στοιχείου"; +"EnableAutomaticSyncing" = "Επιτρέψτε τον αυτόματο συγχρονισμό"; "EnterEmailForHint" = "Εισάγετε την διεύθυνση email του λογαριασμού σας, προκειμένου για να λάβετε υπόδειξη του κύριου κωδικού."; -"ExntesionReenable" = "Ενεργοποιείστε εκ νέου την Επέκτασης Εφαρμογής"; +"ExntesionReenable" = "Εκ νέου ενεργοποίηση επέκτασης εφαρμογής"; "ExtensionAlmostDone" = "Σχεδόν ολοκληρώθηκε!"; -"ExtensionEnable" = "Ενεργοποίηση Επέκτασης Εφαρμογής"; +"ExtensionEnable" = "Ενεργοποίηση επέκτασης εφαρμογής"; "ExtensionInSafari" = "Στο Safari, βρείτε το Bitwarden χρησιμοποιώντας το εικονίδιο κοινής χρήσης (μετακινηθείτε προς τα δεξιά στην κάτω σειρά του μενού)."; "ExtensionInstantAccess" = "Αποκτήστε άμεση πρόσβαση στους κωδικούς σας!"; "ExtensionReady" = "Είστε έτοιμοι να συνδεθείτε!"; @@ -112,12 +112,12 @@ "ExtensionTurnOn" = "Για να ενεργοποιήσετε το Bitwarden στο Safari και σε άλλες εφαρμογές, πατήστε στο εικονίδιο \"Περισσότερα\" στην κάτω σειρά του μενού."; "Favorite" = "Αγαπημένο"; "Fingerprint" = "Δακτυλικό αποτύπωμα"; -"GeneratePassword" = "Δημιουργία Κωδικού"; +"GeneratePassword" = "Παραγωγή κωδικού"; "GetPasswordHint" = "Λήψη υπόδειξης κύριου κωδικού"; -"ImportItems" = "Εισαγωγή Στοιχείων"; +"ImportItems" = "Εισαγωγή στοιχείων"; "ImportItemsConfirmation" = "Μπορείτε να εισαγάγετε μαζικά στοιχεία διαδικτυακά στο bitwarden.com. Θέλετε να επισκεφθείτε την ιστοσελίδα τώρα;"; "ImportItemsDescription" = "Γρήγορη μαζική εισαγωγή των στοιχείων σας από άλλες εφαρμογές διαχείρισης κωδικών."; -"LastSync" = "Τελευταίος Συγχρονισμός:"; +"LastSync" = "Τελευταίος συγχρονισμός:"; "Length" = "Μήκος"; "Lock" = "Κλείδωμα"; "FifteenMinutes" = "15 λεπτά"; @@ -125,23 +125,23 @@ "OneMinute" = "1 λεπτό"; "FourHours" = "4 ώρες"; "Immediately" = "Άμεσα"; -"VaultTimeout" = "Χρόνος Λήξης Vault"; -"VaultTimeoutAction" = "Ενέργεια Χρόνου Λήξης Vault"; +"VaultTimeout" = "Χρόνος λήξης vault"; +"VaultTimeoutAction" = "Ενέργεια χρόνου λήξης vault"; "VaultTimeoutLogOutConfirmation" = "Η αποσύνδεση θα καταργήσει όλη την πρόσβαση στο vault σας και απαιτεί online έλεγχο ταυτότητας μετά το χρονικό όριο λήξης. Είστε βέβαιοι ότι θέλετε να χρησιμοποιήσετε αυτήν τη ρύθμιση;"; "LoggingIn" = "Σύνδεση..."; "LoginOrCreateNewAccount" = "Συνδεθείτε ή δημιουργήστε έναν νέο λογαριασμό για να αποκτήσετε πρόσβαση στο ασφαλές vault σας."; "Manage" = "Διαχείριση"; "MasterPasswordConfirmationValMessage" = "Η επιβεβαίωση του κύριου κωδικού δεν είναι σωστή."; "MasterPasswordDescription" = "Ο κύριος κωδικός χρησιμοποιείται για πρόσβαση στο vault σας. Είναι πολύ σημαντικό να μην τον ξεχάσετε. Δεν υπάρχει τρόπος να τον ανακτήσετε σε αυτή την περίπτωση."; -"MasterPasswordHint" = "Υπόδειξη Κύριου Κωδικού (προαιρετικό)"; +"MasterPasswordHint" = "Υπόδειξη κύριου κωδικού (προαιρετικό)"; "MasterPasswordHintDescription" = "Η υπόδειξη κύριου κωδικού μπορεί να σας βοηθήσει να θυμηθείτε τον κωδικό σας αν τον ξεχάσετε."; "MasterPasswordLengthValMessageX" = "Ο κύριος κωδικός πρέπει να έχει μήκος τουλάχιστον %1$@ χαρακτήρες."; -"MinNumbers" = "Ελάχιστα Αριθμητικά Ψηφία"; -"MinSpecial" = "Ελάχιστο Ειδικών Χαρακτήρων"; -"MoreSettings" = "Περισσότερες Ρυθμίσεις"; +"MinNumbers" = "Ελάχιστα αριθμητικά ψηφία"; +"MinSpecial" = "Ελάχιστοι ειδικοί χαρακτήρες"; +"MoreSettings" = "Περισσότερες ρυθμίσεις"; "MustLogInMainApp" = "Πρέπει να συνδεθείτε στην κύρια εφαρμογή Bitwarden για να μπορέσετε να χρησιμοποιήσετε την επέκταση."; "Never" = "Ποτέ"; -"NewItemCreated" = "Δημιουργήθηκε νέο στοιχείο."; +"NewItemCreated" = "Το στοιχείο προστέθηκε"; "NoFavorites" = "Δεν υπάρχουν αγαπημένα στο vault σας."; "NoItems" = "Δεν υπάρχουν στοιχεία στο vault σας."; "NoItemsTap" = "Δεν υπάρχουν στοιχεία στο vault σας για αυτήν την ιστοσελίδα/εφαρμογή. Πατήστε για προσθήκη."; @@ -150,39 +150,39 @@ "OptionDefaults" = "Οι προεπιλεγμένες επιλογές έχουν οριστεί από το κύριο εργαλείο δημιουργίας κωδικού του Bitwarden."; "Options" = "Επιλογές"; "Other" = "Άλλες"; -"PasswordGenerated" = "Ο κωδικός δημιουργήθηκε."; -"PasswordGenerator" = "Γεννήτρια Κωδικού"; -"PasswordHint" = "Υπόδειξη Κωδικού"; +"PasswordGenerated" = "Ο κωδικός δημιουργήθηκε"; +"PasswordGenerator" = "Γεννήτρια κωδικού"; +"PasswordHint" = "Υπόδειξη κωδικού"; "PasswordHintAlert" = "Σας στείλαμε ένα email με υπόδειξη του κύριου κωδικού."; "PasswordOverrideAlert" = "Είστε βέβαιοι ότι θέλετε να αντικαταστήσετε τον τρέχον κωδικό;"; -"PushNotificationAlert" = "Το Bitwarden κρατάει αυτόματα το vault σας, με τη χρήση ειδοποιήσεων push. Για την καλύτερη δυνατή εμπειρία, επιλέξτε \"Να επιτρέπεται\" στην παρακάτω ερώτηση, όταν σας ζητηθεί να ενεργοποιήσετε τις ειδοποιήσεις push."; -"RateTheApp" = "Αξιολογήστε την Εφαρμογή"; +"PushNotificationAlert" = "Το Bitwarden κρατάει συγχρονισμένο το vault σας αυτόματα, με τη χρήση ειδοποιήσεων push. Για την καλύτερη δυνατή εμπειρία, επιλέξτε \"Να επιτρέπεται\" στην παρακάτω προτροπή όταν σας ζητηθεί να ενεργοποιήσετε τις ειδοποιήσεις push."; +"RateTheApp" = "Αξιολογήστε την εφαρμογή"; "RateTheAppDescription" = "Παρακαλούμε σκεφτείτε να μας βοηθήσετε με μια καλή κριτική!"; -"RegeneratePassword" = "Επαναδημιουργία Κωδικού"; -"RetypeMasterPassword" = "Εισάγετε Ξανά τον Κύριο Κωδικό"; +"RegeneratePassword" = "Επαναδημιουργία κωδικού"; +"RetypeMasterPassword" = "Εισάγετε ξανά τον κύριο κωδικό"; "SearchVault" = "Αναζήτηση στο vault"; "Security" = "Ασφάλεια"; "Select" = "Επιλογή"; "SetPIN" = "Ορισμός PIN"; "SetPINDirection" = "Εισαγωγή τετραψήφιου αριθμού PIN για ξεκλείδωμα εφαρμογής."; -"ItemInformation" = "Πληροφορίες Στοιχείου"; -"ItemUpdated" = "Το στοιχείο ενημερώθηκε."; +"ItemInformation" = "Πληροφορίες στοιχείου"; +"ItemUpdated" = "Το στοιχείο αποθηκεύτηκε"; "Submitting" = "Υποβολή..."; "Syncing" = "Συγχρονισμός..."; -"SyncingComplete" = "Ο συγχρονισμός ολοκληρώθηκε."; -"SyncingFailed" = "Ο συγχρονισμός απέτυχε."; -"SyncVaultNow" = "Συγχρονισμός Vault Τώρα"; +"SyncingComplete" = "Ο συγχρονισμός ολοκληρώθηκε"; +"SyncingFailed" = "Ο συγχρονισμός απέτυχε"; +"SyncVaultNow" = "Συγχρονισμός του vault τώρα"; "TouchID" = "Touch ID"; -"TwoStepLogin" = "Σύνδεση σε δύο βήματα"; +"TwoStepLogin" = "Σύνδεση δύο βημάτων"; "UnlockWith" = "Ξεκλείδωμα με %1$@"; -"UnlockWithPIN" = "Ξεκλείδωμα με PIN"; +"UnlockWithPIN" = "Ξεκλείδωμα με κωδικό PIN"; "Validating" = "Επαλήθευση"; -"VerificationCode" = "Κωδικός Επαλήθευσης"; -"ViewItem" = "Προβολή Στοιχείου"; -"WebVault" = "Bitwarden Web Vault"; +"VerificationCode" = "Κωδικός επαλήθευσης"; +"ViewItem" = "Προβολή στοιχείου"; +"WebVault" = "Διαδικτυακό vault Bitwarden"; "Lost2FAApp" = "Χάσατε την εφαρμογή επαλήθευσης;"; "Items" = "Στοιχεία"; -"ExtensionActivated" = "Η Επέκταση Ενεργοποιήθηκε!"; +"ExtensionActivated" = "Η επέκταση ενεργοποιήθηκε!"; "Icons" = "Εικονίδια"; "Translations" = "Μεταφράσεις"; "ItemsForUri" = "Στοιχεία για %1$@"; @@ -205,43 +205,43 @@ "PossibleMatchingItems" = "Πιθανά Στοιχεία Αντιστοίχισης"; "Search" = "Αναζήτηση"; "BitwardenAutofillServiceSearch" = "Αναζήτηση στοιχείου αυτόματης συμπλήρωσης για \"%1$@\"."; -"LearnOrg" = "Μάθετε για τους Οργανισμούς"; +"LearnOrg" = "Μάθετε για τους οργανισμούς"; "CannotOpenApp" = "Δεν είναι δυνατή η πρόσβαση στην εφαρμογή \"%1$@\"."; -"AuthenticatorAppTitle" = "Εφαρμογή Επαλήθευσης"; +"AuthenticatorAppTitle" = "Εφαρμογή αυθεντικοποίησης"; "EnterVerificationCodeApp" = "Εισάγετε τον 6ψήφιο κωδικό από την εφαρμογή επαλήθευσης."; "EnterVerificationCodeEmail" = "Εισαγωγή 6ψήφιου κωδικού επαλήθευσης που σταλθηκε με email στο %1$@."; -"LoginUnavailable" = "Μη Διαθέσιμη Σύνδεση"; -"NoTwoStepAvailable" = "Αυτός ο λογαριασμός έχει ενεργοποιημένη τη σύνδεση σε δύο βήματα, ωστόσο, σε αυτήν τη συσκευή δεν υποστηρίζεται κανένας από τους διαμορφωμένους παροχείς δύο βημάτων. Χρησιμοποιήστε μια υποστηριζόμενη συσκευή και / ή προσθέστε επιπλέον παρόχους που υποστηρίζονται καλύτερα σε όλες τις συσκευές (όπως μια εφαρμογή επαλήθευσης)."; -"RecoveryCodeTitle" = "Κωδικός Ανάκτησης"; +"LoginUnavailable" = "Μη διαθέσιμη σύνδεση"; +"NoTwoStepAvailable" = "Αυτός ο λογαριασμός έχει ενεργοποιημένη τη σύνδεση δύο βημάτων, ωστόσο, σε αυτήν τη συσκευή δεν υποστηρίζεται κανένας από τους διαμορφωμένους παρόχους δύο βημάτων. Παρακαλώ χρησιμοποιήστε μια υποστηριζόμενη συσκευή και/ή προσθέστε επιπλέον παρόχους που υποστηρίζονται καλύτερα σε όλες τις συσκευές (όπως μια εφαρμογή αυθεντικοποίησης)."; +"RecoveryCodeTitle" = "Κωδικός ανάκτησης"; "RememberMe" = "Να με θυμάσαι"; "SendVerificationCodeAgain" = "Αποστολή email κωδικού επαλήθευσης ξανά"; -"TwoStepLoginOptions" = "Επιλογές σύνδεσης δύο παραγόντων"; +"TwoStepLoginOptions" = "Επιλογές σύνδεσης δύο βημάτων"; "UseAnotherTwoStepMethod" = "Χρήση άλλης μεθόδου δύο παραγόντων"; "VerificationEmailNotSent" = "Δεν ήταν δυνατή η αποστολή του email επαλήθευσης. Προσπάθησε ξανά."; -"VerificationEmailSent" = "Το email επιβεβαίωσης στάλθηκε."; +"VerificationEmailSent" = "Το email επιβεβαίωσης στάλθηκε"; "YubiKeyInstruction" = "Για να συνεχίσετε, κρατήστε το YubiKey NEO στο πίσω μέρος της συσκευής ή τοποθετήστε το YubiKey στη θύρα USB της συσκευής σας, και στη συνέχεια πατήστε το κουμπί του."; -"YubiKeyTitle" = "Κλειδί Ασφαλείας YubiKey"; -"AddNewAttachment" = "Προσθήκη Νέου Συνημμένου"; +"YubiKeyTitle" = "Κλειδί ασφαλείας YubiKey"; +"AddNewAttachment" = "Προσθήκη συνημμένου"; "Attachments" = "Συνημμένα"; "UnableToDownloadFile" = "Αδυναμία λήψης αρχείου."; "UnableToOpenFile" = "Η συσκευή σας δεν μπορεί να ανοίξει αυτού του τύπου αρχεία."; "Downloading" = "Λήψη..."; "AttachmentLargeWarning" = "Αυτό το συνημμένο έχει μέγεθος %1$@. Είστε βέβαιοι ότι θέλετε να το κατεβάσετε στη συσκευή σας;"; -"AuthenticatorKey" = "Κλειδί επαλήθευσης (TOTP)"; -"VerificationCodeTotp" = "Κωδικός Επαλήθευσης (TOTP)"; +"AuthenticatorKey" = "Κλειδί αυθεντικοποίησης (TOTP)"; +"VerificationCodeTotp" = "Κωδικός επαλήθευσης (TOTP)"; "AuthenticatorKeyAdded" = "Το κλειδί επαλήθευσης προστέθηκε."; "AuthenticatorKeyReadError" = "Αδυναμία ανάγνωσης κλειδιού επαλήθευσης."; -"PointYourCameraAtTheQRCode" = "Σημαδέψτε τον κωδικό QR με τη κάμερα. Η σάρωση θα γίνει αυτόματα."; +"PointYourCameraAtTheQRCode" = "Σημαδέψτε με την κάμερα σας τον κωδικό QR. \nΗ σάρωση θα γίνει αυτόματα."; "ScanQrTitle" = "Σάρωση κώδικα QR"; "Camera" = "Κάμερα"; "Photos" = "Φωτογραφίες"; "CopyTotp" = "Αντιγραφή TOTP"; -"CopyTotpAutomaticallyDescription" = "Εάν η σύνδεση έχει κάποιο κλειδί επαλήθευσης, αντιγράψτε τον κωδικό επαλήθευσης TOTP στο πρόχειρο κάθε φορά που κάνετε αυτόματη συμπλήρωση τα στοιχεία σύνδεσης."; +"CopyTotpAutomaticallyDescription" = "Εάν η σύνδεση έχει κάποιο κλειδί αυθεντικοποίησης, αντιγράψτε τον κωδικό επαλήθευσης TOTP στο πρόχειρο σας όταν κάνετε με αυτόματη συμπλήρωση τη συνδεση."; "CopyTotpAutomatically" = "Αυτόματη αντιγραφή TOTP"; "PremiumRequired" = "Για να χρησιμοποιήσετε αυτό το χαρακτηριστικό, απαιτείται η έκδοση premium."; "AttachementAdded" = "Το συνημμένο προστέθηκε"; "AttachmentDeleted" = "Το συνημμένο διαγράφηκε"; -"ChooseFile" = "Επιλογή Αρχείου"; +"ChooseFile" = "Επιλογή αρχείου"; "File" = "Αρχείο"; "NoFileChosen" = "Δεν επιλέχθηκε κανένα αρχείο"; "NoAttachments" = "Δεν υπάρχουν συνημμένα."; @@ -249,28 +249,28 @@ "FeatureUnavailable" = "Μη διαθέσιμο χαρακτηριστικό"; "MaxFileSize" = "Το μέγιστο μέγεθος αρχείου είναι 100 MB."; "UpdateKey" = "Δεν μπορείτε να χρησιμοποιήσετε αυτήν τη δυνατότητα μέχρι να ενημερώσετε το κλειδί κρυπτογράφησης."; -"EncryptionKeyMigrationRequiredDescriptionLong" = "Απαιτείται μεταφορά του κλειδιού κρυπτογράφησης. Παρακαλούμε συνδεθείτε μέσω του web vault για να ενημερώσετε το κλειδί κρυπτογράφησης."; -"LearnMore" = "Μάθετε Περισσότερα"; -"ApiUrl" = "URL Διακομιστή API"; -"CustomEnvironment" = "Προσαρμοσμένο Περιβάλλον"; +"EncryptionKeyMigrationRequiredDescriptionLong" = "Απαιτείται μεταφορά του κλειδιού κρυπτογράφησης. Παρακαλούμε συνδεθείτε μέσω του διαδικτυακού vault για να ενημερώσετε το κλειδί κρυπτογράφησης."; +"LearnMore" = "Μάθετε περισσότερα"; +"ApiUrl" = "URL διακομιστή API"; +"CustomEnvironment" = "Προσαρμοσμένο περιβάλλον"; "CustomEnvironmentFooter" = "Για προχωρημένους χρήστες. Μπορείτε να ορίσετε τo URL κάθε υπηρεσίας ανεξάρτητα."; "EnvironmentSaved" = "Τα URL έχουν αποθηκευτεί."; "FormattedIncorrectly" = "%1$@ δεν έχει μορφοποιηθεί σωστά."; -"IdentityUrl" = "URL Ταυτότητας Διακομιστή"; +"IdentityUrl" = "URL διακομιστή ταυτότητας"; "SelfHostedEnvironment" = "Αυτο-Φιλοξενούμενο Περιβάλλον"; "SelfHostedEnvironmentFooter" = "Καθορίστε το URL εγκατάστασης Bitwarden."; "ServerUrl" = "URL Διακομιστή"; -"WebVaultUrl" = "URL Διακομιστή Web Vault"; +"WebVaultUrl" = "URL διακομιστή διαδικτυακού vault"; "BitwardenAutofillServiceNotificationContentOld" = "Πατήστε στην ειδοποίηση για να συμπληρώσετε αυτόματα ένα στοιχείο από τη λίστα σας."; -"CustomFields" = "Προσαρμοσμένα Πεδία"; -"CopyNumber" = "Αντιγραφή Αριθμού"; -"CopySecurityCode" = "Αντιγραφή Κωδικού Ασφαλείας"; +"CustomFields" = "Προσαρμοσμένα πεδία"; +"CopyNumber" = "Αντιγραφή αριθμού"; +"CopySecurityCode" = "Αντιγραφή κωδικού ασφαλείας"; "Number" = "Αριθμός"; -"SecurityCode" = "Κωδικός Ασφαλείας"; +"SecurityCode" = "Κωδικός ασφαλείας"; "TypeCard" = "Κάρτα"; "TypeIdentity" = "Ταυτότητα"; "TypeLogin" = "Σύνδεση"; -"TypeSecureNote" = "Ασφαλής Σημείωση"; +"TypeSecureNote" = "Ασφαλής σημείωση"; "Address1" = "Διεύθυνση 1"; "Address2" = "Διεύθυνση 2"; "Address3" = "Διεύθυνση 3"; @@ -283,8 +283,8 @@ "Country" = "Χώρα"; "December" = "Δεκέμβριος"; "Dr" = "Dr"; -"ExpirationMonth" = "Μήνας Λήξης"; -"ExpirationYear" = "Έτος Λήξης"; +"ExpirationMonth" = "Μήνας λήξης"; +"ExpirationYear" = "Έτος λήξης"; "February" = "Φεβρουάριος"; "FirstName" = "Όνομα"; "January" = "Ιανουάριος"; @@ -292,28 +292,28 @@ "June" = "Ιούνιος"; "LastName" = "Επίθετο"; "FullName" = "Ονοματεπώνυμο"; -"LicenseNumber" = "Αριθμός Άδειας"; +"LicenseNumber" = "Αριθμός άδειας"; "March" = "Μάρτιος"; "May" = "Μάιος"; -"MiddleName" = "Μεσαίο Όνομα"; +"MiddleName" = "Μεσαίο όνομα"; "Mr" = "Κος"; "Mrs" = "Κα"; "Ms" = "Κα"; "Mx" = "Mx στα Ελληνικά"; "November" = "Νοέμβριος"; "October" = "Οκτώβριος"; -"PassportNumber" = "Αριθμός Διαβατηρίου"; +"PassportNumber" = "Αριθμός διαβατηρίου"; "Phone" = "Τηλέφωνο"; "September" = "Σεπτέμβριος"; "SSN" = "ΑΜΚΑ"; "StateProvince" = "Περιοχή / Νομός"; "Title" = "Τίτλος"; -"ZipPostalCode" = "Ταχυδρομικός Κώδικας"; +"ZipPostalCode" = "Ταχυδρομικός κώδικας"; "Address" = "Διεύθυνση"; "Expiration" = "Λήξη"; "ShowWebsiteIcons" = "Εμφάνιση εικονιδίων ιστοσελίδας"; "ShowWebsiteIconsDescription" = "Εμφάνιση μιας αναγνωρίσιμης εικόνας δίπλα σε κάθε σύνδεση."; -"IconsUrl" = "Εικονίδια Διακομιστή URL"; +"IconsUrl" = "URL διακομιστή εικονιδίων"; "AutofillWithBitwarden" = "Αυτόματη συμπλήρωση με Bitwarden"; "VaultIsLocked" = "Το Vault είναι κλειδωμένο"; "GoToMyVault" = "Μετάβαση στο vault μου"; @@ -331,12 +331,12 @@ "VerifyFaceID" = "Επαλήθευση Face ID"; "WindowsHello" = "Windows Hello"; "BitwardenAutofillGoToSettings" = "Δεν ήταν δυνατό να ανοίξουμε αυτόματα το μενού ρυθμίσεων αυτόματης συμπλήρωσης Android για εσάς. Μπορείτε να πλοηγηθείτε στο μενού ρυθμίσεων αυτόματης συμπλήρωσης με μη αυτόματο τρόπο από τις Ρυθμίσεις Android > Σύστημα > Γλώσσες και εισαγωγή > Σύνθετες > Υπηρεσία αυτόματης συμπλήρωσης."; -"CustomFieldName" = "Όνομα Προσαρμοσμένου Πεδίου"; +"CustomFieldName" = "Όνομα προσαρμοσμένου πεδίου"; "FieldTypeBoolean" = "Δυαδικό"; "FieldTypeHidden" = "Κρυφό"; "FieldTypeLinked" = "Συνδεδεμένο"; "FieldTypeText" = "Κείμενο"; -"NewCustomField" = "Νέο Προσαρμοσμένο Πεδίο"; +"NewCustomField" = "Νέο προσαρμοσμένο πεδίο"; "SelectTypeField" = "Τι τύπος προσαρμοσμένου πεδίου είναι αυτό που θέλετε να προσθέσετε;"; "Remove" = "Διαγραφή"; "NewUri" = "Νέο URI"; @@ -348,43 +348,43 @@ "RegEx" = "Κανονική έκφραση"; "StartsWith" = "Έναρξη με"; "URIMatchDetection" = "Εντοπισμός Αντιστοίχισης URI"; -"MatchDetection" = "Εντοπισμός Αντιστοίχισης"; -"YesAndSave" = "Αποδοχή και Αποθήκευση"; +"MatchDetection" = "Εντοπισμός αντιστοίχισης"; +"YesAndSave" = "Ναι, και αποθήκευση"; "AutofillAndSave" = "Αυτόματη συμπλήρωση και αποθήκευση"; "Organization" = "Οργανισμός"; "HoldYubikeyNearTop" = "Κρατήστε το Yubikey κοντά στην κορυφή της συσκευής."; "TryAgain" = "Προσπαθήστε ξανά"; "YubiKeyInstructionIos" = "Για να συνεχίσετε, κρατήστε παρατεταμένα το YubiKey NEO στο πίσω μέρος της συσκευής."; "BitwardenAutofillAccessibilityServiceDescription2" = "Η υπηρεσία προσβασιμότητας μπορεί να είναι χρήσιμη όταν οι εφαρμογές δεν υποστηρίζουν την τυπική υπηρεσία αυτόματης συμπλήρωσης."; -"DatePasswordUpdated" = "Ο Κωδικός Ενημερώθηκε"; +"DatePasswordUpdated" = "Ο κωδικός ενημερώθηκε"; "DateUpdated" = "Ενημερώθηκε"; "AutofillActivated" = "Η αυτόματη συμπλήρωση ενεργοποιήθηκε!"; "MustLogInMainAppAutofill" = "Για να μπορέσετε να χρησιμοποιήσετε την Αυτόματη συμπλήρωση, πρέπει να συνδεθείτε στην κύρια εφαρμογή Bitwarden."; "AutofillSetup" = "Οι συνδέσεις σας είναι πλέον εύκολα προσβάσιμες απευθείας από το πληκτρολόγιό σας κατά τη σύνδεση σε εφαρμογές και ιστοσελίδες."; "AutofillSetup2" = "Σας συνιστούμε να απενεργοποιήσετε οποιαδήποτε άλλη εφαρμογή Αυτόματης Συμπλήρωσης στις Ρυθμίσεις αν δεν σκοπεύετε να τις χρησιμοποιήσετε."; "BitwardenAutofillDescription" = "Αποκτήστε πρόσβαση στη λίστα σας απευθείας από το πληκτρολόγιό σας για γρήγορη αυτόματη συμπλήρωση κωδικών."; -"AutofillTurnOn" = "Για να ενεργοποιήσετε την αυτόματη συμπλήρωση του κωδικού στη συσκευή σας, ακολουθήστε αυτές τις οδηγίες:"; +"AutofillTurnOn" = "Για να ενεργοποιήσετε την αυτόματη συμπλήρωση κωδικού στη συσκευή σας, ακολουθήστε αυτές τις οδηγίες:"; "AutofillTurnOn1" = "1. Μεταβείτε στις \"Ρυθμίσεις\" του iOS"; "AutofillTurnOn2" = "2. Πατήστε \"Κωδικοί & Λογαριασμοί\""; "AutofillTurnOn3" = "3. Πατήστε \"Αυτόματη συμπλήρωση κωδικών\""; "AutofillTurnOn4" = "4. Ενεργοποιήστε την αυτόματη συμπλήρωση"; "AutofillTurnOn5" = "5. Επιλέξτε \"Bitwarden\""; -"PasswordAutofill" = "Αυτόματη Συμπλήρωση Κωδικού"; +"PasswordAutofill" = "Αυτόματη συμπλήρωση κωδικού"; "BitwardenAutofillAlert2" = "Ο ευκολότερος τρόπος για να προσθέσετε νέες συνδέσεις στο vault σας, είναι με την επέκταση αυτόματης συμπλήρωσης κωδικών Bitwarden. Μάθετε περισσότερα σχετικά με τη χρήση της επέκτασης αυτής, μεταβαίνοντας στις \"Ρυθμίσεις\"."; "InvalidEmail" = "Μη έγκυρη διεύθυνση email."; "Cards" = "Κάρτες"; "Identities" = "Ταυτότητες"; "Logins" = "Συνδέσεις"; -"SecureNotes" = "Ασφαλείς Σημειώσεις"; -"AllItems" = "Όλα τα Στοιχεία"; +"SecureNotes" = "Ασφαλείς σημειώσεις"; +"AllItems" = "Όλα τα στοιχεία"; "URIs" = "URIs"; "CheckingPassword" = "Έλεγχος κωδικού..."; "CheckPassword" = "Ελέγξτε εάν ο κωδικός σας έχει εκτεθεί."; "PasswordExposed" = "Ο κωδικός αυτός έχει εκτεθεί %1$@ φορά (ες) σε διαρροές δεδομένων. Πρέπει να τον αλλάξετε."; "PasswordSafe" = "Ο κωδικός αυτός δεν βρέθηκε σε γνωστές διαρροές δεδομένων. Είναι ασφαλής για χρήση."; -"IdentityName" = "Όνομα Ταυτότητας"; +"IdentityName" = "Όνομα ταυτότητας"; "Value" = "Τιμή"; -"PasswordHistory" = "Ιστορικό Κωδικού"; +"PasswordHistory" = "Ιστορικό κωδικού"; "Types" = "Τύποι"; "NoPasswordsToList" = "Δεν υπάρχουν κωδικοί προς εμφάνιση."; "NoItemsToList" = "Δεν υπάρχουν στοιχεία στη λίστα."; @@ -407,20 +407,20 @@ "MoveToOrganization" = "Μετακίνηση στον Οργανισμό"; "NoOrgsToList" = "Δεν υπάρχουν οργανισμοί προς εμφάνιση."; "MoveToOrgDesc" = "Επιλέξτε έναν οργανισμό στον οποίο θέλετε να μετακινήσετε αυτό το στοιχείο. Η μετακίνηση σε έναν οργανισμό μεταβιβάζει την ιδιοκτησία του στοιχείου σε αυτό τον οργανισμό. Δεν θα είστε πλέον ο άμεσος ιδιοκτήτης αυτού του στοιχείου μόλις το μετακινήσετε."; -"NumberOfWords" = "Αριθμός Λέξεων"; +"NumberOfWords" = "Αριθμός λέξεων"; "Passphrase" = "Συνθηματικό"; -"WordSeparator" = "Διαχωριστής Λέξεων"; +"WordSeparator" = "Διαχωριστής λέξεων"; "Clear" = "Εκκαθάριση"; "Generator" = "Γεννήτρια"; "NoFoldersToList" = "Δεν υπάρχουν φάκελοι προς εμφάνιση."; -"FingerprintPhrase" = "Φράση Δακτυλικών Αποτυπωμάτων"; +"FingerprintPhrase" = "Φράση δακτυλικών αποτυπωμάτων"; "YourAccountsFingerprint" = "Η φράση δακτυλικών αποτυπωμάτων του λογαριασμού σας"; "LearnOrgConfirmation" = "Το Bitwarden επιτρέπει να μοιράζεστε τα στοιχεία του vault σας με άλλους χρησιμοποιώντας ένα λογαριασμό οργανισμού. Θέλετε να επισκεφθείτε την ιστοσελίδα bitwarden.com για να μάθετε περισσότερα;"; -"ExportVault" = "Εξαγωγή Vault"; -"LockNow" = "Κλείδωμα Τώρα"; +"ExportVault" = "Εξαγωγή του vault"; +"LockNow" = "Κλείδωμα τώρα"; "PIN" = "PIN"; "Unlock" = "Ξεκλείδωμα"; -"UnlockVault" = "Ξεκλείδωμα Vault"; +"UnlockVault" = "Ξεκλείδωμα του vault"; "ThirtyMinutes" = "30 λεπτά"; "SetPINDescription" = "Ορίστε τον κωδικό PIN για να ξεκλειδώσετε το Bitwarden. Οι ρυθμίσεις PIN θα επαναρυθμιστούν αν αποσυνδεθείτε πλήρως από την εφαρμογή."; "LoggedInAsOn" = "Συνδεθήκατε ως %1$@ στις %2$@."; @@ -434,7 +434,7 @@ "ThirtySeconds" = "30 δευτερόλεπτα"; "TwentySeconds" = "20 δευτερόλεπτα"; "TwoMinutes" = "2 λεπτά"; -"ClearClipboard" = "Εκκαθάριση Πρόχειρου"; +"ClearClipboard" = "Εκκαθάριση πρόχειρου"; "ClearClipboardDescription" = "Αυτόματη εκκαθάριση αντιγραμμένων τιμών προχείρου."; "DefaultUriMatchDetection" = "Προεπιλεγμένη Ανίχνευση Αντιστοιχίας URI"; "DefaultUriMatchDetectionDescription" = "Επιλέξτε τον προκαθορισμένο τρόπο με τον οποίο αντιμετωπίζεται η ανίχνευση αντιστοίχισης URI για τις συνδέσεις, κατά την εκτέλεση ενεργειών, όπως είναι η αυτόματη συμπλήρωση."; @@ -442,28 +442,28 @@ "ThemeDescription" = "Αλλαγή χρώματος, θέματος εφαρμογής."; "ThemeDefault" = "Προεπιλογή (σύστημα)"; "DefaultDarkTheme" = "Προεπιλεγμένο σκοτεινό θέμα"; -"CopyNotes" = "Αντιγραφή Σημειώσεων"; +"CopyNotes" = "Αντιγραφή σημείωσης"; "Exit" = "Έξοδος"; "ExitConfirmation" = "Είστε βέβαιοι ότι θέλετε να βγείτε από το Bitwarden;"; "PINRequireMasterPasswordRestart" = "Θέλετε να απαιτείται ξεκλείδωμα με τον κύριο κωδικό κατά την επανεκκίνηση της εφαρμογής;"; "Black" = "Μαύρο"; "Nord" = "Nord"; "SolarizedDark" = "Solarized Dark"; -"AutofillBlockedUris" = "Αυτόματη συμπλήρωση μπλοκαρισμένων URIs"; -"AskToAddLogin" = "Ζητήστε να προσθέστε σύνδεση"; -"AskToAddLoginDescription" = "Ζητήστε να προσθέσετε ένα αντικείμενο αν δε βρεθεί στην κρύπτη σας."; -"OnRestart" = "Κατά την Επανεκκίνηση Εφαρμογής"; -"AutofillServiceNotEnabled" = "Η αυτόματη συμπλήρωση διευκολύνει την ασφαλή πρόσβαση στη λίστα από άλλες ιστοσελίδες και εφαρμογές. Φαίνεται ότι δεν έχετε ενεργοποιήσει την υπηρεσία αυτόματης συμπλήρωσης για το Bitwarden. Ενεργοποιήστε την από τις \"Ρυθμίσεις\"."; +"AutofillBlockedUris" = "Αυτόματη συμπλήρωση μπλοκαρισμένων URI"; +"AskToAddLogin" = "Ρωτήστε για να προσθέστε σύνδεση"; +"AskToAddLoginDescription" = "Ρωτήστε για να προσθέσετε ένα αντικείμενο αν δε βρεθεί στο vault σας."; +"OnRestart" = "Κατά την επανεκκίνηση της εφαρμογής"; +"AutofillServiceNotEnabled" = "Η αυτόματη συμπλήρωση διευκολύνει την ασφαλή πρόσβαση στο vault του Bitwarden από άλλες ιστοσελίδες και εφαρμογές. Φαίνεται ότι δεν έχετε ενεργοποιήσει την υπηρεσία αυτόματης συμπλήρωσης για το Bitwarden. Ενεργοποιήστε την αυτόματη συμπλήρωση από τις \"Ρυθμίσεις\"."; "ThemeAppliedOnRestart" = "Οι αλλαγές θεμάτων θα ισχύουν όταν γίνει επανεκκίνηση της εφαρμογής."; "Capitalize" = "Κεφαλαιοποίηση"; -"IncludeNumber" = "Συμπερίληψη Αριθμών"; +"IncludeNumber" = "Συμπερίληψη αριθμών"; "Download" = "Λήψη"; "Shared" = "Κοινοποιήθηκε"; "ToggleVisibility" = "Εναλλαγή ορατότητας"; "LoginExpired" = "Η περίοδος σύνδεσης σας έχει λήξει."; -"BiometricsDirection" = "Χρήση βιομετρικής μεθόδου για επαλήθευση."; +"BiometricsDirection" = "Βιομετρική επαλήθευση"; "Biometrics" = "Βιομετρική"; -"UseBiometricsToUnlock" = "Χρήση βιομετρικής μεθόδου για ξεκλείδωμα"; +"UseBiometricsToUnlock" = "Χρήση βιομετρικών για ξεκλείδωμα"; "AccessibilityOverlayPermissionAlert" = "Το Bitwarden χρειάζεται προσοχή - Ανατρέξτε στην ενότητα \"Υπηρεσία προσβασιμότητας αυτόματης συμπλήρωσης\" από τις ρυθμίσεις Bitwarden"; "BitwardenAutofillServiceOverlayPermission" = "3. Στις Ρυθμίσεις του Bitwarden για Android , μεταβείτε στην ενότητα \"Εμφάνιση από άλλες εφαρμογές\" (Για προχωρημένους) και πατήστε το διακόπτη για να ενεργοποιήσετε την υποστήριξη επικάλυψης."; "OverlayPermission" = "Άδειες"; @@ -471,15 +471,15 @@ "BitwardenAutofillServiceStep3" = "3. Στις Ρυθμίσεις του Bitwarden για Android , μεταβείτε στην ενότητα \"Εμφάνιση πάνω σε άλλες εφαρμογές\" (κάτω απο \"Για προχωρημένους\") και πατήστε το διακόπτη για να ενεργοποιήσετε την υποστήριξη επικάλυψης."; "Denied" = "Αρνήθηκε"; "Granted" = "Χορηγήθηκε"; -"FileFormat" = "Μορφή Αρχείου"; +"FileFormat" = "Μορφή αρχείου"; "ExportVaultMasterPasswordDescription" = "Εισαγάγετε τον κύριο κωδικό για εξαγωγή των δεδομένων vault."; "SendVerificationCodeToEmail" = "Στείλτε έναν κωδικό επαλήθευσης στο email σας"; -"CodeSent" = "Ο Κωδικός Στάλθηκε"; +"CodeSent" = "Ο κωδικός στάλθηκε!"; "ConfirmYourIdentity" = "Επιβεβαιώστε την ταυτότητα σας για να συνεχίσετε."; "ExportVaultWarning" = "Αυτή η εξαγωγή περιέχει τα δεδομένα σε μη κρυπτογραφημένη μορφή. Δεν πρέπει να αποθηκεύετε ή να στείλετε το εξαγόμενο αρχείο μέσω μη ασφαλών τρόπων (όπως μέσω email). Διαγράψτε το αμέσως μόλις τελειώσετε με τη χρήση του."; "EncExportKeyWarning" = "Αυτή η εξαγωγή κρυπτογραφεί τα δεδομένα σας χρησιμοποιώντας το κλειδί κρυπτογράφησης του λογαριασμού σας. Εάν ποτέ περιστρέψετε το κλειδί κρυπτογράφησης του λογαριασμού σας, θα πρέπει να κάνετε εξαγωγή ξανά, καθώς δεν θα μπορείτε να αποκρυπτογραφήσετε αυτό το αρχείο εξαγωγής."; "EncExportAccountWarning" = "Τα κλειδιά κρυπτογράφησης λογαριασμού είναι μοναδικά για κάθε λογαριασμό χρήστη Bitwarden, οπότε δεν μπορείτε να εισάγετε μια κρυπτογραφημένη εξαγωγή σε διαφορετικό λογαριασμό."; -"ExportVaultConfirmationTitle" = "Επιβεβαίωση εξαγωγής Vault"; +"ExportVaultConfirmationTitle" = "Επιβεβαίωση εξαγωγής vault"; "Warning" = "Προειδοποίηση"; "ExportVaultFailure" = "Παρουσιάστηκε πρόβλημα κατά την εξαγωγή της λίστας σας. Εάν το πρόβλημα επιμένει, θα χρειαστεί να εξάγετε το vault μέσω διαδικτύου."; "ExportVaultSuccess" = "Το Vault Εξάχθηκε με Επιτυχία"; @@ -554,10 +554,10 @@ "TextTypeIsSelected" = "Ο τύπος κειμένου είναι επιλεγμένος."; "TextTypeIsNotSelected" = "Ο τύπος κειμένου δεν είναι επιλεγμένος, πατήστε για επιλογή."; "DeletionDate" = "Ημερομηνία διαγραφής"; -"DeletionTime" = "Χρόνος Διαγραφής"; +"DeletionTime" = "Χρόνος διαγραφής"; "DeletionDateInfo" = "Το Send θα διαγραφεί οριστικά την καθορισμένη ημερομηνία και ώρα."; "PendingDelete" = "Εκκρεμεί διαγραφή"; -"ExpirationDate" = "Ημερομηνία Λήξης"; +"ExpirationDate" = "Ημερομηνία λήξης"; "ExpirationTime" = "Χρόνος λήξης"; "ExpirationDateInfo" = "Εάν οριστεί, η πρόσβαση σε αυτό το Send θα λήξει την καθορισμένη ημερομηνία και ώρα."; "Expired" = "Έληξε"; @@ -565,9 +565,9 @@ "MaximumAccessCountInfo" = "Εάν οριστεί, οι χρήστες δεν θα μπορούν πλέον να έχουν πρόσβαση σε αυτό το send μόλις επιτευχθεί ο μέγιστος αριθμός πρόσβασης."; "MaximumAccessCountReached" = "Φτάσατε στον μέγιστο αριθμό πρόσβασης"; "CurrentAccessCount" = "Τρέχων Αριθμός Πρόσβασης"; -"NewPassword" = "Νέος Κωδικός"; +"NewPassword" = "Νέος κωδικός πρόσβασης"; "PasswordInfo" = "Προαιρετικά απαιτείται κωδικός πρόσβασης για τους χρήστες για να έχουν πρόσβαση σε αυτό το Send."; -"RemovePassword" = "Αφαίρεση κωδικού"; +"RemovePassword" = "Αφαίρεση κωδικού πρόσβασης"; "AreYouSureRemoveSendPassword" = "Είστε βέβαιοι ότι θέλετε να καταργήσετε τον κωδικό πρόσβασης;"; "RemovingSendPassword" = "Αφαίρεση κωδικού..."; "SendPasswordRemoved" = "Ο κωδικός έχει αφαιρεθεί."; @@ -575,8 +575,8 @@ "DisableSend" = "Απενεργοποιήστε αυτό το Send έτσι ώστε κανείς να μην μπορεί να έχει πρόσβαση σε αυτό."; "NoSends" = "Δεν υπάρχουν sends στο λογαριασμό σας."; "AddASend" = "Προσθήκη Send"; -"CopyLink" = "Αντιγραφή Συνδέσμου"; -"ShareLink" = "Κοινοποίηση Συνδέσμου"; +"CopyLink" = "Αντιγραφή συνδέσμου"; +"ShareLink" = "Κοινοποίηση συνδέσμου"; "SendLink" = "Σύνδεσμος Send"; "SearchSends" = "Αναζήτηση Sends"; "EditSend" = "Επεξεργασία Send"; @@ -603,12 +603,12 @@ "PasswordConfirmationDesc" = "Αυτή η ενέργεια προστατεύεται, για να συνεχίσετε, πληκτρολογήστε ξανά τον κύριο κωδικό πρόσβασης για να επαληθεύσετε την ταυτότητα σας."; "CaptchaRequired" = "Απαιτείται Captcha"; "CaptchaFailed" = "Το Captcha απέτυχε. Παρακαλώ προσπάθησε ξανα."; -"UpdatedMasterPassword" = "Ενημερώθηκε ο κύριος κωδικός πρόσβασης"; +"UpdatedMasterPassword" = "Ενημερωμένος κύριος κωδικός πρόσβασης"; "UpdateMasterPassword" = "Ενημερώστε τον κύριο κωδικό πρόσβασης"; "UpdateMasterPasswordWarning" = "Ο Κύριος Κωδικός Πρόσβασής σας άλλαξε πρόσφατα από διαχειριστή στον οργανισμό σας. Για να αποκτήσετε πρόσβαση στο vault, πρέπει να ενημερώσετε το κύριο κωδικό τώρα. Η διαδικασία θα σας αποσυνδέσει από την τρέχουσα συνεδρία σας, απαιτώντας από εσάς να συνδεθείτε ξανά. Οι ενεργές συνεδρίες σε άλλες συσκευές ενδέχεται να συνεχίσουν να είναι ενεργές για μία ώρα."; "UpdatingPassword" = "Ενημέρωση Κωδικού Πρόσβασης"; "UpdatePasswordError" = "Δεν είναι δυνατή η ενημέρωση του κωδικού πρόσβασης"; -"RemoveMasterPassword" = "Αφαίρεση Κύριου Κωδικού Πρόσβασης"; +"RemoveMasterPassword" = "Αφαίρεση κύριου κωδικού πρόσβασης"; "RemoveMasterPasswordWarning" = "%1$@ χρησιμοποιεί SSO με κρυπτογράφηση διαχείρισης πελατών. Συνεχίζοντας θα καταργήσετε τον Κύριο Κωδικό από το λογαριασμό σας και θα απαιτήσετε SSO για να συνδεθείτε."; "RemoveMasterPasswordWarning2" = "Αν δεν θέλετε να αφαιρέσετε τον Κύριο Κωδικό Πρόσβασης, μπορείτε να φύγετε από αυτόν τον οργανισμό."; "LeaveOrganization" = "Αποχώρηση από τον οργανισμό"; @@ -847,14 +847,14 @@ "ContinueToX" = "Συνέχεια στο %1$@;"; "ContinueToHelpCenter" = "Συνέχεια στο κέντρο βοήθειας;"; "ContinueToContactSupport" = "Συνέχεια στην επικοινωνία με την υποστήριξη;"; -"ContinueToPrivacyPolicy" = "Continue to privacy policy?"; +"ContinueToPrivacyPolicy" = "Συνέχεια στην πολιτική απορρήτου;"; "ContinueToAppStore" = "Συνέχεια στο κατάστημα εφαρμογών;"; "TwoStepLoginDescriptionLong" = "Κάντε τον λογαριασμό σας πιο ασφαλή με τη ρύθμιση δύο βημάτων σύνδεσης στην εφαρμογή διαδικτύου Bitwarden."; "ChangeMasterPasswordDescriptionLong" = "Μπορείτε να αλλάξετε τον κύριο κωδικό πρόσβασης στην εφαρμογή διαδικτύου Bitwarden."; "YouCanImportDataToYourVaultOnX" = "Μπορείτε να εισαγάγετε δεδομένα στο vault σας στο %1$@."; "LearnMoreAboutHowToUseBitwardenOnTheHelpCenter" = "Μάθετε περισσότερα για το πώς να χρησιμοποιήσετε το Bitwarden στο κέντρο βοήθειας."; "ContactSupportDescriptionLong" = "Δεν μπορείτε να βρείτε αυτό που ψάχνετε; Επικοινωνήστε με την υποστήριξη Bitwarden στο bitwarden.com."; -"PrivacyPolicyDescriptionLong" = "Check out our privacy policy on bitwarden.com."; +"PrivacyPolicyDescriptionLong" = "Δείτε την πολιτική απορρήτου μας στο bitwarden.com."; "ExploreMoreFeaturesOfYourBitwardenAccountOnTheWebApp" = "Εξερευνήστε περισσότερες δυνατότητες του Bitwarden λογαριασμού σας, στην εφαρμογή διαδικτύου."; "LearnAboutOrganizationsDescriptionLong" = "Το Bitwarden σας επιτρέπει να μοιράζεστε τα στοιχεία του vault σας με άλλους, χρησιμοποιώντας έναν λογαριασμό οργανισμού. Μάθετε περισσότερα στην ιστοσελίδα bitwarden.com."; "RateAppDescriptionLong" = "Βοηθήστε άλλους να μάθουν αν το Bitwarden είναι κατάλληλο για αυτούς. Επισκεφθείτε το κατάστημα εφαρμογών και αφήστε τώρα μια βαθμολογία."; @@ -864,4 +864,11 @@ "AccountLoggedOutBiometricExceeded" = "Ο λογαριασμός αποσυνδέθηκε."; "YourOrganizationPermissionsWereUpdatedRequeringYouToSetAMasterPassword" = "Τα δικαιώματα του οργανισμού σας ενημερώθηκαν, απαιτώντας από εσάς να ορίσετε έναν κύριο κωδικό πρόσβασης."; "YourOrganizationRequiresYouToSetAMasterPassword" = "Ο οργανισμός σας απαιτεί να ορίσετε έναν κύριο κωδικό πρόσβασης."; -"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Ρυθμίστε μια επιλογή κλειδώματος για να αλλάξετε την ενέργεια στη λήξη χρόνου του vault σας."; \ No newline at end of file +"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Ρυθμίστε μια επιλογή κλειδώματος για να αλλάξετε την ενέργεια στη λήξη χρόνου του vault σας."; +"DuoTwoStepLoginIsRequiredForYourAccount" = "Duo σύνδεση δύο βημάτων απαιτείται για το λογαριασμό σας."; +"FollowTheStepsFromDuoToFinishLoggingIn" = "Ακολουθήστε τα βήματα από το Duo για να ολοκληρώσετε τη σύνδεση."; +"LaunchDuo" = "Εκκίνηση Duo"; +"OrganizationUnassignedItemsMessageUSEUDescriptionLong" = "Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."; +"OrganizationUnassignedItemsMessageSelfHost041624DescriptionLong" = "On May 16, 2024, unassigned organization items will no longer be visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."; +"RemindMeLater" = "Remind me later"; +"Notice" = "Notice"; \ No newline at end of file diff --git a/BitwardenShared/UI/Platform/Application/Support/Localizations/en-GB.lproj/Localizable.strings b/BitwardenShared/UI/Platform/Application/Support/Localizations/en-GB.lproj/Localizable.strings index b92344d84..2fd2a6a10 100644 --- a/BitwardenShared/UI/Platform/Application/Support/Localizations/en-GB.lproj/Localizable.strings +++ b/BitwardenShared/UI/Platform/Application/Support/Localizations/en-GB.lproj/Localizable.strings @@ -864,4 +864,11 @@ "AccountLoggedOutBiometricExceeded" = "Account logged out."; "YourOrganizationPermissionsWereUpdatedRequeringYouToSetAMasterPassword" = "Your organization permissions were updated, requiring you to set a master password."; "YourOrganizationRequiresYouToSetAMasterPassword" = "Your organization requires you to set a master password."; -"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Set up an unlock option to change your vault timeout action."; \ No newline at end of file +"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Set up an unlock option to change your vault timeout action."; +"DuoTwoStepLoginIsRequiredForYourAccount" = "Duo two-step login is required for your account."; +"FollowTheStepsFromDuoToFinishLoggingIn" = "Follow the steps from Duo to finish logging in."; +"LaunchDuo" = "Launch Duo"; +"OrganizationUnassignedItemsMessageUSEUDescriptionLong" = "Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."; +"OrganizationUnassignedItemsMessageSelfHost041624DescriptionLong" = "On May 16, 2024, unassigned organization items will no longer be visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."; +"RemindMeLater" = "Remind me later"; +"Notice" = "Notice"; \ No newline at end of file diff --git a/BitwardenShared/UI/Platform/Application/Support/Localizations/en-IN.lproj/Localizable.strings b/BitwardenShared/UI/Platform/Application/Support/Localizations/en-IN.lproj/Localizable.strings index a4b5e4b3b..0b898974e 100644 --- a/BitwardenShared/UI/Platform/Application/Support/Localizations/en-IN.lproj/Localizable.strings +++ b/BitwardenShared/UI/Platform/Application/Support/Localizations/en-IN.lproj/Localizable.strings @@ -864,4 +864,11 @@ "AccountLoggedOutBiometricExceeded" = "Account logged out."; "YourOrganizationPermissionsWereUpdatedRequeringYouToSetAMasterPassword" = "Your organization permissions were updated, requiring you to set a master password."; "YourOrganizationRequiresYouToSetAMasterPassword" = "Your organization requires you to set a master password."; -"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Set up an unlock option to change your vault timeout action."; \ No newline at end of file +"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Set up an unlock option to change your vault timeout action."; +"DuoTwoStepLoginIsRequiredForYourAccount" = "Duo two-step login is required for your account."; +"FollowTheStepsFromDuoToFinishLoggingIn" = "Follow the steps from Duo to finish logging in."; +"LaunchDuo" = "Launch Duo"; +"OrganizationUnassignedItemsMessageUSEUDescriptionLong" = "Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."; +"OrganizationUnassignedItemsMessageSelfHost041624DescriptionLong" = "On May 16, 2024, unassigned organization items will no longer be visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."; +"RemindMeLater" = "Remind me later"; +"Notice" = "Notice"; \ No newline at end of file diff --git a/BitwardenShared/UI/Platform/Application/Support/Localizations/en.lproj/Localizable.strings b/BitwardenShared/UI/Platform/Application/Support/Localizations/en.lproj/Localizable.strings index b464f92f8..abd1d6417 100644 --- a/BitwardenShared/UI/Platform/Application/Support/Localizations/en.lproj/Localizable.strings +++ b/BitwardenShared/UI/Platform/Application/Support/Localizations/en.lproj/Localizable.strings @@ -871,10 +871,14 @@ "DuoTwoStepLoginIsRequiredForYourAccount" = "Duo two-step login is required for your account."; "FollowTheStepsFromDuoToFinishLoggingIn" = "Follow the steps from Duo to finish logging in."; "LaunchDuo" = "Launch Duo"; +"OrganizationUnassignedItemsMessageUSEUDescriptionLong" = "Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."; +"OrganizationUnassignedItemsMessageSelfHost041624DescriptionLong" = "On May 16, 2024, unassigned organization items will no longer be visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."; +"RemindMeLater" = "Remind me later"; +"Notice" = "Notice"; "AppInfo" = "App info"; "Browse" = "Browse"; "SelectLanguage" = "Select language"; -"ContinueToCompleteWebAuthnVerfication" = "Continue to complete WebAuthn verfication."; +"ContinueToCompleteWebAuthnVerification" = "Continue to complete WebAuthn verification."; "ThereWasAnErrorStartingWebAuthnTwoFactorAuthentication" = "There was an error starting WebAuthn two factor authentication"; "LaunchWebAuthn" = "Launch WebAuthn"; "Duo" = "Duo"; diff --git a/BitwardenShared/UI/Platform/Application/Support/Localizations/es.lproj/Localizable.strings b/BitwardenShared/UI/Platform/Application/Support/Localizations/es.lproj/Localizable.strings index 11ab57c45..b59787afd 100644 --- a/BitwardenShared/UI/Platform/Application/Support/Localizations/es.lproj/Localizable.strings +++ b/BitwardenShared/UI/Platform/Application/Support/Localizations/es.lproj/Localizable.strings @@ -847,14 +847,14 @@ "ContinueToX" = "¿Continuar a %1$@?"; "ContinueToHelpCenter" = "¿Continuar al centro de ayuda?"; "ContinueToContactSupport" = "¿Continuar con el servicio de asistencia?"; -"ContinueToPrivacyPolicy" = "Continue to privacy policy?"; +"ContinueToPrivacyPolicy" = "¿Continuar a la política de privacidad?"; "ContinueToAppStore" = "¿Continuar a la App Store?"; "TwoStepLoginDescriptionLong" = "Haz tu cuenta más segura al configurar el inicio de sesión en dos pasos en la aplicación web de Bitwarden."; "ChangeMasterPasswordDescriptionLong" = "Puedes cambiar tu contraseña maestra en la aplicación web de Bitwarden."; "YouCanImportDataToYourVaultOnX" = "Puedes importar datos a tu caja fuerte en %1$@."; "LearnMoreAboutHowToUseBitwardenOnTheHelpCenter" = "Más información sobre cómo usar Bitwarden en el centro de Ayuda."; "ContactSupportDescriptionLong" = "¿No encuentras lo que estás buscando? Contacta con el soporte de Bitwarden en bitwarden.com."; -"PrivacyPolicyDescriptionLong" = "Check out our privacy policy on bitwarden.com."; +"PrivacyPolicyDescriptionLong" = "Revise nuestra política de privacidad en bitwarden.com."; "ExploreMoreFeaturesOfYourBitwardenAccountOnTheWebApp" = "Explora más características de tu cuenta de Bitwarden en la aplicación web."; "LearnAboutOrganizationsDescriptionLong" = "Bitwarden te permite compartir tus elementos de la caja fuerte con otros utilizando una organización. Más información en el sitio web de bitwarden.com."; "RateAppDescriptionLong" = "Ayuda a otros a averiguar si Bitwarden es correcto para ellos. Visita la tienda de aplicaciones y deja una calificación ahora."; @@ -864,4 +864,11 @@ "AccountLoggedOutBiometricExceeded" = "Sesión de la cuenta cerrada."; "YourOrganizationPermissionsWereUpdatedRequeringYouToSetAMasterPassword" = "Los permisos de su organización han sido actualizados, requiriendo que establezca una contraseña maestra."; "YourOrganizationRequiresYouToSetAMasterPassword" = "Tu organización requiere que establezcas una contraseña maestra."; -"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Configura una opción de desbloqueo para cambiar tu acción de tiempo de espera de tu caja fuerte."; \ No newline at end of file +"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Configura una opción de desbloqueo para cambiar tu acción de tiempo de espera de tu caja fuerte."; +"DuoTwoStepLoginIsRequiredForYourAccount" = "Se requiere el inicio de sesión en dos pasos Duo para su cuenta."; +"FollowTheStepsFromDuoToFinishLoggingIn" = "Siga los pasos de Duo para terminar de iniciar sesión."; +"LaunchDuo" = "Iniciar Duo"; +"OrganizationUnassignedItemsMessageUSEUDescriptionLong" = "Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."; +"OrganizationUnassignedItemsMessageSelfHost041624DescriptionLong" = "On May 16, 2024, unassigned organization items will no longer be visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."; +"RemindMeLater" = "Remind me later"; +"Notice" = "Notice"; \ No newline at end of file diff --git a/BitwardenShared/UI/Platform/Application/Support/Localizations/et.lproj/Localizable.strings b/BitwardenShared/UI/Platform/Application/Support/Localizations/et.lproj/Localizable.strings index 390b5726a..f12dedcf5 100644 --- a/BitwardenShared/UI/Platform/Application/Support/Localizations/et.lproj/Localizable.strings +++ b/BitwardenShared/UI/Platform/Application/Support/Localizations/et.lproj/Localizable.strings @@ -864,4 +864,11 @@ "AccountLoggedOutBiometricExceeded" = "Account logged out."; "YourOrganizationPermissionsWereUpdatedRequeringYouToSetAMasterPassword" = "Your organization permissions were updated, requiring you to set a master password."; "YourOrganizationRequiresYouToSetAMasterPassword" = "Your organization requires you to set a master password."; -"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Set up an unlock option to change your vault timeout action."; \ No newline at end of file +"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Set up an unlock option to change your vault timeout action."; +"DuoTwoStepLoginIsRequiredForYourAccount" = "Duo two-step login is required for your account."; +"FollowTheStepsFromDuoToFinishLoggingIn" = "Follow the steps from Duo to finish logging in."; +"LaunchDuo" = "Launch Duo"; +"OrganizationUnassignedItemsMessageUSEUDescriptionLong" = "Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."; +"OrganizationUnassignedItemsMessageSelfHost041624DescriptionLong" = "On May 16, 2024, unassigned organization items will no longer be visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."; +"RemindMeLater" = "Remind me later"; +"Notice" = "Notice"; \ No newline at end of file diff --git a/BitwardenShared/UI/Platform/Application/Support/Localizations/eu.lproj/Localizable.strings b/BitwardenShared/UI/Platform/Application/Support/Localizations/eu.lproj/Localizable.strings index f6fc682d7..e513c6fa9 100644 --- a/BitwardenShared/UI/Platform/Application/Support/Localizations/eu.lproj/Localizable.strings +++ b/BitwardenShared/UI/Platform/Application/Support/Localizations/eu.lproj/Localizable.strings @@ -864,4 +864,11 @@ "AccountLoggedOutBiometricExceeded" = "Account logged out."; "YourOrganizationPermissionsWereUpdatedRequeringYouToSetAMasterPassword" = "Your organization permissions were updated, requiring you to set a master password."; "YourOrganizationRequiresYouToSetAMasterPassword" = "Your organization requires you to set a master password."; -"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Set up an unlock option to change your vault timeout action."; \ No newline at end of file +"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Set up an unlock option to change your vault timeout action."; +"DuoTwoStepLoginIsRequiredForYourAccount" = "Duo two-step login is required for your account."; +"FollowTheStepsFromDuoToFinishLoggingIn" = "Follow the steps from Duo to finish logging in."; +"LaunchDuo" = "Launch Duo"; +"OrganizationUnassignedItemsMessageUSEUDescriptionLong" = "Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."; +"OrganizationUnassignedItemsMessageSelfHost041624DescriptionLong" = "On May 16, 2024, unassigned organization items will no longer be visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."; +"RemindMeLater" = "Remind me later"; +"Notice" = "Notice"; \ No newline at end of file diff --git a/BitwardenShared/UI/Platform/Application/Support/Localizations/fa.lproj/Localizable.strings b/BitwardenShared/UI/Platform/Application/Support/Localizations/fa.lproj/Localizable.strings index daf86b54c..237f253e3 100644 --- a/BitwardenShared/UI/Platform/Application/Support/Localizations/fa.lproj/Localizable.strings +++ b/BitwardenShared/UI/Platform/Application/Support/Localizations/fa.lproj/Localizable.strings @@ -864,4 +864,11 @@ "AccountLoggedOutBiometricExceeded" = "حساب از سیستم خارج شد."; "YourOrganizationPermissionsWereUpdatedRequeringYouToSetAMasterPassword" = "Your organization permissions were updated, requiring you to set a master password."; "YourOrganizationRequiresYouToSetAMasterPassword" = "Your organization requires you to set a master password."; -"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Set up an unlock option to change your vault timeout action."; \ No newline at end of file +"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Set up an unlock option to change your vault timeout action."; +"DuoTwoStepLoginIsRequiredForYourAccount" = "Duo two-step login is required for your account."; +"FollowTheStepsFromDuoToFinishLoggingIn" = "Follow the steps from Duo to finish logging in."; +"LaunchDuo" = "Launch Duo"; +"OrganizationUnassignedItemsMessageUSEUDescriptionLong" = "Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."; +"OrganizationUnassignedItemsMessageSelfHost041624DescriptionLong" = "On May 16, 2024, unassigned organization items will no longer be visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."; +"RemindMeLater" = "Remind me later"; +"Notice" = "Notice"; \ No newline at end of file diff --git a/BitwardenShared/UI/Platform/Application/Support/Localizations/fi.lproj/Localizable.strings b/BitwardenShared/UI/Platform/Application/Support/Localizations/fi.lproj/Localizable.strings index 12f3dd675..a84cc01fe 100644 --- a/BitwardenShared/UI/Platform/Application/Support/Localizations/fi.lproj/Localizable.strings +++ b/BitwardenShared/UI/Platform/Application/Support/Localizations/fi.lproj/Localizable.strings @@ -129,7 +129,7 @@ "VaultTimeoutAction" = "Holvin aikakatkaisutoiminto"; "VaultTimeoutLogOutConfirmation" = "Uloskirjautuminen estää pääsyn holviisi ja vaatii ajan umpeuduttua todennuksen Internet-yhteyden välityksellä. Haluatko varmasti käyttää asetusta?"; "LoggingIn" = "Kirjaudutaan sisään…"; -"LoginOrCreateNewAccount" = "Käytä salattua holviasi kirjautumalla sisään tai tai luo uusi tili."; +"LoginOrCreateNewAccount" = "Käytä salattua holviasi kirjautumalla sisään tai luo uusi tili."; "Manage" = "Hallinta"; "MasterPasswordConfirmationValMessage" = "Salasanan vahvistus ei täsmää."; "MasterPasswordDescription" = "Pääsalasanalla pääset käsiksi holviisi. On erittäin tärkeää, että muistat pääsalasanasi, koska sen palautus ei ole mahdollista, jos unohdat sen."; @@ -864,4 +864,11 @@ "AccountLoggedOutBiometricExceeded" = "Tili kirjattiin ulos."; "YourOrganizationPermissionsWereUpdatedRequeringYouToSetAMasterPassword" = "Organisaatiosi käyttöoikeuksia muutettiin ja tämän seurauksena sinun on asetettava pääsalasana."; "YourOrganizationRequiresYouToSetAMasterPassword" = "Organisaatiosi edellyttää, että asetat pääsalasanan."; -"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Muuta holvisi aikakatkaisutoimintoa määrittämällä lukituksen avaustapa."; \ No newline at end of file +"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Muuta holvisi aikakatkaisutoimintoa määrittämällä lukituksen avaustapa."; +"DuoTwoStepLoginIsRequiredForYourAccount" = "Tilillesi kirjautuminen vaatii Duo-vahvistuksen."; +"FollowTheStepsFromDuoToFinishLoggingIn" = "Viimeistele kirjautuminen seuraamalla Duon ohjeita."; +"LaunchDuo" = "Avaa Duo"; +"OrganizationUnassignedItemsMessageUSEUDescriptionLong" = "Organisaatioiden kokoelmiin määrittämättömät kohteet eivät enää näy laitteiden \"Kaikki holvit\" -näkymissä, vaan ne ovat nähtävissä vain Hallintapaneelista. Määritä kohteet kokoelmiin Hallintapaneelista, jotta ne ovat jatkossakin käytettävissä kaikilta laitteilta."; +"OrganizationUnassignedItemsMessageSelfHost041624DescriptionLong" = "16.5.2024 alkaen kokoelmiin määrittämättömät organisaatioiden kohteet eivät enää näy laitteiden \"Kaikki holvit\" -näkymissä, vaan ne ovat nähtävissä vain Hallintapaneelista. Määritä kohteet kokoelmiin Hallintapaneelista, jotta ne ovat jatkossakin käytettävissä kaikilta laitteilta."; +"RemindMeLater" = "Muistuta myöhemmin"; +"Notice" = "Huomautus"; \ No newline at end of file diff --git a/BitwardenShared/UI/Platform/Application/Support/Localizations/fil.lproj/Localizable.strings b/BitwardenShared/UI/Platform/Application/Support/Localizations/fil.lproj/Localizable.strings index 9f9744dd2..8e8e6a416 100644 --- a/BitwardenShared/UI/Platform/Application/Support/Localizations/fil.lproj/Localizable.strings +++ b/BitwardenShared/UI/Platform/Application/Support/Localizations/fil.lproj/Localizable.strings @@ -864,4 +864,11 @@ "AccountLoggedOutBiometricExceeded" = "Account logged out."; "YourOrganizationPermissionsWereUpdatedRequeringYouToSetAMasterPassword" = "Your organization permissions were updated, requiring you to set a master password."; "YourOrganizationRequiresYouToSetAMasterPassword" = "Your organization requires you to set a master password."; -"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Set up an unlock option to change your vault timeout action."; \ No newline at end of file +"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Set up an unlock option to change your vault timeout action."; +"DuoTwoStepLoginIsRequiredForYourAccount" = "Duo two-step login is required for your account."; +"FollowTheStepsFromDuoToFinishLoggingIn" = "Follow the steps from Duo to finish logging in."; +"LaunchDuo" = "Launch Duo"; +"OrganizationUnassignedItemsMessageUSEUDescriptionLong" = "Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."; +"OrganizationUnassignedItemsMessageSelfHost041624DescriptionLong" = "On May 16, 2024, unassigned organization items will no longer be visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."; +"RemindMeLater" = "Remind me later"; +"Notice" = "Notice"; \ No newline at end of file diff --git a/BitwardenShared/UI/Platform/Application/Support/Localizations/fr.lproj/Localizable.strings b/BitwardenShared/UI/Platform/Application/Support/Localizations/fr.lproj/Localizable.strings index 995809390..d084e580d 100644 --- a/BitwardenShared/UI/Platform/Application/Support/Localizations/fr.lproj/Localizable.strings +++ b/BitwardenShared/UI/Platform/Application/Support/Localizations/fr.lproj/Localizable.strings @@ -864,4 +864,11 @@ "AccountLoggedOutBiometricExceeded" = "Compte déconnecté."; "YourOrganizationPermissionsWereUpdatedRequeringYouToSetAMasterPassword" = "Les autorisations de votre organisation ont été mises à jour, vous obligeant à définir un mot de passe principal."; "YourOrganizationRequiresYouToSetAMasterPassword" = "Votre organisation vous demande de définir un mot de passe principal."; -"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Configurez une méthode de déverrouillage pour modifier l'action après délai d'expiration de votre coffre."; \ No newline at end of file +"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Configurez une méthode de déverrouillage pour modifier l'action après délai d'expiration de votre coffre."; +"DuoTwoStepLoginIsRequiredForYourAccount" = "L'authentification à deux facteurs Duo est requise pour votre compte."; +"FollowTheStepsFromDuoToFinishLoggingIn" = "Suivez les étapes à partir de Duo pour terminer la connexion."; +"LaunchDuo" = "Lancer Duo"; +"OrganizationUnassignedItemsMessageUSEUDescriptionLong" = "Les éléments d'organisation non assignés ne sont plus visibles dans la vue de Tous les coffres et sont uniquement accessibles via la Console d'administration. Assignez ces éléments à une collection à partir de la Console d'administration pour les rendre visibles."; +"OrganizationUnassignedItemsMessageSelfHost041624DescriptionLong" = "Au 2 mai 2024, les éléments d'organisation non assignés ne sont plus visibles dans la vue de Tous les coffres et sont uniquement accessibles via la Console d'administration. Assignez ces éléments à une collection à partir de la Console d'administration pour les rendre visibles."; +"RemindMeLater" = "Me le rappeler plus tard"; +"Notice" = "Remarque"; \ No newline at end of file diff --git a/BitwardenShared/UI/Platform/Application/Support/Localizations/gl.lproj/Localizable.strings b/BitwardenShared/UI/Platform/Application/Support/Localizations/gl.lproj/Localizable.strings index 85904a1f2..ae6afd8e8 100644 --- a/BitwardenShared/UI/Platform/Application/Support/Localizations/gl.lproj/Localizable.strings +++ b/BitwardenShared/UI/Platform/Application/Support/Localizations/gl.lproj/Localizable.strings @@ -864,4 +864,11 @@ "AccountLoggedOutBiometricExceeded" = "Account logged out."; "YourOrganizationPermissionsWereUpdatedRequeringYouToSetAMasterPassword" = "Your organization permissions were updated, requiring you to set a master password."; "YourOrganizationRequiresYouToSetAMasterPassword" = "Your organization requires you to set a master password."; -"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Set up an unlock option to change your vault timeout action."; \ No newline at end of file +"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Set up an unlock option to change your vault timeout action."; +"DuoTwoStepLoginIsRequiredForYourAccount" = "Duo two-step login is required for your account."; +"FollowTheStepsFromDuoToFinishLoggingIn" = "Follow the steps from Duo to finish logging in."; +"LaunchDuo" = "Launch Duo"; +"OrganizationUnassignedItemsMessageUSEUDescriptionLong" = "Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."; +"OrganizationUnassignedItemsMessageSelfHost041624DescriptionLong" = "On May 16, 2024, unassigned organization items will no longer be visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."; +"RemindMeLater" = "Remind me later"; +"Notice" = "Notice"; \ No newline at end of file diff --git a/BitwardenShared/UI/Platform/Application/Support/Localizations/he.lproj/Localizable.strings b/BitwardenShared/UI/Platform/Application/Support/Localizations/he.lproj/Localizable.strings index f2919b516..f0448d784 100644 --- a/BitwardenShared/UI/Platform/Application/Support/Localizations/he.lproj/Localizable.strings +++ b/BitwardenShared/UI/Platform/Application/Support/Localizations/he.lproj/Localizable.strings @@ -864,4 +864,11 @@ "AccountLoggedOutBiometricExceeded" = "Account logged out."; "YourOrganizationPermissionsWereUpdatedRequeringYouToSetAMasterPassword" = "Your organization permissions were updated, requiring you to set a master password."; "YourOrganizationRequiresYouToSetAMasterPassword" = "Your organization requires you to set a master password."; -"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Set up an unlock option to change your vault timeout action."; \ No newline at end of file +"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Set up an unlock option to change your vault timeout action."; +"DuoTwoStepLoginIsRequiredForYourAccount" = "Duo two-step login is required for your account."; +"FollowTheStepsFromDuoToFinishLoggingIn" = "Follow the steps from Duo to finish logging in."; +"LaunchDuo" = "Launch Duo"; +"OrganizationUnassignedItemsMessageUSEUDescriptionLong" = "Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."; +"OrganizationUnassignedItemsMessageSelfHost041624DescriptionLong" = "On May 16, 2024, unassigned organization items will no longer be visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."; +"RemindMeLater" = "Remind me later"; +"Notice" = "Notice"; \ No newline at end of file diff --git a/BitwardenShared/UI/Platform/Application/Support/Localizations/hi.lproj/Localizable.strings b/BitwardenShared/UI/Platform/Application/Support/Localizations/hi.lproj/Localizable.strings index 56e061de9..ce1239a5b 100644 --- a/BitwardenShared/UI/Platform/Application/Support/Localizations/hi.lproj/Localizable.strings +++ b/BitwardenShared/UI/Platform/Application/Support/Localizations/hi.lproj/Localizable.strings @@ -864,4 +864,11 @@ "AccountLoggedOutBiometricExceeded" = "Account logged out."; "YourOrganizationPermissionsWereUpdatedRequeringYouToSetAMasterPassword" = "Your organization permissions were updated, requiring you to set a master password."; "YourOrganizationRequiresYouToSetAMasterPassword" = "Your organization requires you to set a master password."; -"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Set up an unlock option to change your vault timeout action."; \ No newline at end of file +"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Set up an unlock option to change your vault timeout action."; +"DuoTwoStepLoginIsRequiredForYourAccount" = "Duo two-step login is required for your account."; +"FollowTheStepsFromDuoToFinishLoggingIn" = "Follow the steps from Duo to finish logging in."; +"LaunchDuo" = "Launch Duo"; +"OrganizationUnassignedItemsMessageUSEUDescriptionLong" = "Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."; +"OrganizationUnassignedItemsMessageSelfHost041624DescriptionLong" = "On May 16, 2024, unassigned organization items will no longer be visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."; +"RemindMeLater" = "Remind me later"; +"Notice" = "Notice"; \ No newline at end of file diff --git a/BitwardenShared/UI/Platform/Application/Support/Localizations/hr.lproj/Localizable.strings b/BitwardenShared/UI/Platform/Application/Support/Localizations/hr.lproj/Localizable.strings index 2a30b2255..558dc0e9d 100644 --- a/BitwardenShared/UI/Platform/Application/Support/Localizations/hr.lproj/Localizable.strings +++ b/BitwardenShared/UI/Platform/Application/Support/Localizations/hr.lproj/Localizable.strings @@ -864,4 +864,11 @@ "AccountLoggedOutBiometricExceeded" = "Račun odjavljen."; "YourOrganizationPermissionsWereUpdatedRequeringYouToSetAMasterPassword" = "Moraš postaviti glavnu lozinku jer su dopuštenja tvoje organizacije ažurirana."; "YourOrganizationRequiresYouToSetAMasterPassword" = "Tvoja organizacija zahtijeva da postaviš glavnu lozinku."; -"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Za promjenu vremena isteka trezora, odredi način otključavanja."; \ No newline at end of file +"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Za promjenu vremena isteka trezora, odredi način otključavanja."; +"DuoTwoStepLoginIsRequiredForYourAccount" = "Duo two-step login is required for your account."; +"FollowTheStepsFromDuoToFinishLoggingIn" = "Follow the steps from Duo to finish logging in."; +"LaunchDuo" = "Launch Duo"; +"OrganizationUnassignedItemsMessageUSEUDescriptionLong" = "Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."; +"OrganizationUnassignedItemsMessageSelfHost041624DescriptionLong" = "On May 16, 2024, unassigned organization items will no longer be visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."; +"RemindMeLater" = "Remind me later"; +"Notice" = "Notice"; \ No newline at end of file diff --git a/BitwardenShared/UI/Platform/Application/Support/Localizations/hu.lproj/Localizable.strings b/BitwardenShared/UI/Platform/Application/Support/Localizations/hu.lproj/Localizable.strings index f4ba9a5fe..f1d12f467 100644 --- a/BitwardenShared/UI/Platform/Application/Support/Localizations/hu.lproj/Localizable.strings +++ b/BitwardenShared/UI/Platform/Application/Support/Localizations/hu.lproj/Localizable.strings @@ -864,4 +864,11 @@ "AccountLoggedOutBiometricExceeded" = "A fiók kijelentkezett."; "YourOrganizationPermissionsWereUpdatedRequeringYouToSetAMasterPassword" = "A szervezeti jogosultságok frissítésre kerültek, ezért be kell állítani egy mesterjelszót."; "YourOrganizationRequiresYouToSetAMasterPassword" = "A szervezet megköveteli egy mesterjelszó beállítását."; -"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Állítsunk be egy feloldási módot a széf időkifutási műveletének módosításához."; \ No newline at end of file +"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Állítsunk be egy feloldási módot a széf időkifutási műveletének módosításához."; +"DuoTwoStepLoginIsRequiredForYourAccount" = "DUO kétlépéses bejelentkezés szükséges a fiókhoz."; +"FollowTheStepsFromDuoToFinishLoggingIn" = "Kövessük a Duo lépéseit a bejelentkezés befejezéséhez."; +"LaunchDuo" = "Duo indítása"; +"OrganizationUnassignedItemsMessageUSEUDescriptionLong" = "Megjegyzés: A nem hozzá nem rendelt szervezeti elemek már nem láthatók az Összes széf nézetben és csak az Adminisztrátori konzolon keresztül érhetők el. Rendeljük ezeket az elemeket egy gyűjteményhez az Adminisztrátor konzolból, hogy láthatóvá tegyük azokat."; +"OrganizationUnassignedItemsMessageSelfHost041624DescriptionLong" = "2024. május 16-tól a nem hozzá rendelt szervezeti elemek többé nem lesznek láthatók az Összes széf nézetben és csak az Adminisztrátori konzolon keresztül érhetők el. Rendeljük ezeket az elemeket egy gyűjteményhez az Adminisztrátori konzolból, hogy láthatóvá tegyük azokat."; +"RemindMeLater" = "Emlékeztetés később"; +"Notice" = "Megjegyzés"; \ No newline at end of file diff --git a/BitwardenShared/UI/Platform/Application/Support/Localizations/id.lproj/Localizable.strings b/BitwardenShared/UI/Platform/Application/Support/Localizations/id.lproj/Localizable.strings index ef92d8b95..557999505 100644 --- a/BitwardenShared/UI/Platform/Application/Support/Localizations/id.lproj/Localizable.strings +++ b/BitwardenShared/UI/Platform/Application/Support/Localizations/id.lproj/Localizable.strings @@ -864,4 +864,11 @@ "AccountLoggedOutBiometricExceeded" = "Account logged out."; "YourOrganizationPermissionsWereUpdatedRequeringYouToSetAMasterPassword" = "Your organization permissions were updated, requiring you to set a master password."; "YourOrganizationRequiresYouToSetAMasterPassword" = "Your organization requires you to set a master password."; -"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Set up an unlock option to change your vault timeout action."; \ No newline at end of file +"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Set up an unlock option to change your vault timeout action."; +"DuoTwoStepLoginIsRequiredForYourAccount" = "Duo two-step login is required for your account."; +"FollowTheStepsFromDuoToFinishLoggingIn" = "Follow the steps from Duo to finish logging in."; +"LaunchDuo" = "Launch Duo"; +"OrganizationUnassignedItemsMessageUSEUDescriptionLong" = "Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."; +"OrganizationUnassignedItemsMessageSelfHost041624DescriptionLong" = "On May 16, 2024, unassigned organization items will no longer be visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."; +"RemindMeLater" = "Remind me later"; +"Notice" = "Notice"; \ No newline at end of file diff --git a/BitwardenShared/UI/Platform/Application/Support/Localizations/it.lproj/Localizable.strings b/BitwardenShared/UI/Platform/Application/Support/Localizations/it.lproj/Localizable.strings index d2419d713..575a444fb 100644 --- a/BitwardenShared/UI/Platform/Application/Support/Localizations/it.lproj/Localizable.strings +++ b/BitwardenShared/UI/Platform/Application/Support/Localizations/it.lproj/Localizable.strings @@ -12,14 +12,14 @@ "Credits" = "Crediti"; "Delete" = "Elimina"; "Deleting" = "Eliminazione in corso..."; -"DoYouReallyWantToDelete" = "Vuoi davvero eliminarlo? Questa operazione non può essere annullata."; +"DoYouReallyWantToDelete" = "Sei sicuro di volerlo eliminare? Questa operazione è irreversibile."; "Edit" = "Modifica"; "EditFolder" = "Modifica cartella"; "Email" = "Email"; "EmailAddress" = "Indirizzo email"; "EmailUs" = "Contattaci"; -"EmailUsDescription" = "Mandaci un'email per ottenere aiuto o lasciare un feedback"; -"EnterPIN" = "Digita il tuo PIN."; +"EmailUsDescription" = "Inviaci un'email per ottenere aiuto o lasciare un feedback."; +"EnterPIN" = "Inserisci il tuo PIN."; "Favorites" = "Preferiti"; "FileBugReport" = "Segnala un bug"; "FileBugReportDescription" = "Segnala un problema nella nostra repository su GitHub."; @@ -864,4 +864,11 @@ "AccountLoggedOutBiometricExceeded" = "Account uscito."; "YourOrganizationPermissionsWereUpdatedRequeringYouToSetAMasterPassword" = "Le autorizzazioni della tua organizzazione sono state aggiornate, obbligandoti a impostare una password principale."; "YourOrganizationRequiresYouToSetAMasterPassword" = "La tua organizzazione ti obbliga di impostare di una password principale."; -"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Imposta un metodo di sblocco per modificare l'azione timeout cassaforte."; \ No newline at end of file +"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Imposta un metodo di sblocco per modificare l'azione timeout cassaforte."; +"DuoTwoStepLoginIsRequiredForYourAccount" = "Per il tuo account è richiesta la verifica in due passaggi di Duo."; +"FollowTheStepsFromDuoToFinishLoggingIn" = "Segui i passaggi da Duo per completare l'accesso."; +"LaunchDuo" = "Avvia Duo"; +"OrganizationUnassignedItemsMessageUSEUDescriptionLong" = "Gli elementi dell'organizzazione non assegnati non sono più visibili nella visualizzazione Tutte le Cassaforti su tutti i dispositivi e sono ora accessibili solo tramite la Console di amministrazione. Assegna questi elementi ad una raccolta dalla Console di amministrazione per renderli visibili."; +"OrganizationUnassignedItemsMessageSelfHost041624DescriptionLong" = "Dal 16 maggio 2024, gli elementi dell'organizzazione non assegnati non saranno più visibili nella visualizzazione Tutte le Cassaforti su tutti i dispositivi e saranno accessibili solo tramite la Console di amministrazione. Assegna questi elementi ad una raccolta dalla Console di amministrazione per renderli visibili."; +"RemindMeLater" = "Ricordamelo più tardi"; +"Notice" = "Avviso"; \ No newline at end of file diff --git a/BitwardenShared/UI/Platform/Application/Support/Localizations/ja.lproj/Localizable.strings b/BitwardenShared/UI/Platform/Application/Support/Localizations/ja.lproj/Localizable.strings index 2e7b3bb97..fb0d907ac 100644 --- a/BitwardenShared/UI/Platform/Application/Support/Localizations/ja.lproj/Localizable.strings +++ b/BitwardenShared/UI/Platform/Application/Support/Localizations/ja.lproj/Localizable.strings @@ -864,4 +864,11 @@ "AccountLoggedOutBiometricExceeded" = "アカウントからログアウトしました。"; "YourOrganizationPermissionsWereUpdatedRequeringYouToSetAMasterPassword" = "組織の権限が更新され、マスターパスワードの設定が必要になりました。"; "YourOrganizationRequiresYouToSetAMasterPassword" = "あなたの組織では、マスターパスワードの設定が義務付けられています。"; -"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "保管庫のタイムアウト動作を変更するには、ロック解除方法を設定してください。"; \ No newline at end of file +"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "保管庫のタイムアウト動作を変更するには、ロック解除方法を設定してください。"; +"DuoTwoStepLoginIsRequiredForYourAccount" = "アカウントには Duo 二段階認証が必要です。"; +"FollowTheStepsFromDuoToFinishLoggingIn" = "Duo の手順に従ってログインを完了してください。"; +"LaunchDuo" = "Duo を起動"; +"OrganizationUnassignedItemsMessageUSEUDescriptionLong" = "割り当てられていない組織アイテムはすべての保管庫ビューに表示されなくなり、管理コンソールからのみアクセス可能になります。 管理コンソールからコレクションにこれらのアイテムを割り当てると、表示できるようになります。"; +"OrganizationUnassignedItemsMessageSelfHost041624DescriptionLong" = "2024年5月16日に、 割り当てられていない組織アイテムはすべての保管庫ビューに表示されなくなり、管理コンソールからのみアクセス可能になります。 管理コンソールからコレクションにこれらのアイテムを割り当てると、表示できるようになります。"; +"RemindMeLater" = "後で再通知"; +"Notice" = "お知らせ"; \ No newline at end of file diff --git a/BitwardenShared/UI/Platform/Application/Support/Localizations/ka.lproj/Localizable.strings b/BitwardenShared/UI/Platform/Application/Support/Localizations/ka.lproj/Localizable.strings index 85904a1f2..ae6afd8e8 100644 --- a/BitwardenShared/UI/Platform/Application/Support/Localizations/ka.lproj/Localizable.strings +++ b/BitwardenShared/UI/Platform/Application/Support/Localizations/ka.lproj/Localizable.strings @@ -864,4 +864,11 @@ "AccountLoggedOutBiometricExceeded" = "Account logged out."; "YourOrganizationPermissionsWereUpdatedRequeringYouToSetAMasterPassword" = "Your organization permissions were updated, requiring you to set a master password."; "YourOrganizationRequiresYouToSetAMasterPassword" = "Your organization requires you to set a master password."; -"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Set up an unlock option to change your vault timeout action."; \ No newline at end of file +"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Set up an unlock option to change your vault timeout action."; +"DuoTwoStepLoginIsRequiredForYourAccount" = "Duo two-step login is required for your account."; +"FollowTheStepsFromDuoToFinishLoggingIn" = "Follow the steps from Duo to finish logging in."; +"LaunchDuo" = "Launch Duo"; +"OrganizationUnassignedItemsMessageUSEUDescriptionLong" = "Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."; +"OrganizationUnassignedItemsMessageSelfHost041624DescriptionLong" = "On May 16, 2024, unassigned organization items will no longer be visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."; +"RemindMeLater" = "Remind me later"; +"Notice" = "Notice"; \ No newline at end of file diff --git a/BitwardenShared/UI/Platform/Application/Support/Localizations/kn.lproj/Localizable.strings b/BitwardenShared/UI/Platform/Application/Support/Localizations/kn.lproj/Localizable.strings index c1e482487..9fe804325 100644 --- a/BitwardenShared/UI/Platform/Application/Support/Localizations/kn.lproj/Localizable.strings +++ b/BitwardenShared/UI/Platform/Application/Support/Localizations/kn.lproj/Localizable.strings @@ -864,4 +864,11 @@ "AccountLoggedOutBiometricExceeded" = "Account logged out."; "YourOrganizationPermissionsWereUpdatedRequeringYouToSetAMasterPassword" = "Your organization permissions were updated, requiring you to set a master password."; "YourOrganizationRequiresYouToSetAMasterPassword" = "Your organization requires you to set a master password."; -"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Set up an unlock option to change your vault timeout action."; \ No newline at end of file +"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Set up an unlock option to change your vault timeout action."; +"DuoTwoStepLoginIsRequiredForYourAccount" = "Duo two-step login is required for your account."; +"FollowTheStepsFromDuoToFinishLoggingIn" = "Follow the steps from Duo to finish logging in."; +"LaunchDuo" = "Launch Duo"; +"OrganizationUnassignedItemsMessageUSEUDescriptionLong" = "Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."; +"OrganizationUnassignedItemsMessageSelfHost041624DescriptionLong" = "On May 16, 2024, unassigned organization items will no longer be visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."; +"RemindMeLater" = "Remind me later"; +"Notice" = "Notice"; \ No newline at end of file diff --git a/BitwardenShared/UI/Platform/Application/Support/Localizations/ko.lproj/Localizable.strings b/BitwardenShared/UI/Platform/Application/Support/Localizations/ko.lproj/Localizable.strings index b98211877..86868e858 100644 --- a/BitwardenShared/UI/Platform/Application/Support/Localizations/ko.lproj/Localizable.strings +++ b/BitwardenShared/UI/Platform/Application/Support/Localizations/ko.lproj/Localizable.strings @@ -864,4 +864,11 @@ "AccountLoggedOutBiometricExceeded" = "Account logged out."; "YourOrganizationPermissionsWereUpdatedRequeringYouToSetAMasterPassword" = "Your organization permissions were updated, requiring you to set a master password."; "YourOrganizationRequiresYouToSetAMasterPassword" = "Your organization requires you to set a master password."; -"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Set up an unlock option to change your vault timeout action."; \ No newline at end of file +"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Set up an unlock option to change your vault timeout action."; +"DuoTwoStepLoginIsRequiredForYourAccount" = "Duo two-step login is required for your account."; +"FollowTheStepsFromDuoToFinishLoggingIn" = "Follow the steps from Duo to finish logging in."; +"LaunchDuo" = "Launch Duo"; +"OrganizationUnassignedItemsMessageUSEUDescriptionLong" = "Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."; +"OrganizationUnassignedItemsMessageSelfHost041624DescriptionLong" = "On May 16, 2024, unassigned organization items will no longer be visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."; +"RemindMeLater" = "Remind me later"; +"Notice" = "Notice"; \ No newline at end of file diff --git a/BitwardenShared/UI/Platform/Application/Support/Localizations/lt.lproj/Localizable.strings b/BitwardenShared/UI/Platform/Application/Support/Localizations/lt.lproj/Localizable.strings index ace1b455e..2b03c595f 100644 --- a/BitwardenShared/UI/Platform/Application/Support/Localizations/lt.lproj/Localizable.strings +++ b/BitwardenShared/UI/Platform/Application/Support/Localizations/lt.lproj/Localizable.strings @@ -249,7 +249,7 @@ "FeatureUnavailable" = "Funkcija nepasiekiama"; "MaxFileSize" = "Maximum file size is 100 MB."; "UpdateKey" = "Negalite naudoti šios funkcijos, kol neatnaujinsite šifravimo raktą."; -"EncryptionKeyMigrationRequiredDescriptionLong" = "Encryption key migration required. Please login through the web vault to update your encryption key."; +"EncryptionKeyMigrationRequiredDescriptionLong" = "Privaloma migruoti šifravimo raktą. Prašome prisijungti per internetinę saugyklą norint jį atnaujinti."; "LearnMore" = "Sužinoti daugiau"; "ApiUrl" = "API serverio nuoroda"; "CustomEnvironment" = "Individualizuota aplinka"; @@ -500,7 +500,7 @@ "DoYouReallyWantToPermanentlyDeleteCipher" = "Ar tikrai norite panaikinti? Atkurti nebebus galima."; "DoYouReallyWantToRestoreCipher" = "Ar tikrai norite atkurti šį elementą?"; "DoYouReallyWantToSoftDeleteCipher" = "Ar tikrai norite perkelti į šiukšlinę?"; -"AccountBiometricInvalidated" = "Biometric unlock for this account is disabled pending verification of master password."; +"AccountBiometricInvalidated" = "Biometrinis atrakinimas išjungtas, kol neįvesite pagrindinio slaptažodžio."; "AccountBiometricInvalidatedExtension" = "Autofill biometric unlock for this account is disabled pending verification of master password."; "EnableSyncOnRefresh" = "Leisti sinchronizuoti atnaujinant"; "EnableSyncOnRefreshDescription" = "Sinchronizuojama saugykla su perbraukimu iš viršaus į apačią gestu."; @@ -820,17 +820,17 @@ "ThereAreNoBlockedURIs" = "There are no blocked URIs"; "TheURIXIsAlreadyBlocked" = "The URI %1$@ is already blocked"; "CannotEditMultipleURIsAtOnce" = "Cannot edit multiple URIs at once"; -"LoginApproved" = "Login approved"; +"LoginApproved" = "Prisijungimas patvirtintas"; "LogInWithDeviceMustBeSetUpInTheSettingsOfTheBitwardenAppNeedAnotherOption" = "Log in with device must be set up in the settings of the Bitwarden app. Need another option?"; "LogInWithDevice" = "Log in with device"; -"LoggingInOn" = "Logging in on"; +"LoggingInOn" = "Prisijungiama prie"; "Vault" = "Vault"; -"Appearance" = "Appearance"; +"Appearance" = "Išvaizda"; "AccountSecurity" = "Account security"; "BitwardenHelpCenter" = "Bitwarden Help Center"; "ContactBitwardenSupport" = "Contact Bitwarden support"; "CopyAppInformation" = "Copy app information"; -"SyncNow" = "Sync now"; +"SyncNow" = "Sinchronizuoti dabar"; "UnlockOptions" = "Unlock options"; "SessionTimeout" = "Session timeout"; "SessionTimeoutAction" = "Session timeout action"; @@ -859,9 +859,16 @@ "LearnAboutOrganizationsDescriptionLong" = "Bitwarden allows you to share your vault items with others by using an organization. Learn more on the bitwarden.com website."; "RateAppDescriptionLong" = "Help others find out if Bitwarden is right for them. Visit the app store and leave a rating now."; "DefaultDarkThemeDescriptionLong" = "Choose the dark theme to use when your device’s dark mode is in use"; -"CreatedXY" = "Created %1$@, %2$@"; -"TooManyAttempts" = "Too many attempts"; -"AccountLoggedOutBiometricExceeded" = "Account logged out."; -"YourOrganizationPermissionsWereUpdatedRequeringYouToSetAMasterPassword" = "Your organization permissions were updated, requiring you to set a master password."; -"YourOrganizationRequiresYouToSetAMasterPassword" = "Your organization requires you to set a master password."; -"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Set up an unlock option to change your vault timeout action."; \ No newline at end of file +"CreatedXY" = "Sukurta %1$@, %2$@"; +"TooManyAttempts" = "Viršytas bandymų skaičius"; +"AccountLoggedOutBiometricExceeded" = "Atsijungta nuo paskyros."; +"YourOrganizationPermissionsWereUpdatedRequeringYouToSetAMasterPassword" = "Dėl pasikeitusių Jūsų organizacijos nuostatų, Jums reikia nustatyti pagrindinį slaptažodį."; +"YourOrganizationRequiresYouToSetAMasterPassword" = "Jūsų organizacijos nuostatos reikalauja Jus nustatyti pagrindinį slaptažodį."; +"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Nustatykite atrakinimo būdą, kad pakeistumėte Jūsų saugyklos laiko limito veiksmą."; +"DuoTwoStepLoginIsRequiredForYourAccount" = "Duo two-step login is required for your account."; +"FollowTheStepsFromDuoToFinishLoggingIn" = "Follow the steps from Duo to finish logging in."; +"LaunchDuo" = "Paleisti „Duo“"; +"OrganizationUnassignedItemsMessageUSEUDescriptionLong" = "Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."; +"OrganizationUnassignedItemsMessageSelfHost041624DescriptionLong" = "On May 16, 2024, unassigned organization items will no longer be visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."; +"RemindMeLater" = "Remind me later"; +"Notice" = "Notice"; \ No newline at end of file diff --git a/BitwardenShared/UI/Platform/Application/Support/Localizations/lv.lproj/Localizable.strings b/BitwardenShared/UI/Platform/Application/Support/Localizations/lv.lproj/Localizable.strings index 49aa4ef89..457795462 100644 --- a/BitwardenShared/UI/Platform/Application/Support/Localizations/lv.lproj/Localizable.strings +++ b/BitwardenShared/UI/Platform/Application/Support/Localizations/lv.lproj/Localizable.strings @@ -864,4 +864,11 @@ "AccountLoggedOutBiometricExceeded" = "Konts tika izrakstīts."; "YourOrganizationPermissionsWereUpdatedRequeringYouToSetAMasterPassword" = "Apvienības atļaujas tika atjauninātas, un tās pieprasa iestatīt galveno paroli."; "YourOrganizationRequiresYouToSetAMasterPassword" = "Apvienība pieprasa iestatīt galveno paroli."; -"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Jāuzstāda atslēgšanas iespēja, lai mainītu glabātavas noildzes darbību."; \ No newline at end of file +"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Jāuzstāda atslēgšanas iespēja, lai mainītu glabātavas noildzes darbību."; +"DuoTwoStepLoginIsRequiredForYourAccount" = "Kontam ir nepieciešama Duo divpakāpju pieteikšanās."; +"FollowTheStepsFromDuoToFinishLoggingIn" = "Jāseko Duo norādēm, lai pabeigtu pieteikšanos."; +"LaunchDuo" = "Palaist Duo"; +"OrganizationUnassignedItemsMessageUSEUDescriptionLong" = "Nepiešķirti apvienības vienumi vairs nav redzami skatā \"Visas glabātavas\" un ir sasniedzami tikai no pārvaldības konsoles, kur šie vienumi jāpiešķir krājumam, lai padarītu tos redzamus."; +"OrganizationUnassignedItemsMessageSelfHost041624DescriptionLong" = "No 2024. gada 16. maija nepiešķirti apvienības vienumi vairs nebūs redzami skatā \"Visas glabātavas\" un būs sasniedzami tikai no pārvaldības konsoles, kur šie vienumi jāpiešķir krājumam, lai padarītu tos redzamus."; +"RemindMeLater" = "Atgādināt man vēlāk"; +"Notice" = "Jāņem vērā"; \ No newline at end of file diff --git a/BitwardenShared/UI/Platform/Application/Support/Localizations/ml.lproj/Localizable.strings b/BitwardenShared/UI/Platform/Application/Support/Localizations/ml.lproj/Localizable.strings index b2b05d086..24d5cd84a 100644 --- a/BitwardenShared/UI/Platform/Application/Support/Localizations/ml.lproj/Localizable.strings +++ b/BitwardenShared/UI/Platform/Application/Support/Localizations/ml.lproj/Localizable.strings @@ -864,4 +864,11 @@ "AccountLoggedOutBiometricExceeded" = "Account logged out."; "YourOrganizationPermissionsWereUpdatedRequeringYouToSetAMasterPassword" = "Your organization permissions were updated, requiring you to set a master password."; "YourOrganizationRequiresYouToSetAMasterPassword" = "Your organization requires you to set a master password."; -"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Set up an unlock option to change your vault timeout action."; \ No newline at end of file +"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Set up an unlock option to change your vault timeout action."; +"DuoTwoStepLoginIsRequiredForYourAccount" = "Duo two-step login is required for your account."; +"FollowTheStepsFromDuoToFinishLoggingIn" = "Follow the steps from Duo to finish logging in."; +"LaunchDuo" = "Launch Duo"; +"OrganizationUnassignedItemsMessageUSEUDescriptionLong" = "Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."; +"OrganizationUnassignedItemsMessageSelfHost041624DescriptionLong" = "On May 16, 2024, unassigned organization items will no longer be visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."; +"RemindMeLater" = "Remind me later"; +"Notice" = "Notice"; \ No newline at end of file diff --git a/BitwardenShared/UI/Platform/Application/Support/Localizations/mr.lproj/Localizable.strings b/BitwardenShared/UI/Platform/Application/Support/Localizations/mr.lproj/Localizable.strings index b08be6f47..1f48e2315 100644 --- a/BitwardenShared/UI/Platform/Application/Support/Localizations/mr.lproj/Localizable.strings +++ b/BitwardenShared/UI/Platform/Application/Support/Localizations/mr.lproj/Localizable.strings @@ -864,4 +864,11 @@ "AccountLoggedOutBiometricExceeded" = "Account logged out."; "YourOrganizationPermissionsWereUpdatedRequeringYouToSetAMasterPassword" = "Your organization permissions were updated, requiring you to set a master password."; "YourOrganizationRequiresYouToSetAMasterPassword" = "Your organization requires you to set a master password."; -"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Set up an unlock option to change your vault timeout action."; \ No newline at end of file +"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Set up an unlock option to change your vault timeout action."; +"DuoTwoStepLoginIsRequiredForYourAccount" = "Duo two-step login is required for your account."; +"FollowTheStepsFromDuoToFinishLoggingIn" = "Follow the steps from Duo to finish logging in."; +"LaunchDuo" = "Launch Duo"; +"OrganizationUnassignedItemsMessageUSEUDescriptionLong" = "Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."; +"OrganizationUnassignedItemsMessageSelfHost041624DescriptionLong" = "On May 16, 2024, unassigned organization items will no longer be visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."; +"RemindMeLater" = "Remind me later"; +"Notice" = "Notice"; \ No newline at end of file diff --git a/BitwardenShared/UI/Platform/Application/Support/Localizations/my.lproj/Localizable.strings b/BitwardenShared/UI/Platform/Application/Support/Localizations/my.lproj/Localizable.strings index 85904a1f2..ae6afd8e8 100644 --- a/BitwardenShared/UI/Platform/Application/Support/Localizations/my.lproj/Localizable.strings +++ b/BitwardenShared/UI/Platform/Application/Support/Localizations/my.lproj/Localizable.strings @@ -864,4 +864,11 @@ "AccountLoggedOutBiometricExceeded" = "Account logged out."; "YourOrganizationPermissionsWereUpdatedRequeringYouToSetAMasterPassword" = "Your organization permissions were updated, requiring you to set a master password."; "YourOrganizationRequiresYouToSetAMasterPassword" = "Your organization requires you to set a master password."; -"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Set up an unlock option to change your vault timeout action."; \ No newline at end of file +"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Set up an unlock option to change your vault timeout action."; +"DuoTwoStepLoginIsRequiredForYourAccount" = "Duo two-step login is required for your account."; +"FollowTheStepsFromDuoToFinishLoggingIn" = "Follow the steps from Duo to finish logging in."; +"LaunchDuo" = "Launch Duo"; +"OrganizationUnassignedItemsMessageUSEUDescriptionLong" = "Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."; +"OrganizationUnassignedItemsMessageSelfHost041624DescriptionLong" = "On May 16, 2024, unassigned organization items will no longer be visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."; +"RemindMeLater" = "Remind me later"; +"Notice" = "Notice"; \ No newline at end of file diff --git a/BitwardenShared/UI/Platform/Application/Support/Localizations/nb.lproj/Localizable.strings b/BitwardenShared/UI/Platform/Application/Support/Localizations/nb.lproj/Localizable.strings index 159c6f2e9..082c47c4d 100644 --- a/BitwardenShared/UI/Platform/Application/Support/Localizations/nb.lproj/Localizable.strings +++ b/BitwardenShared/UI/Platform/Application/Support/Localizations/nb.lproj/Localizable.strings @@ -864,4 +864,11 @@ "AccountLoggedOutBiometricExceeded" = "Konto logget ut."; "YourOrganizationPermissionsWereUpdatedRequeringYouToSetAMasterPassword" = "Your organization permissions were updated, requiring you to set a master password."; "YourOrganizationRequiresYouToSetAMasterPassword" = "Your organization requires you to set a master password."; -"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Set up an unlock option to change your vault timeout action."; \ No newline at end of file +"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Set up an unlock option to change your vault timeout action."; +"DuoTwoStepLoginIsRequiredForYourAccount" = "Duo two-step login is required for your account."; +"FollowTheStepsFromDuoToFinishLoggingIn" = "Follow the steps from Duo to finish logging in."; +"LaunchDuo" = "Launch Duo"; +"OrganizationUnassignedItemsMessageUSEUDescriptionLong" = "Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."; +"OrganizationUnassignedItemsMessageSelfHost041624DescriptionLong" = "On May 16, 2024, unassigned organization items will no longer be visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."; +"RemindMeLater" = "Remind me later"; +"Notice" = "Notice"; \ No newline at end of file diff --git a/BitwardenShared/UI/Platform/Application/Support/Localizations/ne.lproj/Localizable.strings b/BitwardenShared/UI/Platform/Application/Support/Localizations/ne.lproj/Localizable.strings index 85904a1f2..ae6afd8e8 100644 --- a/BitwardenShared/UI/Platform/Application/Support/Localizations/ne.lproj/Localizable.strings +++ b/BitwardenShared/UI/Platform/Application/Support/Localizations/ne.lproj/Localizable.strings @@ -864,4 +864,11 @@ "AccountLoggedOutBiometricExceeded" = "Account logged out."; "YourOrganizationPermissionsWereUpdatedRequeringYouToSetAMasterPassword" = "Your organization permissions were updated, requiring you to set a master password."; "YourOrganizationRequiresYouToSetAMasterPassword" = "Your organization requires you to set a master password."; -"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Set up an unlock option to change your vault timeout action."; \ No newline at end of file +"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Set up an unlock option to change your vault timeout action."; +"DuoTwoStepLoginIsRequiredForYourAccount" = "Duo two-step login is required for your account."; +"FollowTheStepsFromDuoToFinishLoggingIn" = "Follow the steps from Duo to finish logging in."; +"LaunchDuo" = "Launch Duo"; +"OrganizationUnassignedItemsMessageUSEUDescriptionLong" = "Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."; +"OrganizationUnassignedItemsMessageSelfHost041624DescriptionLong" = "On May 16, 2024, unassigned organization items will no longer be visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."; +"RemindMeLater" = "Remind me later"; +"Notice" = "Notice"; \ No newline at end of file diff --git a/BitwardenShared/UI/Platform/Application/Support/Localizations/nl.lproj/Localizable.strings b/BitwardenShared/UI/Platform/Application/Support/Localizations/nl.lproj/Localizable.strings index 1c5d2eee3..f076d9907 100644 --- a/BitwardenShared/UI/Platform/Application/Support/Localizations/nl.lproj/Localizable.strings +++ b/BitwardenShared/UI/Platform/Application/Support/Localizations/nl.lproj/Localizable.strings @@ -68,7 +68,7 @@ "URI" = "URI"; "UseFingerprintToUnlock" = "Vingerafdruk gebruiken om te ontgrendelen"; "Username" = "Gebruikersnaam"; -"ValidationFieldRequired" = "Het %1$@-veld is vereist."; +"ValidationFieldRequired" = "Vul het veld %1$@ in."; "ValueHasBeenCopied" = "%1$@ is gekopieerd."; "VerifyFingerprint" = "Vingerafdruk verifiëren"; "VerifyMasterPassword" = "Hoofdwachtwoord invoeren"; @@ -249,7 +249,7 @@ "FeatureUnavailable" = "Functionaliteit niet beschikbaar"; "MaxFileSize" = "De maximale bestandsgrootte is 100 MB."; "UpdateKey" = "Je kunt deze functie pas gebruiken als je je encryptiesleutel bijwerkt."; -"EncryptionKeyMigrationRequiredDescriptionLong" = "Encryption key migration required. Please login through the web vault to update your encryption key."; +"EncryptionKeyMigrationRequiredDescriptionLong" = "Migratie van de encryptiesleutel vereist. Login via de website om je encryptiesleutel bij te werken."; "LearnMore" = "Meer informatie"; "ApiUrl" = "API server-URL"; "CustomEnvironment" = "Aangepaste omgeving"; @@ -265,7 +265,7 @@ "CustomFields" = "Aangepaste velden"; "CopyNumber" = "Nummer kopiëren"; "CopySecurityCode" = "Beveiligingscode kopiëren"; -"Number" = "Kaartummer"; +"Number" = "Kaartnummer"; "SecurityCode" = "Beveiligingscode"; "TypeCard" = "Kaart"; "TypeIdentity" = "Identiteit"; @@ -782,12 +782,12 @@ "Region" = "Regio"; "UpdateWeakMasterPasswordWarning" = "Je hoofdwachtwoord voldoet niet aan en of meerdere oganisatiebeleidsonderdelen. Om toegang te krijgen tot de kluis, moet je je hoofdwachtwoord nu bijwerken. Doorgaan zal je huidige sessie uitloggen, waarna je opnieuw moet inloggen. Actieve sessies op andere apparaten blijven mogelijk nog een uur actief."; "CurrentMasterPassword" = "Huidig hoofdwachtwoord"; -"LoggedIn" = "Logged in!"; -"ApproveWithMyOtherDevice" = "Approve with my other device"; -"RequestAdminApproval" = "Request admin approval"; -"ApproveWithMasterPassword" = "Approve with master password"; -"TurnOffUsingPublicDevice" = "Turn off using a public device"; -"RememberThisDevice" = "Remember this device"; +"LoggedIn" = "Ingelogd!"; +"ApproveWithMyOtherDevice" = "Goedkeuren met mijn andere apparaat"; +"RequestAdminApproval" = "Goedkeuring van beheerder vragen"; +"ApproveWithMasterPassword" = "Goedkeuren met hoofdwachtwoord"; +"TurnOffUsingPublicDevice" = "Uitschakelen met een openbaar apparaat"; +"RememberThisDevice" = "Dit apparaat onthouden"; "Passkey" = "Passkey"; "Passkeys" = "Passkeys"; "Application" = "Applicatie"; @@ -797,15 +797,15 @@ "CopyApplication" = "Applicatie kopiëren"; "AvailableForTwoStepLogin" = "Beschikbaar voor tweestapsaanmelding"; "MasterPasswordRePromptHelp" = "Hulptekst hoofdwachtwoord opnieuw vragen"; -"UnlockingMayFailDueToInsufficientMemoryDecreaseYourKDFMemorySettingsToResolve" = "Unlocking may fail due to insufficient memory. Decrease your KDF memory settings or set up biometric unlock to resolve."; +"UnlockingMayFailDueToInsufficientMemoryDecreaseYourKDFMemorySettingsToResolve" = "Ontgrendelen kan mislukken als er onvoldoende geheugen is. Verminder je KDF-geheugeninstellingen of stel biometrische ontgrendeling in om dit op te lossen."; "InvalidAPIKey" = "Ongeldige API-sleutel"; "InvalidAPIToken" = "Ongeldige API-token"; -"AdminApprovalRequested" = "Admin approval requested"; -"YourRequestHasBeenSentToYourAdmin" = "Your request has been sent to your admin."; -"YouWillBeNotifiedOnceApproved" = "You will be notified once approved."; -"TroubleLoggingIn" = "Trouble logging in?"; -"LoggingInAsX" = "Logging in as %1$@"; -"VaultTimeoutActionChangedToLogOut" = "Vault timeout action changed to log out"; +"AdminApprovalRequested" = "Goedkeuring van beheerder aangevraagd"; +"YourRequestHasBeenSentToYourAdmin" = "Je verzoek is naar je beheerder verstuurd."; +"YouWillBeNotifiedOnceApproved" = "Je krijgt een melding zodra je bent goedgekeurd."; +"TroubleLoggingIn" = "Problemen met inloggen?"; +"LoggingInAsX" = "Inloggen als %1$@"; +"VaultTimeoutActionChangedToLogOut" = "Kluis time-out actie gewijzigd naar uitloggen"; "BlockAutoFill" = "Automatisch aanvullen blokkeren"; "AutoFillWillNotBeOfferedForTheseURIs" = "Automatisch is uitgeschakeld voor deze URIs."; "NewBlockedURI" = "Nieuwe geblokkeerde URI"; @@ -820,10 +820,10 @@ "ThereAreNoBlockedURIs" = "Er zijn geen geblokkeerde URIs"; "TheURIXIsAlreadyBlocked" = "De URI %1$@ is al geblokkeerd"; "CannotEditMultipleURIsAtOnce" = "Meerdere URIs in één keer bewerken kan niet"; -"LoginApproved" = "Login approved"; -"LogInWithDeviceMustBeSetUpInTheSettingsOfTheBitwardenAppNeedAnotherOption" = "Log in with device must be set up in the settings of the Bitwarden app. Need another option?"; -"LogInWithDevice" = "Log in with device"; -"LoggingInOn" = "Logging in on"; +"LoginApproved" = "Inloggen goedgekeurd"; +"LogInWithDeviceMustBeSetUpInTheSettingsOfTheBitwardenAppNeedAnotherOption" = "Je moet Inloggen met apparaat instellen in de instellingen van de Bitwarden-app. Behoefte aan een andere mogelijkheid?"; +"LogInWithDevice" = "Inloggen met apparaat"; +"LoggingInOn" = "Inloggen op"; "Vault" = "Kluis"; "Appearance" = "Weergave"; "AccountSecurity" = "Accountbeveiliging"; @@ -860,8 +860,15 @@ "RateAppDescriptionLong" = "Help anderen met beslissen of Bitwarden iets voor hen is. Bezoek de app store en laat een beoordeling achter."; "DefaultDarkThemeDescriptionLong" = "Kies het donkere thema dat je wilt gebruiken wanneer de donkere modus van je apparaat actief is"; "CreatedXY" = "Aangemaakt %1$@, %2$@"; -"TooManyAttempts" = "Too many attempts"; -"AccountLoggedOutBiometricExceeded" = "Account logged out."; -"YourOrganizationPermissionsWereUpdatedRequeringYouToSetAMasterPassword" = "Your organization permissions were updated, requiring you to set a master password."; -"YourOrganizationRequiresYouToSetAMasterPassword" = "Your organization requires you to set a master password."; -"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Stel een ontgrendelingsmethode in om je kluis time-out actie te wijzigen."; \ No newline at end of file +"TooManyAttempts" = "Te veel pogingen"; +"AccountLoggedOutBiometricExceeded" = "Account uitgelogd."; +"YourOrganizationPermissionsWereUpdatedRequeringYouToSetAMasterPassword" = "De organisatierechten zijn bijgewerkt, je moet een hoofdwachtwoord instellen."; +"YourOrganizationRequiresYouToSetAMasterPassword" = "Je organisatie vereist dat je een hoofdwachtwoord instelt."; +"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Stel een ontgrendelingsmethode in om je kluis time-out actie te wijzigen."; +"DuoTwoStepLoginIsRequiredForYourAccount" = "Jouw account vereist Duo-tweestapsaanmelding."; +"FollowTheStepsFromDuoToFinishLoggingIn" = "Volg de stappen van Duo om in te loggen."; +"LaunchDuo" = "Duo starten"; +"OrganizationUnassignedItemsMessageUSEUDescriptionLong" = "Niet-toegewezen organisatie-items zijn niet meer zichtbaar in de weergave van alle kluisjes en alleen toegankelijk via de Admin Console. Je kunt deze items in het Admin Console aan een collectie toewijzen om ze zichtbaar te maken."; +"OrganizationUnassignedItemsMessageSelfHost041624DescriptionLong" = "Vanaf 2 mei 2024 zijn niet-toegewezen organisatie-items niet meer zichtbaar in de weergave van alle kluisjes en alleen toegankelijk via de Admin Console. Je kunt deze items in het Admin Console aan een collectie toewijzen om ze zichtbaar te maken."; +"RemindMeLater" = "Herinner me later"; +"Notice" = "Kennisgeving"; \ No newline at end of file diff --git a/BitwardenShared/UI/Platform/Application/Support/Localizations/nn.lproj/Localizable.strings b/BitwardenShared/UI/Platform/Application/Support/Localizations/nn.lproj/Localizable.strings index b3d423903..6d33854e5 100644 --- a/BitwardenShared/UI/Platform/Application/Support/Localizations/nn.lproj/Localizable.strings +++ b/BitwardenShared/UI/Platform/Application/Support/Localizations/nn.lproj/Localizable.strings @@ -864,4 +864,11 @@ "AccountLoggedOutBiometricExceeded" = "Account logged out."; "YourOrganizationPermissionsWereUpdatedRequeringYouToSetAMasterPassword" = "Your organization permissions were updated, requiring you to set a master password."; "YourOrganizationRequiresYouToSetAMasterPassword" = "Your organization requires you to set a master password."; -"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Set up an unlock option to change your vault timeout action."; \ No newline at end of file +"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Set up an unlock option to change your vault timeout action."; +"DuoTwoStepLoginIsRequiredForYourAccount" = "Duo two-step login is required for your account."; +"FollowTheStepsFromDuoToFinishLoggingIn" = "Follow the steps from Duo to finish logging in."; +"LaunchDuo" = "Launch Duo"; +"OrganizationUnassignedItemsMessageUSEUDescriptionLong" = "Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."; +"OrganizationUnassignedItemsMessageSelfHost041624DescriptionLong" = "På 16. mai 2024 vil ikkje utildelte organisasjonselement vere synlege lenger i Alle kvelv-visninga og vil berre vere tilgjengelege frå Admin-konsollen. Tildel desse elementa til ei samling frå Admin-konsollen for å gjere dei synlege."; +"RemindMeLater" = "Remind me later"; +"Notice" = "Notice"; \ No newline at end of file diff --git a/BitwardenShared/UI/Platform/Application/Support/Localizations/or.lproj/Localizable.strings b/BitwardenShared/UI/Platform/Application/Support/Localizations/or.lproj/Localizable.strings index 05ce7e5d6..46db4b463 100644 --- a/BitwardenShared/UI/Platform/Application/Support/Localizations/or.lproj/Localizable.strings +++ b/BitwardenShared/UI/Platform/Application/Support/Localizations/or.lproj/Localizable.strings @@ -864,4 +864,11 @@ "AccountLoggedOutBiometricExceeded" = "Account logged out."; "YourOrganizationPermissionsWereUpdatedRequeringYouToSetAMasterPassword" = "Your organization permissions were updated, requiring you to set a master password."; "YourOrganizationRequiresYouToSetAMasterPassword" = "Your organization requires you to set a master password."; -"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Set up an unlock option to change your vault timeout action."; \ No newline at end of file +"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Set up an unlock option to change your vault timeout action."; +"DuoTwoStepLoginIsRequiredForYourAccount" = "Duo two-step login is required for your account."; +"FollowTheStepsFromDuoToFinishLoggingIn" = "Follow the steps from Duo to finish logging in."; +"LaunchDuo" = "Launch Duo"; +"OrganizationUnassignedItemsMessageUSEUDescriptionLong" = "Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."; +"OrganizationUnassignedItemsMessageSelfHost041624DescriptionLong" = "On May 16, 2024, unassigned organization items will no longer be visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."; +"RemindMeLater" = "Remind me later"; +"Notice" = "Notice"; \ No newline at end of file diff --git a/BitwardenShared/UI/Platform/Application/Support/Localizations/pl.lproj/Localizable.strings b/BitwardenShared/UI/Platform/Application/Support/Localizations/pl.lproj/Localizable.strings index 706f010b3..3a4e235d4 100644 --- a/BitwardenShared/UI/Platform/Application/Support/Localizations/pl.lproj/Localizable.strings +++ b/BitwardenShared/UI/Platform/Application/Support/Localizations/pl.lproj/Localizable.strings @@ -843,7 +843,7 @@ "AutofillServicesExplanationLong" = "Android Autofill Framework jest używany do uzupełniania danych logowania w innych aplikacjach na twoim urządzeniu."; "UseInlineAutofillExplanationLong" = "Użyj bezpośredniego autouzupełniania, jeśli wybrana klawiatura je obsługuje. W przeciwnym razie użyj domyślnej nakładki."; "AdditionalOptions" = "Dodatkowe opcje"; -"ContinueToWebApp" = "Kontynuować przez aplikację internetową?"; +"ContinueToWebApp" = "Kontynuować do aplikacji internetowej?"; "ContinueToX" = "Kontynuować przez %1$@?"; "ContinueToHelpCenter" = "Kontynuować do centrum pomocy?"; "ContinueToContactSupport" = "Kontynuować kontakt z pomocą techniczną?"; @@ -864,4 +864,11 @@ "AccountLoggedOutBiometricExceeded" = "Konto wylogowane."; "YourOrganizationPermissionsWereUpdatedRequeringYouToSetAMasterPassword" = "Uprawnienia w Twojej organizacji zostały zaktualizowane, musisz teraz ustawić hasło główne."; "YourOrganizationRequiresYouToSetAMasterPassword" = "Twoja organizacja wymaga ustawienia hasła głównego."; -"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Ustaw opcje odblokowania, aby zmienić czas blokowania sejfu."; \ No newline at end of file +"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Ustaw opcje odblokowania, aby zmienić czas blokowania sejfu."; +"DuoTwoStepLoginIsRequiredForYourAccount" = "Dwustopniowe logowanie Duo jest wymagane dla Twojego konta."; +"FollowTheStepsFromDuoToFinishLoggingIn" = "Wykonaj kroki od Duo, aby zakończyć logowanie."; +"LaunchDuo" = "Uruchom Duo"; +"OrganizationUnassignedItemsMessageUSEUDescriptionLong" = "Nieprzypisane elementy w organizacji nie są już widoczne w widoku Wszystkie sejfy i są dostępne tylko przez Konsolę Administracyjną. Przypisz te elementy do kolekcji z konsoli administracyjnej, aby były one widoczne."; +"OrganizationUnassignedItemsMessageSelfHost041624DescriptionLong" = "16 maja 2024 r. nieprzypisane elementy w organizacji nie będą już widoczne w widoku Wszystkie sejfy i będą dostępne tylko przez Konsolę Administracyjną. Przypisz te elementy do kolekcji z konsoli administracyjnej, aby były one widoczne."; +"RemindMeLater" = "Przypomnij mi później"; +"Notice" = "Zawiadomienie"; \ No newline at end of file diff --git a/BitwardenShared/UI/Platform/Application/Support/Localizations/pt-BR.lproj/Localizable.strings b/BitwardenShared/UI/Platform/Application/Support/Localizations/pt-BR.lproj/Localizable.strings index 550a490c2..f57e38ec9 100644 --- a/BitwardenShared/UI/Platform/Application/Support/Localizations/pt-BR.lproj/Localizable.strings +++ b/BitwardenShared/UI/Platform/Application/Support/Localizations/pt-BR.lproj/Localizable.strings @@ -847,21 +847,28 @@ "ContinueToX" = "Continuar para %1$@?"; "ContinueToHelpCenter" = "Continuar para o centro de ajuda?"; "ContinueToContactSupport" = "Continuar e contatar o suporte?"; -"ContinueToPrivacyPolicy" = "Continue to privacy policy?"; +"ContinueToPrivacyPolicy" = "Continuar para a política de privacidade?"; "ContinueToAppStore" = "Continuar para a loja de apps?"; -"TwoStepLoginDescriptionLong" = "Make your account more secure by setting up two-step login in the Bitwarden web app."; -"ChangeMasterPasswordDescriptionLong" = "You can change your master password on the Bitwarden web app."; +"TwoStepLoginDescriptionLong" = "Torne sua conta mais segura configurando o login em duas etapas no aplicativo web do Bitwarden."; +"ChangeMasterPasswordDescriptionLong" = "Você pode alterar a sua senha mestra no aplicativo web Bitwarden."; "YouCanImportDataToYourVaultOnX" = "Você pode importar dados para o seu cofre no %1$@."; "LearnMoreAboutHowToUseBitwardenOnTheHelpCenter" = "Saiba mais sobre como usar o Bitwarden no centro de ajuda."; -"ContactSupportDescriptionLong" = "Can’t find what you are looking for? Reach out to Bitwarden support on bitwarden.com."; -"PrivacyPolicyDescriptionLong" = "Check out our privacy policy on bitwarden.com."; -"ExploreMoreFeaturesOfYourBitwardenAccountOnTheWebApp" = "Explore more features of your Bitwarden account on the web app."; -"LearnAboutOrganizationsDescriptionLong" = "Bitwarden allows you to share your vault items with others by using an organization. Learn more on the bitwarden.com website."; -"RateAppDescriptionLong" = "Help others find out if Bitwarden is right for them. Visit the app store and leave a rating now."; +"ContactSupportDescriptionLong" = "Não encontrou o que está procurando? Entre em contato com o suporte do Bitwarden em bitwarden.com."; +"PrivacyPolicyDescriptionLong" = "Confira a nossa política de privacidade em bitwarden.com."; +"ExploreMoreFeaturesOfYourBitwardenAccountOnTheWebApp" = "Explore mais recursos da sua conta no Bitwarden no aplicativo web."; +"LearnAboutOrganizationsDescriptionLong" = "O Bitwarden permite compartilhar os seus itens do cofre com outros ao utilizar uma organização. Gostaria de visitar o site bitwarden.com para saber mais?"; +"RateAppDescriptionLong" = "Ajude outros a descobrir se o Bitwarden é adequado para eles. Visite a loja de aplicativos e deixe uma avaliação agora."; "DefaultDarkThemeDescriptionLong" = "Usar o tema escuro quando o modo escuro do seu dispositivo estiver ativado"; "CreatedXY" = "Criado em %1$@, %2$@"; "TooManyAttempts" = "Tentativas demais"; "AccountLoggedOutBiometricExceeded" = "Conta desconectada."; -"YourOrganizationPermissionsWereUpdatedRequeringYouToSetAMasterPassword" = "Your organization permissions were updated, requiring you to set a master password."; -"YourOrganizationRequiresYouToSetAMasterPassword" = "Your organization requires you to set a master password."; -"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Set up an unlock option to change your vault timeout action."; \ No newline at end of file +"YourOrganizationPermissionsWereUpdatedRequeringYouToSetAMasterPassword" = "As permissões da sua organização foram atualizadas, exigindo que você defina uma senha mestra."; +"YourOrganizationRequiresYouToSetAMasterPassword" = "Sua organização requer que você defina uma senha mestra."; +"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Configure um método de desbloqueio para alterar o tempo limite do cofre."; +"DuoTwoStepLoginIsRequiredForYourAccount" = "A autenticação em duas etapas do Duo é necessária para sua conta."; +"FollowTheStepsFromDuoToFinishLoggingIn" = "Siga os passos no Duo para finalizar o login."; +"LaunchDuo" = "Abrir o Duo"; +"OrganizationUnassignedItemsMessageUSEUDescriptionLong" = "Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."; +"OrganizationUnassignedItemsMessageSelfHost041624DescriptionLong" = "On May 16, 2024, unassigned organization items will no longer be visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."; +"RemindMeLater" = "Remind me later"; +"Notice" = "Notice"; \ No newline at end of file diff --git a/BitwardenShared/UI/Platform/Application/Support/Localizations/pt-PT.lproj/Localizable.strings b/BitwardenShared/UI/Platform/Application/Support/Localizations/pt-PT.lproj/Localizable.strings index bfd9e2c8d..4d863a09e 100644 --- a/BitwardenShared/UI/Platform/Application/Support/Localizations/pt-PT.lproj/Localizable.strings +++ b/BitwardenShared/UI/Platform/Application/Support/Localizations/pt-PT.lproj/Localizable.strings @@ -448,7 +448,7 @@ "PINRequireMasterPasswordRestart" = "Pretende exigir o desbloqueio com a sua palavra-passe mestra quando a aplicação é reiniciada?"; "Black" = "Preto"; "Nord" = "Nord"; -"SolarizedDark" = "Solarized Dark"; +"SolarizedDark" = "Solarized (escuro)"; "AutofillBlockedUris" = "Preencher automaticamente URIs bloqueados"; "AskToAddLogin" = "Pedir para adicionar credencial"; "AskToAddLoginDescription" = "Pedir para adicionar um item se não o encontrar no seu cofre."; @@ -849,7 +849,7 @@ "ContinueToContactSupport" = "Continuar a contactar o apoio ao cliente?"; "ContinueToPrivacyPolicy" = "Continuar para a política de privacidade?"; "ContinueToAppStore" = "Continuar para a loja de aplicações?"; -"TwoStepLoginDescriptionLong" = "Torne a sua conta mais segura configurando a verificação em dois passos na aplicação Web Bitwarden."; +"TwoStepLoginDescriptionLong" = "Torne a sua conta mais segura configurando a verificação de dois passos na aplicação Web Bitwarden."; "ChangeMasterPasswordDescriptionLong" = "Pode alterar a sua palavra-passe mestra na aplicação Web Bitwarden."; "YouCanImportDataToYourVaultOnX" = "Pode importar dados para o seu cofre em %1$@."; "LearnMoreAboutHowToUseBitwardenOnTheHelpCenter" = "Saiba mais sobre como utilizar o Bitwarden no Centro de ajuda."; @@ -864,4 +864,11 @@ "AccountLoggedOutBiometricExceeded" = "Conta com sessão terminada."; "YourOrganizationPermissionsWereUpdatedRequeringYouToSetAMasterPassword" = "As permissões da sua organização foram atualizadas, exigindo a definição de uma palavra-passe mestra."; "YourOrganizationRequiresYouToSetAMasterPassword" = "A sua organização exige a definição de uma palavra-passe mestra."; -"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Configure uma opção de desbloqueio para alterar a ação de tempo limite do seu cofre."; \ No newline at end of file +"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Configure uma opção de desbloqueio para alterar a ação de tempo limite do seu cofre."; +"DuoTwoStepLoginIsRequiredForYourAccount" = "A verificação de dois passos Duo é necessária para a sua conta."; +"FollowTheStepsFromDuoToFinishLoggingIn" = "Siga os passos do Duo para concluir o início de sessão."; +"LaunchDuo" = "Iniciar o Duo"; +"OrganizationUnassignedItemsMessageUSEUDescriptionLong" = "Os itens da organização não atribuídos já não são visíveis na vista Todos os cofres e só são acessíveis através da consola de administração. Atribua estes itens a uma coleção a partir da Consola de administração para os tornar visíveis."; +"OrganizationUnassignedItemsMessageSelfHost041624DescriptionLong" = "A 16 de maio de 2024, os itens da organização não atribuídos deixarão de estar visíveis na vista Todos os cofres e só estarão acessíveis através da Consola de administração. Atribua estes itens a uma coleção a partir da Consola de administração para os tornar visíveis."; +"RemindMeLater" = "Lembrar-me mais tarde"; +"Notice" = "Aviso"; \ No newline at end of file diff --git a/BitwardenShared/UI/Platform/Application/Support/Localizations/ro.lproj/Localizable.strings b/BitwardenShared/UI/Platform/Application/Support/Localizations/ro.lproj/Localizable.strings index 01ad1af3f..24162830f 100644 --- a/BitwardenShared/UI/Platform/Application/Support/Localizations/ro.lproj/Localizable.strings +++ b/BitwardenShared/UI/Platform/Application/Support/Localizations/ro.lproj/Localizable.strings @@ -864,4 +864,11 @@ "AccountLoggedOutBiometricExceeded" = "Account logged out."; "YourOrganizationPermissionsWereUpdatedRequeringYouToSetAMasterPassword" = "Your organization permissions were updated, requiring you to set a master password."; "YourOrganizationRequiresYouToSetAMasterPassword" = "Your organization requires you to set a master password."; -"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Set up an unlock option to change your vault timeout action."; \ No newline at end of file +"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Set up an unlock option to change your vault timeout action."; +"DuoTwoStepLoginIsRequiredForYourAccount" = "Duo two-step login is required for your account."; +"FollowTheStepsFromDuoToFinishLoggingIn" = "Follow the steps from Duo to finish logging in."; +"LaunchDuo" = "Launch Duo"; +"OrganizationUnassignedItemsMessageUSEUDescriptionLong" = "Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."; +"OrganizationUnassignedItemsMessageSelfHost041624DescriptionLong" = "On May 16, 2024, unassigned organization items will no longer be visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."; +"RemindMeLater" = "Remind me later"; +"Notice" = "Notice"; \ No newline at end of file diff --git a/BitwardenShared/UI/Platform/Application/Support/Localizations/ru.lproj/Localizable.strings b/BitwardenShared/UI/Platform/Application/Support/Localizations/ru.lproj/Localizable.strings index 523390ca7..8a46d7dd1 100644 --- a/BitwardenShared/UI/Platform/Application/Support/Localizations/ru.lproj/Localizable.strings +++ b/BitwardenShared/UI/Platform/Application/Support/Localizations/ru.lproj/Localizable.strings @@ -788,12 +788,12 @@ "ApproveWithMasterPassword" = "Одобрить с мастер-паролем"; "TurnOffUsingPublicDevice" = "Выключить с помощью публичного устройства"; "RememberThisDevice" = "Запомнить это устройство"; -"Passkey" = "Ключ доступа"; -"Passkeys" = "Ключи доступа"; +"Passkey" = "Passkey"; +"Passkeys" = "Passkeys"; "Application" = "Приложение"; -"YouCannotEditPasskeyApplicationBecauseItWouldInvalidateThePasskey" = "Редактирование ключа доступа невозможно, поскольку это приведет к его аннулированию"; -"PasskeyWillNotBeCopied" = "Ключ доступа не будет скопирован"; -"ThePasskeyWillNotBeCopiedToTheClonedItemDoYouWantToContinueCloningThisItem" = "Ключ доступа не будет скопирован в клонированный элемент. Продолжить клонирование этого элемента?"; +"YouCannotEditPasskeyApplicationBecauseItWouldInvalidateThePasskey" = "Редактирование passkey невозможно, поскольку это приведет к его аннулированию"; +"PasskeyWillNotBeCopied" = "Passkey не будет скопирован"; +"ThePasskeyWillNotBeCopiedToTheClonedItemDoYouWantToContinueCloningThisItem" = "Passkey не будет скопирован в клонированный элемент. Продолжить клонирование этого элемента?"; "CopyApplication" = "Скопировать приложение"; "AvailableForTwoStepLogin" = "Доступно для двухэтапной аутентификации"; "MasterPasswordRePromptHelp" = "Помощь по повторному запросу мастер-пароля"; @@ -864,4 +864,11 @@ "AccountLoggedOutBiometricExceeded" = "Аккаунт не авторизован."; "YourOrganizationPermissionsWereUpdatedRequeringYouToSetAMasterPassword" = "Права доступа организации были обновлены, требуется установить мастер-пароль."; "YourOrganizationRequiresYouToSetAMasterPassword" = "Необходимо установить мастер-пароль для организации."; -"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Настройте опцию разблокировки для изменения действия по тайм-ауту хранилища."; \ No newline at end of file +"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Настройте опцию разблокировки для изменения действия по тайм-ауту хранилища."; +"DuoTwoStepLoginIsRequiredForYourAccount" = "Для вашего аккаунта требуется двухэтапная аутентификация Duo."; +"FollowTheStepsFromDuoToFinishLoggingIn" = "Следуйте указаниям Duo, чтобы завершить авторизацию."; +"LaunchDuo" = "Запустить Duo"; +"OrganizationUnassignedItemsMessageUSEUDescriptionLong" = "Уведомление: неприсвоенные элементы организации больше не видны в представлении \"Все хранилища\" и доступны только через консоль администратора. Назначьте эти элементы коллекции в консоли администратора, чтобы сделать их видимыми."; +"OrganizationUnassignedItemsMessageSelfHost041624DescriptionLong" = "16 мая 2024 года неприсвоенные элементы организации больше не будут видны в представлении \"Все хранилища\" и будут доступны только через консоль администратора. Назначьте эти элементы коллекции в консоли администратора, чтобы сделать их видимыми."; +"RemindMeLater" = "Напомнить позже"; +"Notice" = "Уведомление"; \ No newline at end of file diff --git a/BitwardenShared/UI/Platform/Application/Support/Localizations/si.lproj/Localizable.strings b/BitwardenShared/UI/Platform/Application/Support/Localizations/si.lproj/Localizable.strings index 20386f5bd..940190b59 100644 --- a/BitwardenShared/UI/Platform/Application/Support/Localizations/si.lproj/Localizable.strings +++ b/BitwardenShared/UI/Platform/Application/Support/Localizations/si.lproj/Localizable.strings @@ -864,4 +864,11 @@ "AccountLoggedOutBiometricExceeded" = "Account logged out."; "YourOrganizationPermissionsWereUpdatedRequeringYouToSetAMasterPassword" = "Your organization permissions were updated, requiring you to set a master password."; "YourOrganizationRequiresYouToSetAMasterPassword" = "Your organization requires you to set a master password."; -"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Set up an unlock option to change your vault timeout action."; \ No newline at end of file +"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Set up an unlock option to change your vault timeout action."; +"DuoTwoStepLoginIsRequiredForYourAccount" = "Duo two-step login is required for your account."; +"FollowTheStepsFromDuoToFinishLoggingIn" = "Follow the steps from Duo to finish logging in."; +"LaunchDuo" = "Launch Duo"; +"OrganizationUnassignedItemsMessageUSEUDescriptionLong" = "Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."; +"OrganizationUnassignedItemsMessageSelfHost041624DescriptionLong" = "On May 16, 2024, unassigned organization items will no longer be visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."; +"RemindMeLater" = "Remind me later"; +"Notice" = "Notice"; \ No newline at end of file diff --git a/BitwardenShared/UI/Platform/Application/Support/Localizations/sk.lproj/Localizable.strings b/BitwardenShared/UI/Platform/Application/Support/Localizations/sk.lproj/Localizable.strings index 52c67946e..f374de679 100644 --- a/BitwardenShared/UI/Platform/Application/Support/Localizations/sk.lproj/Localizable.strings +++ b/BitwardenShared/UI/Platform/Application/Support/Localizations/sk.lproj/Localizable.strings @@ -864,4 +864,11 @@ "AccountLoggedOutBiometricExceeded" = "Účet bol odhlásený."; "YourOrganizationPermissionsWereUpdatedRequeringYouToSetAMasterPassword" = "Povolenia vašej organizácie boli aktualizované, čo si vyžaduje nastavenie hlavného hesla."; "YourOrganizationRequiresYouToSetAMasterPassword" = "Vaša organizácia vyžaduje, aby ste nastavili hlavné heslo."; -"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Nastavte možnosť odomknutia, aby ste zmenili akciu pri vypršaní času trezoru."; \ No newline at end of file +"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Nastavte možnosť odomknutia, aby ste zmenili akciu pri vypršaní času trezoru."; +"DuoTwoStepLoginIsRequiredForYourAccount" = "Pre váš účet je potrebné dvojstupňové prihlásenie Duo."; +"FollowTheStepsFromDuoToFinishLoggingIn" = "Postupujte podľa krokov z aplikácie Duo a dokončite prihlasovanie."; +"LaunchDuo" = "Spustiť Duo"; +"OrganizationUnassignedItemsMessageUSEUDescriptionLong" = "Nepriradené položky organizácie už nie sú viditeľné v zobrazení Všetky Trezory a sú prístupné len cez administrátorskú konzolu. Aby boli viditeľné, priraďte tieto položky do kolekcie z konzoly administrátora."; +"OrganizationUnassignedItemsMessageSelfHost041624DescriptionLong" = "16. mája nepriradené položky organizácie už nebudú viditeľné v zobrazení Všetky Trezory a budú prístupné len cez administrátorskú konzolu. Aby boli viditeľné, priraďte tieto položky do kolekcie z konzoly administrátora."; +"RemindMeLater" = "Pripomenúť neskôr"; +"Notice" = "Upozornenie"; \ No newline at end of file diff --git a/BitwardenShared/UI/Platform/Application/Support/Localizations/sl.lproj/Localizable.strings b/BitwardenShared/UI/Platform/Application/Support/Localizations/sl.lproj/Localizable.strings index fd6998e51..60ff4ba80 100644 --- a/BitwardenShared/UI/Platform/Application/Support/Localizations/sl.lproj/Localizable.strings +++ b/BitwardenShared/UI/Platform/Application/Support/Localizations/sl.lproj/Localizable.strings @@ -864,4 +864,11 @@ "AccountLoggedOutBiometricExceeded" = "Account logged out."; "YourOrganizationPermissionsWereUpdatedRequeringYouToSetAMasterPassword" = "Your organization permissions were updated, requiring you to set a master password."; "YourOrganizationRequiresYouToSetAMasterPassword" = "Your organization requires you to set a master password."; -"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Set up an unlock option to change your vault timeout action."; \ No newline at end of file +"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Set up an unlock option to change your vault timeout action."; +"DuoTwoStepLoginIsRequiredForYourAccount" = "Duo two-step login is required for your account."; +"FollowTheStepsFromDuoToFinishLoggingIn" = "Follow the steps from Duo to finish logging in."; +"LaunchDuo" = "Launch Duo"; +"OrganizationUnassignedItemsMessageUSEUDescriptionLong" = "Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."; +"OrganizationUnassignedItemsMessageSelfHost041624DescriptionLong" = "On May 16, 2024, unassigned organization items will no longer be visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."; +"RemindMeLater" = "Remind me later"; +"Notice" = "Notice"; \ No newline at end of file diff --git a/BitwardenShared/UI/Platform/Application/Support/Localizations/sr.lproj/Localizable.strings b/BitwardenShared/UI/Platform/Application/Support/Localizations/sr.lproj/Localizable.strings index 40889f55a..3ed20064e 100644 --- a/BitwardenShared/UI/Platform/Application/Support/Localizations/sr.lproj/Localizable.strings +++ b/BitwardenShared/UI/Platform/Application/Support/Localizations/sr.lproj/Localizable.strings @@ -864,4 +864,11 @@ "AccountLoggedOutBiometricExceeded" = "Налог је одјављен."; "YourOrganizationPermissionsWereUpdatedRequeringYouToSetAMasterPassword" = "Дозволе за вашу организацију су ажуриране, што захтева да поставите главну лозинку."; "YourOrganizationRequiresYouToSetAMasterPassword" = "Ваша организација захтева да поставите главну лозинку."; -"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Подесите опцију откључавања да бисте променили радњу временског ограничења сефа."; \ No newline at end of file +"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Подесите опцију откључавања да бисте променили радњу временског ограничења сефа."; +"DuoTwoStepLoginIsRequiredForYourAccount" = "Duo пријава у два корака је потребна за ваш налог."; +"FollowTheStepsFromDuoToFinishLoggingIn" = "Пратите кораке од Duo да завршите пријављивање."; +"LaunchDuo" = "Покренути Duo"; +"OrganizationUnassignedItemsMessageUSEUDescriptionLong" = "Недодељене ставке организације више нису видљиве у приказу Сви сефови и доступне су само преко Админ конзоле. Доделите ове ставке колекцији са Админ конзолом да бисте их учинили видљивим."; +"OrganizationUnassignedItemsMessageSelfHost041624DescriptionLong" = "On May 16, 2024, unassigned organization items will no longer be visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."; +"RemindMeLater" = "Подсети ме касније"; +"Notice" = "Напомена"; \ No newline at end of file diff --git a/BitwardenShared/UI/Platform/Application/Support/Localizations/sv.lproj/Localizable.strings b/BitwardenShared/UI/Platform/Application/Support/Localizations/sv.lproj/Localizable.strings index a1486b84f..a36f785c7 100644 --- a/BitwardenShared/UI/Platform/Application/Support/Localizations/sv.lproj/Localizable.strings +++ b/BitwardenShared/UI/Platform/Application/Support/Localizations/sv.lproj/Localizable.strings @@ -864,4 +864,11 @@ "AccountLoggedOutBiometricExceeded" = "Kontot har loggats ut."; "YourOrganizationPermissionsWereUpdatedRequeringYouToSetAMasterPassword" = "Din organisations behörigheter uppdaterades, vilket kräver att du anger ett huvudlösenord."; "YourOrganizationRequiresYouToSetAMasterPassword" = "Din organisation kräver att du anger ett huvudlösenord."; -"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Ställ in ett upplåsningsalternativ för att ändra vad som händer när tidsgränsen uppnås."; \ No newline at end of file +"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Ställ in ett upplåsningsalternativ för att ändra vad som händer när tidsgränsen uppnås."; +"DuoTwoStepLoginIsRequiredForYourAccount" = "Duo tvåstegsverifiering krävs för ditt konto."; +"FollowTheStepsFromDuoToFinishLoggingIn" = "Följ stegen från Duo för att slutföra inloggningen."; +"LaunchDuo" = "Starta Duo"; +"OrganizationUnassignedItemsMessageUSEUDescriptionLong" = "Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."; +"OrganizationUnassignedItemsMessageSelfHost041624DescriptionLong" = "On May 16, 2024, unassigned organization items will no longer be visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."; +"RemindMeLater" = "Påminn mig senare"; +"Notice" = "Notice"; \ No newline at end of file diff --git a/BitwardenShared/UI/Platform/Application/Support/Localizations/ta.lproj/Localizable.strings b/BitwardenShared/UI/Platform/Application/Support/Localizations/ta.lproj/Localizable.strings index 66ca6249c..091678512 100644 --- a/BitwardenShared/UI/Platform/Application/Support/Localizations/ta.lproj/Localizable.strings +++ b/BitwardenShared/UI/Platform/Application/Support/Localizations/ta.lproj/Localizable.strings @@ -155,7 +155,7 @@ "PasswordHint" = "கடவுச்சொல் குறிப்பு"; "PasswordHintAlert" = "உங்கள் பிரதான கடவுச்சொல் குறிப்பைக் கொண்ட மின்னஞ்சலை நாங்கள் உங்களுக்கு அனுப்பியுள்ளோம்."; "PasswordOverrideAlert" = "தற்போதைய கடவுச்சொல்லை மேலெழுத உறுதியா?"; -"PushNotificationAlert" = "push அறிவிப்புகள் பயன்படுத்தி Bitwarden உம் பெட்டகத்தை தானாக ஒத்திசைகிறது. சிறந்த சாத்தியமான அனுபவத்திற்கு, பின்வரும் தூண்டியில் push அறிவிப்புகளை இயக்கச்சொல்லி கேட்டால் \"அனுமதி\"ஐ தேர்ந்தெடுக்கவும்."; +"PushNotificationAlert" = "Push அறிவிப்புகள் பயன்படுத்தி Bitwarden உம் பெட்டகத்தை தானாக ஒத்திசைகிறது. சிறந்த சாத்தியமான அனுபவத்திற்கு, பின்வரும் தூண்டியில் push அறிவிப்புகளை இயக்கச்சொல்லி கேட்டால் \"அனுமதி\"ஐ தேர்ந்தெடுக்கவும்."; "RateTheApp" = "செயலியை மதிப்பிடுக"; "RateTheAppDescription" = "ஒரு நல்ல விமர்சனம் மூலம் எங்களுக்கு உதவ தயவுசெய்து கருதுங்கள்!"; "RegeneratePassword" = "கடவுச்சொல்லை மீண்டுமுருவாக்கு"; @@ -241,7 +241,7 @@ "PremiumRequired" = "இவ்வம்சத்தை பயன்படுத்த உயர்தர உறுப்பினர் தகுதி தேவை."; "AttachementAdded" = "இணைப்பு சேர்க்கப்பட்டது"; "AttachmentDeleted" = "இணைப்பு அழிக்கப்பட்டது"; -"ChooseFile" = "கோப்பை தேர்ந்தெடு"; +"ChooseFile" = "கோப்பைத் தேர்ந்தெடு"; "File" = "கோப்பு"; "NoFileChosen" = "எந்த கோப்பும் தேர்ந்தெடுக்கப்படவில்லை"; "NoAttachments" = "இணைப்புகள் ஏதுமில்லை."; @@ -276,7 +276,7 @@ "Address3" = "முகவரி ௩"; "April" = "ஏப்ரல்"; "August" = "ஆகஸ்ட்"; -"Brand" = "சூட்டுக்குறி"; +"Brand" = "வணிகக்குறி"; "CardholderName" = "அட்டைதாரரின் பெயர்"; "CityTown" = "மாநகரம் / நகரம்"; "Company" = "நிறுவனம்"; @@ -357,7 +357,7 @@ "YubiKeyInstructionIos" = "தொடர, உம் யீபிகீ NEOவை சாதனத்தின் பின்புறத்திற்கெதிரே பிடித்திரு."; "BitwardenAutofillAccessibilityServiceDescription2" = "செயலிகள் வழக்கமான தன்னிரப்பிச்சேவையை ஆதரிக்காதபோது அணுகல்தன்மை சேவை பயன்படுத்த உதவிகரமாகவிருக்கலாம்."; "DatePasswordUpdated" = "கடவுச்சொல் புதுப்பிக்கப்பட்டது"; -"DateUpdated" = "புதுப்பிக்கப்பட்டது"; +"DateUpdated" = "புதுப்பித்த நேரம்"; "AutofillActivated" = "தன்னிரப்பல் செயல்படுத்தப்பட்டது!"; "MustLogInMainAppAutofill" = "தன்னிரப்பலை பயன்படுத்தும் முன் நீங்கள் முதன்மை Bitwarden செயலியில் உள்நுழைய வேண்டும்."; "AutofillSetup" = "செயலிகள் மற்றும் வலைத்தளங்களில் உள்நுழையும்போது உம் உள்நுழைவுகளை இப்போது விசைப்பலகையிலிருந்து எளிதாக அணுக இயலும்."; @@ -463,7 +463,7 @@ "LoginExpired" = "உம் உள்நுழைவு அமர்வு காலாவதியானது."; "BiometricsDirection" = "உயிரியளவுச் சரிபார்ப்பு"; "Biometrics" = "உயிரியளவுகள்"; -"UseBiometricsToUnlock" = "உயிரியளவுகளைப் பயன்படுத்திப் பூட்டவிழ்"; +"UseBiometricsToUnlock" = "உயிரியளவுகளால் பூட்டவிழ்"; "AccessibilityOverlayPermissionAlert" = "Bitwardenக்கு கவனம் தேவை - Bitwarden அமைப்புகளிலிருந்து \"தன்னிரப்பி அணுகல்தன்மை சேவை\" ஐப் பார்"; "BitwardenAutofillServiceOverlayPermission" = "௩. Bitwarden-க்கான Android செயலி அமைப்புகள் திரையில், \"பிற ஆப்ஸின் மேலே காட்டு\" தெரிவுகளுக்கு (மேம்பட்டவை அடியில்) செல் மற்றும் மேலடுக்கு ஆதரவை இயக்க நிலைமாற்றியை தட்டுக."; "OverlayPermission" = "அனுமதி"; @@ -702,7 +702,7 @@ "ConfimLogInAttempForX" = "%1$@-க்கான உள்நுழைவு முயற்சியை உறுதுபடுத்து"; "AllNotifications" = "எல்லா அறிவிப்புகளும்"; "PasswordType" = "கடவுச்சொல் வகை"; -"WhatWouldYouLikeToGenerate" = "என்ன உருவாக்க விரும்புகிறீர்?"; +"WhatWouldYouLikeToGenerate" = "எதை உருவாக்க விரும்புகிறீர்?"; "UsernameType" = "பயனர்பெயர் வகை"; "PlusAddressedEmail" = "Plus addressed email"; "CatchAllEmail" = "Catch-all email"; @@ -864,4 +864,11 @@ "AccountLoggedOutBiometricExceeded" = "கணக்கிலிருந்து வெளியேறப்பட்டது."; "YourOrganizationPermissionsWereUpdatedRequeringYouToSetAMasterPassword" = "Your organization permissions were updated, requiring you to set a master password."; "YourOrganizationRequiresYouToSetAMasterPassword" = "Your organization requires you to set a master password."; -"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Set up an unlock option to change your vault timeout action."; \ No newline at end of file +"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Set up an unlock option to change your vault timeout action."; +"DuoTwoStepLoginIsRequiredForYourAccount" = "Duo two-step login is required for your account."; +"FollowTheStepsFromDuoToFinishLoggingIn" = "Follow the steps from Duo to finish logging in."; +"LaunchDuo" = "Launch Duo"; +"OrganizationUnassignedItemsMessageUSEUDescriptionLong" = "Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."; +"OrganizationUnassignedItemsMessageSelfHost041624DescriptionLong" = "On May 16, 2024, unassigned organization items will no longer be visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."; +"RemindMeLater" = "Remind me later"; +"Notice" = "Notice"; \ No newline at end of file diff --git a/BitwardenShared/UI/Platform/Application/Support/Localizations/te.lproj/Localizable.strings b/BitwardenShared/UI/Platform/Application/Support/Localizations/te.lproj/Localizable.strings index 85904a1f2..ae6afd8e8 100644 --- a/BitwardenShared/UI/Platform/Application/Support/Localizations/te.lproj/Localizable.strings +++ b/BitwardenShared/UI/Platform/Application/Support/Localizations/te.lproj/Localizable.strings @@ -864,4 +864,11 @@ "AccountLoggedOutBiometricExceeded" = "Account logged out."; "YourOrganizationPermissionsWereUpdatedRequeringYouToSetAMasterPassword" = "Your organization permissions were updated, requiring you to set a master password."; "YourOrganizationRequiresYouToSetAMasterPassword" = "Your organization requires you to set a master password."; -"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Set up an unlock option to change your vault timeout action."; \ No newline at end of file +"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Set up an unlock option to change your vault timeout action."; +"DuoTwoStepLoginIsRequiredForYourAccount" = "Duo two-step login is required for your account."; +"FollowTheStepsFromDuoToFinishLoggingIn" = "Follow the steps from Duo to finish logging in."; +"LaunchDuo" = "Launch Duo"; +"OrganizationUnassignedItemsMessageUSEUDescriptionLong" = "Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."; +"OrganizationUnassignedItemsMessageSelfHost041624DescriptionLong" = "On May 16, 2024, unassigned organization items will no longer be visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."; +"RemindMeLater" = "Remind me later"; +"Notice" = "Notice"; \ No newline at end of file diff --git a/BitwardenShared/UI/Platform/Application/Support/Localizations/th.lproj/Localizable.strings b/BitwardenShared/UI/Platform/Application/Support/Localizations/th.lproj/Localizable.strings index da8707c25..6281e174d 100644 --- a/BitwardenShared/UI/Platform/Application/Support/Localizations/th.lproj/Localizable.strings +++ b/BitwardenShared/UI/Platform/Application/Support/Localizations/th.lproj/Localizable.strings @@ -864,4 +864,11 @@ "AccountLoggedOutBiometricExceeded" = "Account logged out."; "YourOrganizationPermissionsWereUpdatedRequeringYouToSetAMasterPassword" = "Your organization permissions were updated, requiring you to set a master password."; "YourOrganizationRequiresYouToSetAMasterPassword" = "Your organization requires you to set a master password."; -"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Set up an unlock option to change your vault timeout action."; \ No newline at end of file +"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Set up an unlock option to change your vault timeout action."; +"DuoTwoStepLoginIsRequiredForYourAccount" = "Duo two-step login is required for your account."; +"FollowTheStepsFromDuoToFinishLoggingIn" = "Follow the steps from Duo to finish logging in."; +"LaunchDuo" = "Launch Duo"; +"OrganizationUnassignedItemsMessageUSEUDescriptionLong" = "Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."; +"OrganizationUnassignedItemsMessageSelfHost041624DescriptionLong" = "On May 16, 2024, unassigned organization items will no longer be visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."; +"RemindMeLater" = "Remind me later"; +"Notice" = "Notice"; \ No newline at end of file diff --git a/BitwardenShared/UI/Platform/Application/Support/Localizations/tr.lproj/Localizable.strings b/BitwardenShared/UI/Platform/Application/Support/Localizations/tr.lproj/Localizable.strings index bd7460ecd..561e7d6b7 100644 --- a/BitwardenShared/UI/Platform/Application/Support/Localizations/tr.lproj/Localizable.strings +++ b/BitwardenShared/UI/Platform/Application/Support/Localizations/tr.lproj/Localizable.strings @@ -864,4 +864,11 @@ "AccountLoggedOutBiometricExceeded" = "Hesabın oturumu kapatıldı."; "YourOrganizationPermissionsWereUpdatedRequeringYouToSetAMasterPassword" = "Kuruluş izinleriniz güncellendi ve bir ana parola belirlemeniz gerekiyor."; "YourOrganizationRequiresYouToSetAMasterPassword" = "Kuruluşunuz bir ana parola belirlemenizi gerektiriyor."; -"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Kasa zaman aşımı eyleminizi değiştirmek için bir kilit açma yöntemi ayarlayın."; \ No newline at end of file +"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Kasa zaman aşımı eyleminizi değiştirmek için bir kilit açma yöntemi ayarlayın."; +"DuoTwoStepLoginIsRequiredForYourAccount" = "Hesabınız için Duo'ya iki adımlı giriş yapmanız gerekiyor."; +"FollowTheStepsFromDuoToFinishLoggingIn" = "Oturum açmayı tamamlamak için Duo'daki adımları izleyin."; +"LaunchDuo" = "Duo'yu başlat"; +"OrganizationUnassignedItemsMessageUSEUDescriptionLong" = "Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."; +"OrganizationUnassignedItemsMessageSelfHost041624DescriptionLong" = "On May 16, 2024, unassigned organization items will no longer be visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."; +"RemindMeLater" = "Remind me later"; +"Notice" = "Notice"; \ No newline at end of file diff --git a/BitwardenShared/UI/Platform/Application/Support/Localizations/uk.lproj/Localizable.strings b/BitwardenShared/UI/Platform/Application/Support/Localizations/uk.lproj/Localizable.strings index 893c93daa..7ddf09751 100644 --- a/BitwardenShared/UI/Platform/Application/Support/Localizations/uk.lproj/Localizable.strings +++ b/BitwardenShared/UI/Platform/Application/Support/Localizations/uk.lproj/Localizable.strings @@ -724,7 +724,7 @@ "EmailType" = "Тип е-пошти"; "WebsiteRequired" = "Вебсайт (обов'язково)"; "UnknownXErrorMessage" = "Сталася невідома помилка %1$@."; -"PlusAddressedEmailDescription" = "Використовуйте розширені можливості адрес вашого постачальника електронної пошти"; +"PlusAddressedEmailDescription" = "Використовуйте розширені можливості адрес вашого провайдера електронної пошти"; "CatchAllEmailDescription" = "Використовуйте свою скриньку вхідних Catch-All власного домену."; "ForwardedEmailDescription" = "Згенеруйте псевдонім е-пошти зі стороннім сервісом пересилання."; "Random" = "Випадково"; @@ -864,4 +864,11 @@ "AccountLoggedOutBiometricExceeded" = "Ви вийшли з облікового запису."; "YourOrganizationPermissionsWereUpdatedRequeringYouToSetAMasterPassword" = "Оновлено дозволи вашої організації – вимагається встановлення головного пароля."; "YourOrganizationRequiresYouToSetAMasterPassword" = "Ваша організація вимагає, щоб ви встановили головний пароль."; -"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Налаштуйте спосіб розблокування, щоб змінити час очікування сховища."; \ No newline at end of file +"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Налаштуйте спосіб розблокування, щоб змінити час очікування сховища."; +"DuoTwoStepLoginIsRequiredForYourAccount" = "Для вашого облікового запису необхідна двоетапна перевірка з Duo."; +"FollowTheStepsFromDuoToFinishLoggingIn" = "Виконайте дії з Duo для завершення входу."; +"LaunchDuo" = "Запустити Duo"; +"OrganizationUnassignedItemsMessageUSEUDescriptionLong" = "Непризначені елементи організації більше не видимі у поданні \"Усі сховища\" і доступні лише в консолі адміністратора. Щоб зробити їх видимими, призначте ці елементи збірці в консолі адміністратора."; +"OrganizationUnassignedItemsMessageSelfHost041624DescriptionLong" = "16 травня 2024 року, непризначені елементи організації більше не будуть видимі в поданні \"Усі сховища\", і доступні лише через консоль адміністратора. Щоб зробити їх видимими, призначте ці елементи збірці в консолі адміністратора."; +"RemindMeLater" = "Нагадати пізніше"; +"Notice" = "Сповіщення"; \ No newline at end of file diff --git a/BitwardenShared/UI/Platform/Application/Support/Localizations/vi.lproj/Localizable.strings b/BitwardenShared/UI/Platform/Application/Support/Localizations/vi.lproj/Localizable.strings index c4cdde789..b02cee1d0 100644 --- a/BitwardenShared/UI/Platform/Application/Support/Localizations/vi.lproj/Localizable.strings +++ b/BitwardenShared/UI/Platform/Application/Support/Localizations/vi.lproj/Localizable.strings @@ -314,7 +314,7 @@ "ShowWebsiteIcons" = "Hiện biểu tượng trang web"; "ShowWebsiteIconsDescription" = "Hiện logo trang web bên cạnh mỗi đăng nhập."; "IconsUrl" = "Địa chỉ biểu tượng máy chủ"; -"AutofillWithBitwarden" = "Tự động điền với Bitwarden"; +"AutofillWithBitwarden" = "Tự động điền bằng Bitwarden"; "VaultIsLocked" = "Kho đã khóa"; "GoToMyVault" = "Đến kho của tôi"; "Collections" = "Các Bộ Sưu Tập"; @@ -864,4 +864,11 @@ "AccountLoggedOutBiometricExceeded" = "Đã đăng xuất tài khoản."; "YourOrganizationPermissionsWereUpdatedRequeringYouToSetAMasterPassword" = "Quyền tổ chức của bạn đã được cập nhật, yêu cầu bạn đặt mật khẩu chính."; "YourOrganizationRequiresYouToSetAMasterPassword" = "Tổ chức của bạn yêu cầu bạn đặt mật khẩu chính."; -"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Thiết lập khóa khi hết thời gian chờ kho."; \ No newline at end of file +"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "Thiết lập khóa khi hết thời gian chờ kho."; +"DuoTwoStepLoginIsRequiredForYourAccount" = "Tài khoản của bạn bắt buộc đăng nhập 2 bước Dou."; +"FollowTheStepsFromDuoToFinishLoggingIn" = "Làm theo các bước từ Dou để hoàn tất đăng nhập."; +"LaunchDuo" = "Khởi động Dou"; +"OrganizationUnassignedItemsMessageUSEUDescriptionLong" = "Các mục tổ chức chưa được chỉ định sẽ không còn hiển thị trong chế độ xem Toàn Bộ, mà chỉ có thể truy cập qua Bảng điều khiển dành cho quản trị viên. Để hiện thị, chỉ định các mục này cho một thư mục từ Bảng điều khiển dành cho quản trị viên."; +"OrganizationUnassignedItemsMessageSelfHost041624DescriptionLong" = "Vào ngày 16 tháng 5 năm 2024, các mục tổ chức chưa được chỉ định sẽ không còn hiển thị trong chế độ xem Toàn Bộ, mà chỉ có thể truy cập qua Bảng điều khiển dành cho quản trị viên. Để hiện thị, chỉ định các mục này cho một thư mục từ Bảng điều khiển dành cho quản trị viên."; +"RemindMeLater" = "Nhắc sau"; +"Notice" = "Lưu ý"; \ No newline at end of file diff --git a/BitwardenShared/UI/Platform/Application/Support/Localizations/zh-Hans.lproj/Localizable.strings b/BitwardenShared/UI/Platform/Application/Support/Localizations/zh-Hans.lproj/Localizable.strings index fd15b2441..de3ea0598 100644 --- a/BitwardenShared/UI/Platform/Application/Support/Localizations/zh-Hans.lproj/Localizable.strings +++ b/BitwardenShared/UI/Platform/Application/Support/Localizations/zh-Hans.lproj/Localizable.strings @@ -843,12 +843,12 @@ "AutofillServicesExplanationLong" = "Android 自动填充框架用于帮助将登录信息填充到您设备上的其他应用。"; "UseInlineAutofillExplanationLong" = "如果您所选的键盘支持,请使用内联自动填充。否则,请使用默认叠加层。"; "AdditionalOptions" = "附加选项"; -"ContinueToWebApp" = "接下来前往网页应用吗?"; -"ContinueToX" = "接下来前往 %1$@ 吗?"; -"ContinueToHelpCenter" = "接下来前往帮助中心吗?"; -"ContinueToContactSupport" = "接下来要联系支持吗?"; -"ContinueToPrivacyPolicy" = "接下来查看隐私政策吗?"; -"ContinueToAppStore" = "接下来前往 App Store 吗?"; +"ContinueToWebApp" = "前往网页 App 吗?"; +"ContinueToX" = "前往 %1$@ 吗?"; +"ContinueToHelpCenter" = "前往帮助中心吗?"; +"ContinueToContactSupport" = "要联系支持吗?"; +"ContinueToPrivacyPolicy" = "查看隐私政策吗?"; +"ContinueToAppStore" = "前往 App Store 吗?"; "TwoStepLoginDescriptionLong" = "通过在 Bitwarden 网页应用中设置两步登录,可以使您的账户更加安全。"; "ChangeMasterPasswordDescriptionLong" = "您可以在 Bitwarden 网页应用上更改您的主密码。"; "YouCanImportDataToYourVaultOnX" = "您可以将数据导入到 %1$@ 上的密码库。"; @@ -864,4 +864,11 @@ "AccountLoggedOutBiometricExceeded" = "账户已注销。"; "YourOrganizationPermissionsWereUpdatedRequeringYouToSetAMasterPassword" = "您的组织权限已更新,要求您设置主密码。"; "YourOrganizationRequiresYouToSetAMasterPassword" = "您的组织要求您设置主密码。"; -"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "设置解锁选项以更改您的密码库超时操作。"; \ No newline at end of file +"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "设置解锁选项以更改您的密码库超时动作。"; +"DuoTwoStepLoginIsRequiredForYourAccount" = "您的账户要求使用 Duo 两步登录。"; +"FollowTheStepsFromDuoToFinishLoggingIn" = "按照 Duo 的步骤完成登录。"; +"LaunchDuo" = "启动 Duo"; +"OrganizationUnassignedItemsMessageUSEUDescriptionLong" = "未分配的组织项目在「所有密码库」视图中不再可见,只能通过管理控制台访问。通过管理控制台将这些项目分配给集合以使其可见。"; +"OrganizationUnassignedItemsMessageSelfHost041624DescriptionLong" = "注意:从 2024 年 5 月 16 日起,未分配的组织项目在「所有密码库」视图中将不再可见,只能通过管理控制台访问。通过管理控制台将这些项目分配给集合以使其可见。"; +"RemindMeLater" = "稍后提醒我"; +"Notice" = "注意"; \ No newline at end of file diff --git a/BitwardenShared/UI/Platform/Application/Support/Localizations/zh-Hant.lproj/Localizable.strings b/BitwardenShared/UI/Platform/Application/Support/Localizations/zh-Hant.lproj/Localizable.strings index a5c731fce..b3e1dc348 100644 --- a/BitwardenShared/UI/Platform/Application/Support/Localizations/zh-Hant.lproj/Localizable.strings +++ b/BitwardenShared/UI/Platform/Application/Support/Localizations/zh-Hant.lproj/Localizable.strings @@ -864,4 +864,11 @@ "AccountLoggedOutBiometricExceeded" = "帳戶已登出。"; "YourOrganizationPermissionsWereUpdatedRequeringYouToSetAMasterPassword" = "您的組織權限已更新,需要您設定主密碼。"; "YourOrganizationRequiresYouToSetAMasterPassword" = "您的組織要求您設定主密碼。"; -"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "設定一個解鎖方式來變更您的密碼庫逾時動作。"; \ No newline at end of file +"SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" = "設定一個解鎖方式來變更您的密碼庫逾時動作。"; +"DuoTwoStepLoginIsRequiredForYourAccount" = "Duo two-step login is required for your account."; +"FollowTheStepsFromDuoToFinishLoggingIn" = "Follow the steps from Duo to finish logging in."; +"LaunchDuo" = "Launch Duo"; +"OrganizationUnassignedItemsMessageUSEUDescriptionLong" = "Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."; +"OrganizationUnassignedItemsMessageSelfHost041624DescriptionLong" = "On May 16, 2024, unassigned organization items will no longer be visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."; +"RemindMeLater" = "Remind me later"; +"Notice" = "Notice"; \ No newline at end of file diff --git a/BitwardenShared/UI/Platform/Application/Utilities/Alert/Alert/Alert.swift b/BitwardenShared/UI/Platform/Application/Utilities/Alert/Alert/Alert.swift index 06801606b..7fbbe6eb9 100644 --- a/BitwardenShared/UI/Platform/Application/Utilities/Alert/Alert/Alert.swift +++ b/BitwardenShared/UI/Platform/Application/Utilities/Alert/Alert/Alert.swift @@ -94,11 +94,13 @@ public class Alert { /// Creates a `UIAlertController` from the `Alert` that can be presented in the view. /// + /// - Parameter onDismissed: An optional closure that is called when the alert is dismissed. /// - Returns An initialized `UIAlertController` that has the `AlertAction`s added. /// @MainActor - func createAlertController() -> UIAlertController { - let alert = UIAlertController(title: title, message: message, preferredStyle: preferredStyle) + func createAlertController(onDismissed: (() -> Void)? = nil) -> UIAlertController { + let alert = AlertController(title: title, message: message, preferredStyle: preferredStyle) + alert.onDismissed = onDismissed alertTextFields.forEach { alertTextField in alert.addTextField { textField in textField.placeholder = alertTextField.placeholder @@ -166,3 +168,22 @@ extension Alert: Hashable { hasher.combine(title) } } + +// MARK: - AlertController + +/// An `UIAlertController` subclass that allows for setting a closure to be notified when the alert +/// controller is dismissed. +/// +private class AlertController: UIAlertController { + // MARK: Properties + + /// A closure that is called when the alert controller has been dismissed. + var onDismissed: (() -> Void)? + + // MARK: UIViewController + + override func viewDidDisappear(_ animated: Bool) { + super.viewDidDisappear(animated) + onDismissed?() + } +} diff --git a/BitwardenShared/UI/Platform/Application/Utilities/Alert/Alert/AlertPresentable.swift b/BitwardenShared/UI/Platform/Application/Utilities/Alert/Alert/AlertPresentable.swift index 9c4a4dee0..9d7f06ecc 100644 --- a/BitwardenShared/UI/Platform/Application/Utilities/Alert/Alert/AlertPresentable.swift +++ b/BitwardenShared/UI/Platform/Application/Utilities/Alert/Alert/AlertPresentable.swift @@ -1,3 +1,4 @@ +import OSLog import UIKit // MARK: - AlertPresentable @@ -14,6 +15,14 @@ public protocol AlertPresentable { /// - Parameter alert: The `Alert` used to create a `UIAlertController` to present. /// func present(_ alert: Alert) + + /// Presents a `UIAlertController` created from the `Alert` on the provided `rootViewController`. + /// + /// - Parameters: + /// - alert: The `Alert` used to create a `UIAlertController` to present. + /// - onDismissed: An optional closure that is called when the alert is dismissed. + /// + func present(_ alert: Alert, onDismissed: (() -> Void)?) } public extension AlertPresentable { @@ -22,9 +31,25 @@ public extension AlertPresentable { /// - Parameter alert: The `Alert` used to create a `UIAlertController` to present. /// func present(_ alert: Alert) { - let alertController = alert.createAlertController() + present(alert, onDismissed: nil) + } + + /// Presents a `UIAlertController` created from the `Alert` on the provided `rootViewController`. + /// + /// - Parameters: + /// - alert: The `Alert` used to create a `UIAlertController` to present. + /// - onDismissed: An optional closure that is called when the alert is dismissed. + /// + func present(_ alert: Alert, onDismissed: (() -> Void)?) { guard let parent = rootViewController?.topmostViewController() else { return } + // Prevent presenting alerts on alerts. + guard !(parent is UIAlertController) else { + Logger.application.error("⛔️ Error: attempted to present an alert on top of another alert!") + return + } + + let alertController = alert.createAlertController(onDismissed: onDismissed) if alert.preferredStyle == .actionSheet { // iPadOS requires an anchor for action sheets. This solution keeps the iPad app from crashing, and centers // the presentation of the action sheet. diff --git a/BitwardenShared/UI/Platform/Application/Utilities/Alert/Alert/AlertPresentableTests.swift b/BitwardenShared/UI/Platform/Application/Utilities/Alert/Alert/AlertPresentableTests.swift index a5cf435d9..50951f0e7 100644 --- a/BitwardenShared/UI/Platform/Application/Utilities/Alert/Alert/AlertPresentableTests.swift +++ b/BitwardenShared/UI/Platform/Application/Utilities/Alert/Alert/AlertPresentableTests.swift @@ -33,6 +33,17 @@ class AlertPresentableTests: BitwardenTestCase { subject.present(Alert(title: "🍎", message: "🥝", preferredStyle: .alert)) XCTAssertNotNil(rootViewController.presentedViewController as? UIAlertController) } + + /// `present(_:)` presents a `UIAlertController` and calls the `onDismissed` closure when it's been dismissed. + func test_present_onDismissed() { + var onDismissedCalled = false + subject.present(Alert(title: "🍎", message: "🥝", preferredStyle: .alert)) { + onDismissedCalled = true + } + rootViewController.dismiss(animated: false) + waitFor(rootViewController.presentedViewController == nil) + XCTAssertTrue(onDismissedCalled) + } } class AlertPresentableSubject: AlertPresentable { diff --git a/BitwardenShared/UI/Platform/Application/Utilities/Alert/Alert/AlertTests.swift b/BitwardenShared/UI/Platform/Application/Utilities/Alert/Alert/AlertTests.swift index e8e3835d5..3c45edd0d 100644 --- a/BitwardenShared/UI/Platform/Application/Utilities/Alert/Alert/AlertTests.swift +++ b/BitwardenShared/UI/Platform/Application/Utilities/Alert/Alert/AlertTests.swift @@ -61,6 +61,20 @@ class AlertTests: BitwardenTestCase { XCTAssertEqual(alertController.preferredAction?.title, "OK") } + /// `createAlertController` sets an `onDismissed` closure that's called when the alert is dismissed. + func test_createAlertController_onDismissed() { + var dismissedCalled = false + let alertController = subject.createAlertController { dismissedCalled = true } + let rootViewController = UIViewController() + setKeyWindowRoot(viewController: rootViewController) + + rootViewController.present(alertController, animated: false) + XCTAssertFalse(dismissedCalled) + rootViewController.dismiss(animated: false) + waitFor(rootViewController.presentedViewController == nil) + XCTAssertTrue(dismissedCalled) + } + /// `debugDescription` contains the alert's properties func test_debugDescription() { XCTAssertEqual( @@ -133,6 +147,7 @@ class AlertTests: BitwardenTestCase { ) let alert = Alert.moreOptions( cipherView: cipher, + hasMasterPassword: false, hasPremium: false, id: cipher.id!, showEdit: true, @@ -155,7 +170,7 @@ class AlertTests: BitwardenTestCase { await second.handler?(second, []) XCTAssertEqual( capturedAction, - .edit(cipherView: cipher) + .edit(cipherView: cipher, requiresMasterPasswordReprompt: false) ) capturedAction = nil @@ -212,6 +227,7 @@ class AlertTests: BitwardenTestCase { ) let alert = Alert.moreOptions( cipherView: cipher, + hasMasterPassword: false, hasPremium: false, id: cipher.id!, showEdit: true, @@ -234,7 +250,7 @@ class AlertTests: BitwardenTestCase { await second.handler?(second, []) XCTAssertEqual( capturedAction, - .edit(cipherView: cipher) + .edit(cipherView: cipher, requiresMasterPasswordReprompt: false) ) capturedAction = nil diff --git a/BitwardenShared/UI/Platform/Application/Utilities/AnyCoordinator.swift b/BitwardenShared/UI/Platform/Application/Utilities/AnyCoordinator.swift index 047c20b1c..126faf5c3 100644 --- a/BitwardenShared/UI/Platform/Application/Utilities/AnyCoordinator.swift +++ b/BitwardenShared/UI/Platform/Application/Utilities/AnyCoordinator.swift @@ -15,7 +15,7 @@ open class AnyCoordinator: Coordinator { private let doNavigate: (Route, AnyObject?) -> Void /// A closure that wraps the `showAlert(_:)` method. - private let doShowAlert: (Alert) -> Void + private let doShowAlert: (Alert, (() -> Void)?) -> Void /// A closure that wraps the `showLoadingOverlay(_:)` method. private let doShowLoadingOverlay: (LoadingOverlayState) -> Void @@ -42,7 +42,7 @@ open class AnyCoordinator: Coordinator { doNavigate = { route, context in coordinator.navigate(to: route, context: context) } - doShowAlert = { coordinator.showAlert($0) } + doShowAlert = { coordinator.showAlert($0, onDismissed: $1) } doShowLoadingOverlay = { coordinator.showLoadingOverlay($0) } doShowToast = { coordinator.showToast($0) } doStart = { coordinator.start() } @@ -58,8 +58,8 @@ open class AnyCoordinator: Coordinator { doNavigate(route, context) } - open func showAlert(_ alert: Alert) { - doShowAlert(alert) + open func showAlert(_ alert: Alert, onDismissed: (() -> Void)?) { + doShowAlert(alert, onDismissed) } open func showLoadingOverlay(_ state: LoadingOverlayState) { diff --git a/BitwardenShared/UI/Platform/Application/Utilities/Coordinator.swift b/BitwardenShared/UI/Platform/Application/Utilities/Coordinator.swift index b48071133..6a7fd36c3 100644 --- a/BitwardenShared/UI/Platform/Application/Utilities/Coordinator.swift +++ b/BitwardenShared/UI/Platform/Application/Utilities/Coordinator.swift @@ -34,6 +34,14 @@ public protocol Coordinator: AnyObject { /// func showAlert(_ alert: Alert) + /// Shows the alert. + /// + /// - Parameters: + /// - alert: The alert to show. + /// - onDismissed: An optional closure that is called when the alert is dismissed. + /// + func showAlert(_ alert: Alert, onDismissed: (() -> Void)?) + /// Shows the loading overlay view. /// /// - Parameter state: The state for configuring the loading overlay. @@ -118,6 +126,14 @@ public extension Coordinator { func navigate(to route: Route) { navigate(to: route, context: nil) } + + /// Shows the provided alert on the `stackNavigator`. + /// + /// - Parameter alert: The alert to show. + /// + func showAlert(_ alert: Alert) { + showAlert(alert, onDismissed: nil) + } } extension Coordinator where Self.Event == Void { @@ -137,10 +153,12 @@ extension Coordinator where Self: HasNavigator { /// Shows the provided alert on the `stackNavigator`. /// - /// - Parameter alert: The alert to show. + /// - Parameters: + /// - alert: The alert to show. + /// - onDismissed: An optional closure that is called when the alert is dismissed. /// - func showAlert(_ alert: Alert) { - navigator?.present(alert) + func showAlert(_ alert: Alert, onDismissed: (() -> Void)? = nil) { + navigator?.present(alert, onDismissed: onDismissed) } /// Shows the loading overlay view. diff --git a/BitwardenShared/UI/Platform/Application/Views/PasswordVisibilityButton.swift b/BitwardenShared/UI/Platform/Application/Views/PasswordVisibilityButton.swift index 1e1425332..2111577b0 100644 --- a/BitwardenShared/UI/Platform/Application/Views/PasswordVisibilityButton.swift +++ b/BitwardenShared/UI/Platform/Application/Views/PasswordVisibilityButton.swift @@ -6,6 +6,9 @@ import SwiftUI /// current password visibility. /// struct PasswordVisibilityButton: View { + /// The button's accessibility ID. + var accessibilityIdentifier: String + /// The accessibility label for this button. var accessibilityLabel: String @@ -28,12 +31,13 @@ struct PasswordVisibilityButton: View { .resizable() .frame(width: size, height: size) } - .accessibilityIdentifier("ShowValueButton") + .accessibilityIdentifier(accessibilityIdentifier) } /// Creates a new `PasswordVisibilityButton`. /// /// - Parameters: + /// - accessibilityIdentifier: The button's accessibility ID. /// - accessibilityLabel: The accessibility label for this button. Defaults to /// `Localizations.toggleVisibility`. /// - isPasswordVisible: A flag indicating if the password is visible. @@ -41,11 +45,13 @@ struct PasswordVisibilityButton: View { /// - action: The action that is triggered when the user interacts with this button. /// init( + accessibilityIdentifier: String = "", accessibilityLabel: String = Localizations.toggleVisibility, isPasswordVisible: Bool, size: CGFloat = 16, action: @escaping () -> Void ) { + self.accessibilityIdentifier = accessibilityIdentifier self.accessibilityLabel = accessibilityLabel self.action = action self.isPasswordVisible = isPasswordVisible diff --git a/BitwardenShared/UI/Platform/Extensions/Alert+Account.swift b/BitwardenShared/UI/Platform/Extensions/Alert+Account.swift index 38d767d58..b1a30730a 100644 --- a/BitwardenShared/UI/Platform/Extensions/Alert+Account.swift +++ b/BitwardenShared/UI/Platform/Extensions/Alert+Account.swift @@ -29,22 +29,6 @@ extension Alert { ) } - /// An alert notifying the user that a pending login request has been denied. - /// - /// - Parameter action: The action to perform when the user exits the alert. - /// - /// - Returns: An alert notifying the user that a pending login request has been denied. - /// - static func requestDenied(action: @escaping () async -> Void) -> Alert { - Alert( - title: Localizations.logInDenied, - message: nil, - alertActions: [ - AlertAction(title: Localizations.ok, style: .default) { _, _ in await action() }, - ] - ) - } - /// An alert notifying the user that a pending login request has expired. /// /// - Parameter action: The action to perform when the user exits the alert. diff --git a/BitwardenShared/UI/Platform/Settings/Extensions/Alert+Settings.swift b/BitwardenShared/UI/Platform/Settings/Extensions/Alert+Settings.swift index 977eb3799..53cb9d4f1 100644 --- a/BitwardenShared/UI/Platform/Settings/Extensions/Alert+Settings.swift +++ b/BitwardenShared/UI/Platform/Settings/Extensions/Alert+Settings.swift @@ -330,7 +330,8 @@ extension Alert { id: "otp", autocapitalizationType: .none, autocorrectionType: .no, - isSecureTextEntry: true + isSecureTextEntry: true, + keyboardType: .numberPad ), ] ) diff --git a/BitwardenShared/UI/Platform/Settings/Extensions/AlertSettingsTests.swift b/BitwardenShared/UI/Platform/Settings/Extensions/AlertSettingsTests.swift index 49c23f909..d3ef1d746 100644 --- a/BitwardenShared/UI/Platform/Settings/Extensions/AlertSettingsTests.swift +++ b/BitwardenShared/UI/Platform/Settings/Extensions/AlertSettingsTests.swift @@ -227,6 +227,7 @@ class AlertSettingsTests: BitwardenTestCase { XCTAssertEqual(subject.alertActions.last?.style, .cancel) var textField = try XCTUnwrap(subject.alertTextFields.first) + XCTAssertEqual(textField.keyboardType, .numberPad) textField = AlertTextField(id: "otp", text: "otp") let action = try XCTUnwrap(subject.alertActions.first(where: { $0.title == Localizations.submit })) diff --git a/BitwardenShared/UI/Vault/Extensions/Alert+Vault.swift b/BitwardenShared/UI/Vault/Extensions/Alert+Vault.swift index 7a444fefb..720115376 100644 --- a/BitwardenShared/UI/Vault/Extensions/Alert+Vault.swift +++ b/BitwardenShared/UI/Vault/Extensions/Alert+Vault.swift @@ -95,6 +95,7 @@ extension Alert { /// /// - Parameters: /// - cipherView: The cipher view to show. + /// - hasMasterPassword: Whether the user has a master password. /// - hasPremium: Whether the user has a premium account. /// - id: The id of the item. /// - showEdit: Whether to show the edit option (should be `false` for items in the trash). @@ -102,8 +103,9 @@ extension Alert { /// /// - Returns: An alert presenting the user with options to select an attachment type. @MainActor - static func moreOptions( // swiftlint:disable:this function_body_length + static func moreOptions( // swiftlint:disable:this function_body_length function_parameter_count cipherView: CipherView, + hasMasterPassword: Bool, hasPremium: Bool, id: String, showEdit: Bool, @@ -117,7 +119,10 @@ extension Alert { // Add the option to edit the cipher if desired. if showEdit { alertActions.append(AlertAction(title: Localizations.edit, style: .default) { _, _ in - await action(.edit(cipherView: cipherView)) + await action(.edit( + cipherView: cipherView, + requiresMasterPasswordReprompt: cipherView.reprompt == .password && hasMasterPassword + )) }) } @@ -129,7 +134,7 @@ extension Alert { await action(.copy( toast: Localizations.number, value: number, - requiresMasterPasswordReprompt: cipherView.reprompt == .password + requiresMasterPasswordReprompt: cipherView.reprompt == .password && hasMasterPassword )) }) } @@ -138,7 +143,7 @@ extension Alert { await action(.copy( toast: Localizations.securityCode, value: code, - requiresMasterPasswordReprompt: cipherView.reprompt == .password + requiresMasterPasswordReprompt: cipherView.reprompt == .password && hasMasterPassword )) }) } @@ -158,7 +163,7 @@ extension Alert { await action(.copy( toast: Localizations.password, value: password, - requiresMasterPasswordReprompt: cipherView.reprompt == .password + requiresMasterPasswordReprompt: cipherView.reprompt == .password && hasMasterPassword )) }) } @@ -166,7 +171,7 @@ extension Alert { alertActions.append(AlertAction(title: Localizations.copyTotp, style: .default) { _, _ in await action(.copyTotp( totpKey: totpKey, - requiresMasterPasswordReprompt: cipherView.reprompt == .password + requiresMasterPasswordReprompt: cipherView.reprompt == .password && hasMasterPassword )) }) } diff --git a/BitwardenShared/UI/Vault/Vault/VaultGroup/VaultGroupProcessor.swift b/BitwardenShared/UI/Vault/Vault/VaultGroup/VaultGroupProcessor.swift index 7e179cc00..64e24c966 100644 --- a/BitwardenShared/UI/Vault/Vault/VaultGroup/VaultGroupProcessor.swift +++ b/BitwardenShared/UI/Vault/Vault/VaultGroup/VaultGroupProcessor.swift @@ -240,10 +240,11 @@ final class VaultGroupProcessor: StateProcessor = .loading(nil) /// Whether the user has a master password. - var userHasMasterPassword = true + var hasMasterPassword = true /// A flag indicating if the user has premium features. var hasPremiumFeatures = false @@ -35,7 +35,7 @@ struct ViewItemState: Equatable { guard !hasVerifiedMasterPassword else { return false } return switch loadingState { case let .data(state): - state.isMasterPasswordRePromptOn + state.isMasterPasswordRePromptOn && hasMasterPassword case .loading: false } diff --git a/BitwardenShared/UI/Vault/VaultItem/ViewItem/ViewItemStateTests.swift b/BitwardenShared/UI/Vault/VaultItem/ViewItem/ViewItemStateTests.swift index 741232984..a2b5ed8e4 100644 --- a/BitwardenShared/UI/Vault/VaultItem/ViewItem/ViewItemStateTests.swift +++ b/BitwardenShared/UI/Vault/VaultItem/ViewItem/ViewItemStateTests.swift @@ -42,6 +42,24 @@ class ViewItemStateTests: BitwardenTestCase { XCTAssertFalse(subject.canClone) } + /// `isMasterPasswordRequired` is false when the user has no password. + func test_isMasterPasswordRequired_repromptOff_noPassword() { + let subject = ViewItemState( + loadingState: .data( + CipherItemState( + existing: .fixture( + id: "id", + reprompt: .password + ), + hasPremium: true + )! + ), + hasMasterPassword: false, + hasVerifiedMasterPassword: false + ) + XCTAssertFalse(subject.isMasterPasswordRequired) + } + /// `isMasterPasswordRequired` is true when the reprompt is on and the master password has not /// been verified yet. func test_isMasterPasswordRequired_repromptOn_unverifiedPassword() { diff --git a/BitwardenShared/UI/Vault/Views/VaultListItemRow/VaultListItemRowView.swift b/BitwardenShared/UI/Vault/Views/VaultListItemRow/VaultListItemRowView.swift index 7b624c40d..62cbc98a2 100644 --- a/BitwardenShared/UI/Vault/Views/VaultListItemRow/VaultListItemRowView.swift +++ b/BitwardenShared/UI/Vault/Views/VaultListItemRow/VaultListItemRowView.swift @@ -23,7 +23,6 @@ struct VaultListItemRowView: View { ) .imageStyle(.rowIcon) .padding(.vertical, 19) - .accessibilityHidden(true) HStack { switch store.state.item.itemType { @@ -113,24 +112,32 @@ struct VaultListItemRowView: View { /// @ViewBuilder private func decorativeImage(_ item: VaultListItem, iconBaseURL: URL?, showWebIcons: Bool) -> some View { - if showWebIcons, let loginView = item.loginView, let iconBaseURL { - AsyncImage( - url: IconImageHelper.getIconImage( - for: loginView, - from: iconBaseURL - ), - content: { image in - image - .resizable() - .scaledToFit() - }, - placeholder: { - placeholderDecorativeImage(item.icon) - } - ) - } else { - placeholderDecorativeImage(item.icon) + // The Group is needed so `.accessibilityHidden(false)` can be applied to this image wrapper. + // This allows automated tests to detect the image's accessibility ID even though the image itself + // is excluded from the accessibility tree. + Group { + if showWebIcons, let loginView = item.loginView, let iconBaseURL { + AsyncImage( + url: IconImageHelper.getIconImage( + for: loginView, + from: iconBaseURL + ), + content: { image in + image + .resizable() + .scaledToFit() + .accessibilityHidden(true) + }, + placeholder: { + placeholderDecorativeImage(item.icon) + } + ) + } else { + placeholderDecorativeImage(item.icon) + } } + .accessibilityIdentifier(item.iconAccessibilityId) + .accessibilityHidden(false) } /// The placeholder image for the decorative image. @@ -138,6 +145,7 @@ struct VaultListItemRowView: View { Image(decorative: icon) .resizable() .scaledToFit() + .accessibilityHidden(true) } /// The row showing the totp code. diff --git a/GlobalTestHelpers/MockCoordinator.swift b/GlobalTestHelpers/MockCoordinator.swift index d6b5126ea..7d7de4a30 100644 --- a/GlobalTestHelpers/MockCoordinator.swift +++ b/GlobalTestHelpers/MockCoordinator.swift @@ -8,6 +8,7 @@ enum MockCoordinatorError: Error { class MockCoordinator: Coordinator { var alertShown = [Alert]() + var alertOnDismissed: (() -> Void)? var contexts: [AnyObject?] = [] var events = [Event]() var isLoadingOverlayShowing = false @@ -30,8 +31,9 @@ class MockCoordinator: Coordinator { contexts.append(context) } - func showAlert(_ alert: Alert) { + func showAlert(_ alert: BitwardenShared.Alert, onDismissed: (() -> Void)?) { alertShown.append(alert) + alertOnDismissed = onDismissed } func showLoadingOverlay(_ state: LoadingOverlayState) { diff --git a/GlobalTestHelpers/MockStackNavigator.swift b/GlobalTestHelpers/MockStackNavigator.swift index aeb561fd2..fb76ff64c 100644 --- a/GlobalTestHelpers/MockStackNavigator.swift +++ b/GlobalTestHelpers/MockStackNavigator.swift @@ -71,6 +71,10 @@ final class MockStackNavigator: StackNavigator { alerts.append(alert) } + func present(_ alert: BitwardenShared.Alert, onDismissed: (() -> Void)?) { + alerts.append(alert) + } + func present( _ view: Content, animated: Bool, diff --git a/Mintfile b/Mintfile index dbd12f9cc..ecf3f1cfc 100644 --- a/Mintfile +++ b/Mintfile @@ -1,5 +1,5 @@ mono0926/LicensePlist@3.25.1 -nicklockwood/SwiftFormat@0.53.7 +nicklockwood/SwiftFormat@0.53.8 SwiftGen/SwiftGen@6.6.3 realm/SwiftLint@0.54.0 yonaskolb/xcodegen@2.40.1 diff --git a/project.yml b/project.yml index 733bf5d98..38d9bc5b2 100644 --- a/project.yml +++ b/project.yml @@ -14,7 +14,7 @@ options: indentWidth: 4 tabWidth: 4 settings: - MARKETING_VERSION: 2024.04.0 # Bump this for a new version update. + MARKETING_VERSION: 2024.05.0 # Bump this for a new version update. CURRENT_PROJECT_VERSION: 1 packages: BitwardenSdk: